24#include "absl/log/check.h"
25#include "absl/strings/str_format.h"
37#include "ortools/sat/boolean_problem.pb.h"
46using ::operations_research::glop::ColIndex;
50using ::operations_research::glop::LinearProgram;
51using ::operations_research::glop::LPDecomposer;
52using ::operations_research::glop::RowIndex;
53using ::operations_research::glop::SparseColumn;
54using ::operations_research::glop::SparseMatrix;
55using ::operations_research::sat::LinearBooleanConstraint;
56using ::operations_research::sat::LinearBooleanProblem;
57using ::operations_research::sat::LinearObjective;
69bool ProblemIsBooleanAndHasOnlyIntegralConstraints(
73 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
77 if (lower_bound <= -1.0 || upper_bound >= 2.0) {
82 for (
const SparseColumn::Entry e : matrix.column(col)) {
95void BuildBooleanProblemWithIntegralConstraints(
97 LinearBooleanProblem* boolean_problem,
98 std::vector<bool>* boolean_initial_solution) {
99 CHECK(boolean_problem !=
nullptr);
100 boolean_problem->Clear();
104 for (ColIndex col(0); col < matrix.num_cols(); ++col) {
107 boolean_problem->set_num_variables(matrix.num_cols().value());
108 boolean_problem->set_name(linear_problem.
name());
111 for (RowIndex row(0); row < matrix.num_rows(); ++row) {
112 LinearBooleanConstraint*
const constraint =
113 boolean_problem->add_constraints();
116 constraint->set_lower_bound(
120 constraint->set_upper_bound(
126 for (ColIndex col(0); col < matrix.num_cols(); ++col) {
127 for (
const SparseColumn::Entry e : matrix.column(col)) {
128 LinearBooleanConstraint*
const constraint =
129 boolean_problem->mutable_constraints(e.row().value());
130 constraint->add_literals(col.value() + 1);
131 constraint->add_coefficients(e.coefficient());
137 for (ColIndex col(0); col < matrix.num_cols(); ++col) {
142 LinearBooleanConstraint* ct = boolean_problem->add_constraints();
143 ct->set_lower_bound(ub);
144 ct->set_upper_bound(ub);
145 ct->add_literals(col.value() + 1);
146 ct->add_coefficients(1.0);
151 std::vector<double> coefficients;
152 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
154 if (coeff != 0.0) coefficients.push_back(coeff);
156 double scaling_factor = 0.0;
157 double relative_error = 0.0;
159 std::numeric_limits<int64_t>::max(),
160 &scaling_factor, &relative_error);
162 LinearObjective*
const objective = boolean_problem->mutable_objective();
168 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
169 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
171 const int64_t value =
172 static_cast<int64_t
>(round(coeff * scaling_factor)) / gcd;
174 objective->add_literals(col.value() + 1);
175 objective->add_coefficients(value);
185 if (!initial_solution.empty()) {
186 CHECK(boolean_initial_solution !=
nullptr);
187 CHECK_EQ(boolean_problem->num_variables(), initial_solution.size());
188 boolean_initial_solution->assign(boolean_problem->num_variables(),
false);
189 for (
int i = 0;
i < initial_solution.size(); ++
i) {
190 (*boolean_initial_solution)[
i] = (initial_solution[ColIndex(i)] != 0);
203class IntegralVariable {
212 void BuildFromRange(
int start_var_index,
Fractional lower_bound,
216 void set_offset(int64_t offset) { offset_ = offset; }
217 void set_weight(VariableIndex var, int64_t weight);
219 int GetNumberOfBooleanVariables()
const {
return bits_.size(); }
221 const std::vector<VariableIndex>& bits()
const {
return bits_; }
222 const std::vector<int64_t>& weights()
const {
return weights_; }
223 int64_t offset()
const {
return offset_; }
227 int64_t GetSolutionValue(
const BopSolution&
solution)
const;
233 std::vector<bool> GetBooleanSolutionValues(int64_t integral_value)
const;
235 std::string DebugString()
const;
241 std::vector<VariableIndex> bits_;
242 std::vector<int64_t> weights_;
248 bool can_be_reversed_;
251IntegralVariable::IntegralVariable()
252 : bits_(), weights_(), offset_(0), can_be_reversed_(
true) {}
254void IntegralVariable::BuildFromRange(
int start_var_index,
255 Fractional lower_bound,
256 Fractional upper_bound) {
261 CHECK_NE(-kInfinity, lower_bound);
262 CHECK_NE(kInfinity, upper_bound);
264 const int64_t integral_lower_bound =
static_cast<int64_t
>(ceil(lower_bound));
265 const int64_t integral_upper_bound =
static_cast<int64_t
>(
floor(upper_bound));
266 offset_ = integral_lower_bound;
267 const int64_t delta = integral_upper_bound - integral_lower_bound;
269 for (
int i = 0;
i < num_used_bits; ++
i) {
270 bits_.push_back(VariableIndex(start_var_index + i));
271 weights_.push_back(1ULL << i);
275void IntegralVariable::Clear() {
279 can_be_reversed_ =
true;
282void IntegralVariable::set_weight(VariableIndex var, int64_t weight) {
283 bits_.push_back(var);
284 weights_.push_back(weight);
285 can_be_reversed_ =
false;
288int64_t IntegralVariable::GetSolutionValue(
const BopSolution& solution)
const {
289 int64_t value = offset_;
290 for (
int i = 0;
i < bits_.size(); ++
i) {
291 value += weights_[
i] *
solution.Value(bits_[i]);
296std::vector<bool> IntegralVariable::GetBooleanSolutionValues(
297 int64_t integral_value)
const {
298 if (can_be_reversed_) {
299 DCHECK(std::is_sorted(weights_.begin(), weights_.end()));
300 std::vector<bool> boolean_values(weights_.size(),
false);
301 int64_t remaining_value = integral_value - offset_;
302 for (
int i = weights_.size() - 1; i >= 0; --i) {
303 if (remaining_value >= weights_[i]) {
304 boolean_values[
i] =
true;
305 remaining_value -= weights_[
i];
308 CHECK_EQ(0, remaining_value)
309 <<
"Couldn't map integral value to boolean variables.";
310 return boolean_values;
312 return std::vector<bool>();
315std::string IntegralVariable::DebugString()
const {
317 CHECK_EQ(bits_.size(), weights_.size());
318 for (
int i = 0;
i < bits_.size(); ++
i) {
319 str += absl::StrFormat(
"%d [%d] ", weights_[i], bits_[i].value());
321 str += absl::StrFormat(
" Offset: %d", offset_);
342class IntegralProblemConverter {
344 IntegralProblemConverter();
350 bool ConvertToBooleanProblem(
const LinearProgram& linear_problem,
351 const DenseRow& initial_solution,
352 LinearBooleanProblem* boolean_problem,
353 std::vector<bool>* boolean_initial_solution);
357 int64_t GetSolutionValue(ColIndex global_col,
358 const BopSolution& solution)
const;
364 bool CheckProblem(
const LinearProgram& linear_problem)
const;
367 void InitVariableTypes(
const LinearProgram& linear_problem,
368 LinearBooleanProblem* boolean_problem);
371 void ConvertAllVariables(
const LinearProgram& linear_problem,
372 LinearBooleanProblem* boolean_problem);
375 void AddVariableConstraints(
const LinearProgram& linear_problem,
376 LinearBooleanProblem* boolean_problem);
379 void ConvertAllConstraints(
const LinearProgram& linear_problem,
380 LinearBooleanProblem* boolean_problem);
383 void ConvertObjective(
const LinearProgram& linear_problem,
384 LinearBooleanProblem* boolean_problem);
390 bool ConvertUsingExistingBooleans(
const LinearProgram& linear_problem,
392 IntegralVariable* integral_var);
403 bool CreateVariableUsingConstraint(
const LinearProgram& linear_problem,
405 IntegralVariable* integral_var);
410 ColIndex col, Fractional weight,
411 util_intops::StrongVector<VariableIndex, Fractional>* dense_weights);
419 double ScaleAndSparsifyWeights(
420 double scaling_factor, int64_t gcd,
421 const util_intops::StrongVector<VariableIndex, Fractional>& dense_weights,
425 bool HasNonZeroWeights(
426 const util_intops::StrongVector<VariableIndex, Fractional>& dense_weights)
429 bool problem_is_boolean_and_has_only_integral_constraints_;
434 util_intops::StrongVector< glop::ColIndex,
int>
436 std::vector<IntegralVariable> integral_variables_;
437 std::vector<ColIndex> integral_indices_;
438 int num_boolean_variables_;
440 enum VariableType { BOOLEAN, INTEGRAL, INTEGRAL_EXPRESSED_AS_BOOLEAN };
441 util_intops::StrongVector<glop::ColIndex, VariableType> variable_types_;
444IntegralProblemConverter::IntegralProblemConverter()
445 : global_to_boolean_(),
446 integral_variables_(),
448 num_boolean_variables_(0),
451bool IntegralProblemConverter::ConvertToBooleanProblem(
452 const LinearProgram& linear_problem,
const DenseRow& initial_solution,
453 LinearBooleanProblem* boolean_problem,
454 std::vector<bool>* boolean_initial_solution) {
455 bool use_initial_solution = (initial_solution.size() > 0);
456 if (use_initial_solution) {
457 CHECK_EQ(initial_solution.size(), linear_problem.
num_variables())
458 <<
"The initial solution should have the same number of variables as "
459 "the LinearProgram.";
460 CHECK(boolean_initial_solution !=
nullptr);
462 if (!CheckProblem(linear_problem)) {
466 problem_is_boolean_and_has_only_integral_constraints_ =
467 ProblemIsBooleanAndHasOnlyIntegralConstraints(linear_problem);
468 if (problem_is_boolean_and_has_only_integral_constraints_) {
469 BuildBooleanProblemWithIntegralConstraints(linear_problem, initial_solution,
471 boolean_initial_solution);
475 InitVariableTypes(linear_problem, boolean_problem);
476 ConvertAllVariables(linear_problem, boolean_problem);
477 boolean_problem->set_num_variables(num_boolean_variables_);
478 boolean_problem->set_name(linear_problem.
name());
480 AddVariableConstraints(linear_problem, boolean_problem);
481 ConvertAllConstraints(linear_problem, boolean_problem);
482 ConvertObjective(linear_problem, boolean_problem);
486 sat::ChangeOptimizationDirection(boolean_problem);
489 if (use_initial_solution) {
490 boolean_initial_solution->assign(boolean_problem->num_variables(),
false);
491 for (ColIndex global_col(0); global_col < global_to_boolean_.size();
493 const int col = global_to_boolean_[global_col];
495 (*boolean_initial_solution)[col] = (initial_solution[global_col] != 0);
497 const IntegralVariable& integral_variable =
498 integral_variables_[-col - 1];
499 const std::vector<VariableIndex>& boolean_cols =
500 integral_variable.bits();
501 const std::vector<bool>& boolean_values =
502 integral_variable.GetBooleanSolutionValues(
503 round(initial_solution[global_col]));
504 if (!boolean_values.empty()) {
505 CHECK_EQ(boolean_cols.size(), boolean_values.size());
506 for (
int i = 0;
i < boolean_values.size(); ++
i) {
507 const int boolean_col = boolean_cols[
i].value();
508 (*boolean_initial_solution)[boolean_col] = boolean_values[
i];
518int64_t IntegralProblemConverter::GetSolutionValue(
519 ColIndex global_col,
const BopSolution& solution)
const {
520 if (problem_is_boolean_and_has_only_integral_constraints_) {
521 return solution.Value(VariableIndex(global_col.value()));
524 const int pos = global_to_boolean_[global_col];
525 return pos >= 0 ?
solution.Value(VariableIndex(pos))
526 : integral_variables_[-pos - 1].GetSolutionValue(solution);
529bool IntegralProblemConverter::CheckProblem(
531 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
534 <<
" is continuous. This is not supported by BOP.";
539 <<
" has no lower bound. This is not supported by BOP.";
544 <<
" has no upper bound. This is not supported by BOP.";
551void IntegralProblemConverter::InitVariableTypes(
553 LinearBooleanProblem* boolean_problem) {
554 global_to_boolean_.assign(linear_problem.
num_variables().value(), 0);
555 variable_types_.assign(linear_problem.
num_variables().value(), INTEGRAL);
556 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
560 if (lower_bound > -1.0 && upper_bound < 2.0) {
562 variable_types_[col] = BOOLEAN;
563 global_to_boolean_[col] = num_boolean_variables_;
564 ++num_boolean_variables_;
568 variable_types_[col] = INTEGRAL;
569 integral_indices_.push_back(col);
574void IntegralProblemConverter::ConvertAllVariables(
576 LinearBooleanProblem* boolean_problem) {
577 for (
const ColIndex col : integral_indices_) {
578 CHECK_EQ(INTEGRAL, variable_types_[col]);
579 IntegralVariable integral_var;
580 if (!ConvertUsingExistingBooleans(linear_problem, col, &integral_var)) {
585 integral_var.BuildFromRange(num_boolean_variables_, lower_bound,
587 num_boolean_variables_ += integral_var.GetNumberOfBooleanVariables();
589 for (
int i = 0;
i < integral_var.bits().size(); ++
i) {
590 boolean_problem->add_var_names(var_name + absl::StrFormat(
"_%d", i));
593 integral_variables_.push_back(integral_var);
594 global_to_boolean_[col] = -integral_variables_.size();
595 variable_types_[col] = INTEGRAL_EXPRESSED_AS_BOOLEAN;
599void IntegralProblemConverter::ConvertAllConstraints(
601 LinearBooleanProblem* boolean_problem) {
604 glop::SparseMatrix transpose;
607 double max_relative_error = 0.0;
608 double max_bound_error = 0.0;
609 double max_scaling_factor = 0.0;
610 double relative_error = 0.0;
611 double scaling_factor = 0.0;
612 std::vector<double> coefficients;
616 num_boolean_variables_, 0.0);
619 offset += AddWeightedIntegralVariable(
RowToColIndex(e.row()),
620 e.coefficient(), &dense_weights);
622 if (!HasNonZeroWeights(dense_weights)) {
627 coefficients.clear();
628 for (VariableIndex var(0); var < num_boolean_variables_; ++var) {
629 if (dense_weights[var] != 0.0) {
630 coefficients.push_back(dense_weights[var]);
634 std::numeric_limits<int64_t>::max(),
635 &scaling_factor, &relative_error);
638 max_relative_error = std::max(relative_error, max_relative_error);
639 max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
641 LinearBooleanConstraint* constraint = boolean_problem->add_constraints();
643 const double bound_error =
644 ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, constraint);
645 max_bound_error = std::max(max_bound_error, bound_error);
649 if (lower_bound != -kInfinity) {
650 const Fractional offset_lower_bound = lower_bound - offset;
651 const double offset_scaled_lower_bound =
652 round(offset_lower_bound * scaling_factor - bound_error);
653 if (offset_scaled_lower_bound >=
654 static_cast<double>(std::numeric_limits<int64_t>::max())) {
655 LOG(WARNING) <<
"A constraint is trivially unsatisfiable.";
658 if (offset_scaled_lower_bound >
659 -
static_cast<double>(std::numeric_limits<int64_t>::max())) {
661 constraint->set_lower_bound(
662 static_cast<int64_t
>(offset_scaled_lower_bound) / gcd);
667 if (upper_bound != kInfinity) {
668 const Fractional offset_upper_bound = upper_bound - offset;
669 const double offset_scaled_upper_bound =
670 round(offset_upper_bound * scaling_factor + bound_error);
671 if (offset_scaled_upper_bound <=
672 -
static_cast<double>(std::numeric_limits<int64_t>::max())) {
673 LOG(WARNING) <<
"A constraint is trivially unsatisfiable.";
676 if (offset_scaled_upper_bound <
677 static_cast<double>(std::numeric_limits<int64_t>::max())) {
679 constraint->set_upper_bound(
680 static_cast<int64_t
>(offset_scaled_upper_bound) / gcd);
686void IntegralProblemConverter::ConvertObjective(
688 LinearBooleanProblem* boolean_problem) {
689 LinearObjective* objective = boolean_problem->mutable_objective();
692 num_boolean_variables_, 0.0);
694 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
695 offset += AddWeightedIntegralVariable(
700 std::vector<double> coefficients;
701 for (VariableIndex var(0); var < num_boolean_variables_; ++var) {
702 if (dense_weights[var] != 0.0) {
703 coefficients.push_back(dense_weights[var]);
706 double scaling_factor = 0.0;
707 double max_relative_error = 0.0;
708 double relative_error = 0.0;
710 std::numeric_limits<int64_t>::max(),
711 &scaling_factor, &relative_error);
713 max_relative_error = std::max(relative_error, max_relative_error);
714 VLOG(1) <<
"objective relative error: " << relative_error;
715 VLOG(1) <<
"objective scaling factor: " << scaling_factor / gcd;
717 ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, objective);
721 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
723 scaling_factor / gcd);
726void IntegralProblemConverter::AddVariableConstraints(
728 LinearBooleanProblem* boolean_problem) {
729 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
732 const int pos = global_to_boolean_[col];
735 CHECK_EQ(BOOLEAN, variable_types_[col]);
736 const bool is_fixed = (lower_bound > -1.0 && upper_bound < 1.0) ||
737 (lower_bound > 0.0 && upper_bound < 2.0);
740 const int fixed_value = lower_bound > -1.0 && upper_bound < 1.0 ? 0 : 1;
741 LinearBooleanConstraint* constraint =
742 boolean_problem->add_constraints();
743 constraint->set_lower_bound(fixed_value);
744 constraint->set_upper_bound(fixed_value);
745 constraint->add_literals(pos + 1);
746 constraint->add_coefficients(1);
749 CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[col]);
751 if (lower_bound != -kInfinity || upper_bound != kInfinity) {
752 const IntegralVariable& integral_var = integral_variables_[-pos - 1];
753 LinearBooleanConstraint* constraint =
754 boolean_problem->add_constraints();
755 for (
int i = 0;
i < integral_var.bits().size(); ++
i) {
756 constraint->add_literals(integral_var.bits()[i].value() + 1);
757 constraint->add_coefficients(integral_var.weights()[i]);
759 if (lower_bound != -kInfinity) {
760 constraint->set_lower_bound(
static_cast<int64_t
>(ceil(lower_bound)) -
761 integral_var.offset());
763 if (upper_bound != kInfinity) {
764 constraint->set_upper_bound(
static_cast<int64_t
>(
floor(upper_bound)) -
765 integral_var.offset());
772bool IntegralProblemConverter::ConvertUsingExistingBooleans(
774 IntegralVariable* integral_var) {
775 CHECK(
nullptr != integral_var);
776 CHECK_EQ(INTEGRAL, variable_types_[col]);
781 const RowIndex constraint = var_entry.row();
799 bool only_one_integral_variable =
true;
802 const ColIndex var_index =
RowToColIndex(constraint_entry.row());
803 if (var_index != col && variable_types_[var_index] == INTEGRAL) {
804 only_one_integral_variable =
false;
808 if (only_one_integral_variable &&
809 CreateVariableUsingConstraint(linear_problem, constraint,
815 integral_var->Clear();
819bool IntegralProblemConverter::CreateVariableUsingConstraint(
821 IntegralVariable* integral_var) {
822 CHECK(
nullptr != integral_var);
823 integral_var->Clear();
827 num_boolean_variables_, 0.0);
829 int64_t variable_offset = 0;
833 if (variable_types_[col] == INTEGRAL) {
834 scale = constraint_entry.coefficient();
835 }
else if (variable_types_[col] == BOOLEAN) {
836 const int pos = global_to_boolean_[col];
838 dense_weights[VariableIndex(pos)] -= constraint_entry.coefficient();
840 CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[col]);
841 const int pos = global_to_boolean_[col];
843 const IntegralVariable& local_integral_var =
844 integral_variables_[-pos - 1];
846 constraint_entry.coefficient() * local_integral_var.offset();
847 for (
int i = 0;
i < local_integral_var.bits().size(); ++
i) {
848 dense_weights[local_integral_var.bits()[
i]] -=
849 constraint_entry.coefficient() * local_integral_var.weights()[
i];
856 const Fractional offset = (lb + variable_offset) / scale;
860 integral_var->set_offset(
static_cast<int64_t
>(offset));
862 for (VariableIndex var(0); var < dense_weights.size(); ++var) {
863 if (dense_weights[var] != 0.0) {
864 const Fractional weight = dense_weights[var] / scale;
868 integral_var->set_weight(var,
static_cast<int64_t
>(weight));
875Fractional IntegralProblemConverter::AddWeightedIntegralVariable(
876 ColIndex col, Fractional weight,
878 CHECK(
nullptr != dense_weights);
885 const int pos = global_to_boolean_[col];
888 (*dense_weights)[VariableIndex(pos)] += weight;
891 const IntegralVariable& integral_var = integral_variables_[-pos - 1];
892 for (
int i = 0;
i < integral_var.bits().size(); ++
i) {
893 (*dense_weights)[integral_var.bits()[
i]] +=
894 integral_var.weights()[
i] * weight;
896 offset += weight * integral_var.offset();
902double IntegralProblemConverter::ScaleAndSparsifyWeights(
903 double scaling_factor, int64_t gcd,
908 double bound_error = 0.0;
909 for (VariableIndex var(0); var < dense_weights.size(); ++var) {
910 if (dense_weights[var] != 0.0) {
911 const double scaled_weight = dense_weights[var] * scaling_factor;
912 bound_error += fabs(round(scaled_weight) - scaled_weight);
913 t->add_literals(var.value() + 1);
914 t->add_coefficients(
static_cast<int64_t
>(round(scaled_weight)) / gcd);
920bool IntegralProblemConverter::HasNonZeroWeights(
923 for (
const Fractional weight : dense_weights) {
932 const glop::DenseRow& variable_values) {
933 glop::DenseColumn constraint_values(linear_problem.
num_constraints(), 0);
936 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
939 const Fractional value = variable_values[col];
940 if (lower_bound > value || upper_bound < value) {
941 LOG(ERROR) <<
"Variable " << col <<
" out of bound: " << value
942 <<
" should be in " << lower_bound <<
" .. " << upper_bound;
947 constraint_values[entry.row()] += entry.coefficient() * value;
954 const Fractional value = constraint_values[row];
955 if (lb > value || ub < value) {
956 LOG(ERROR) <<
"Constraint " << row <<
" out of bound: " << value
957 <<
" should be in " << lb <<
" .. " << ub;
967 const BopParameters& parameters,
968 const DenseRow& initial_solution,
969 TimeLimit*
time_limit, DenseRow* variable_values,
970 Fractional* objective_value,
971 Fractional* best_bound) {
972 CHECK(variable_values !=
nullptr);
973 CHECK(objective_value !=
nullptr);
974 CHECK(best_bound !=
nullptr);
975 const bool use_initial_solution = (initial_solution.size() > 0);
976 if (use_initial_solution) {
977 CHECK_EQ(initial_solution.size(), linear_problem.
num_variables());
985 LinearBooleanProblem boolean_problem;
986 std::vector<bool> boolean_initial_solution;
987 IntegralProblemConverter converter;
988 if (!converter.ConvertToBooleanProblem(linear_problem, initial_solution,
990 &boolean_initial_solution)) {
991 return BopSolveStatus::INVALID_PROBLEM;
994 BopSolver bop_solver(boolean_problem);
995 bop_solver.SetParameters(parameters);
997 if (use_initial_solution) {
998 BopSolution bop_solution(boolean_problem,
"InitialSolution");
999 CHECK_EQ(boolean_initial_solution.size(), boolean_problem.num_variables());
1000 for (
int i = 0;
i < boolean_initial_solution.size(); ++
i) {
1001 bop_solution.SetValue(VariableIndex(i), boolean_initial_solution[i]);
1003 status = bop_solver.SolveWithTimeLimit(bop_solution,
time_limit);
1005 status = bop_solver.SolveWithTimeLimit(
time_limit);
1007 if (status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND ||
1008 status == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
1010 const BopSolution&
solution = bop_solver.best_solution();
1014 for (ColIndex col(0); col < linear_problem.
num_variables(); ++col) {
1015 const int64_t value = converter.GetSolutionValue(col, solution);
1016 (*variable_values)[col] = value;
1024 *best_bound = status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND
1026 : bop_solver.GetScaledBestBound();
1031void RunOneBop(
const BopParameters& parameters,
int problem_index,
1032 const DenseRow& initial_solution, TimeLimit*
time_limit,
1034 Fractional* objective_value, Fractional* best_bound,
1035 BopSolveStatus* status) {
1036 CHECK(decomposer !=
nullptr);
1037 CHECK(variable_values !=
nullptr);
1038 CHECK(objective_value !=
nullptr);
1039 CHECK(best_bound !=
nullptr);
1040 CHECK(status !=
nullptr);
1045 if (initial_solution.size() > 0) {
1046 local_initial_solution =
1051 const double total_num_variables = std::max(
1052 1.0,
static_cast<double>(
1054 const double time_per_variable =
1055 parameters.max_time_in_seconds() / total_num_variables;
1056 const double deterministic_time_per_variable =
1057 parameters.max_deterministic_time() / total_num_variables;
1058 const int local_num_variables = std::max(1, problem.
num_variables().value());
1060 NestedTimeLimit subproblem_time_limit(
1062 std::max(time_per_variable * local_num_variables,
1063 parameters.decomposed_problem_min_time_in_seconds()),
1064 deterministic_time_per_variable * local_num_variables);
1066 *status = InternalSolve(problem, parameters, local_initial_solution,
1067 subproblem_time_limit.GetTimeLimit(), variable_values,
1068 objective_value, best_bound);
1072IntegralSolver::IntegralSolver()
1073 : parameters_(), variable_values_(), objective_value_(0.0) {}
1086 const DenseRow& user_provided_initial_solution) {
1097 DenseRow initial_solution = user_provided_initial_solution;
1098 if (initial_solution.
size() > 0) {
1100 <<
"The initial solution should have the same number of variables as "
1101 "the LinearProgram.";
1109 if (lp->
num_variables() >= parameters_.decomposer_num_variables_threshold()) {
1113 VLOG(1) <<
"Problem is decomposable into " << num_sub_problems
1115 if (num_sub_problems > 1) {
1118 std::vector<DenseRow> variable_values(num_sub_problems);
1119 std::vector<Fractional> objective_values(num_sub_problems,
1121 std::vector<Fractional> best_bounds(num_sub_problems,
Fractional(0.0));
1122 std::vector<BopSolveStatus> statuses(num_sub_problems,
1125 for (
int i = 0; i < num_sub_problems; ++i) {
1126 RunOneBop(parameters_, i, initial_solution,
time_limit, &decomposer,
1127 &(variable_values[i]), &(objective_values[i]),
1128 &(best_bounds[i]), &(statuses[i]));
1132 status = BopSolveStatus::OPTIMAL_SOLUTION_FOUND;
1135 for (
int i = 0; i < num_sub_problems; ++i) {
1136 objective_value_ += objective_values[i];
1137 best_bound_ += best_bounds[i];
1138 if (statuses[i] == BopSolveStatus::NO_SOLUTION_FOUND ||
1139 statuses[i] == BopSolveStatus::INFEASIBLE_PROBLEM ||
1140 statuses[i] == BopSolveStatus::INVALID_PROBLEM) {
1144 if (statuses[i] == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
1145 status = BopSolveStatus::FEASIBLE_SOLUTION_FOUND;
1152 InternalSolve(*lp, parameters_, initial_solution,
time_limit,
1153 &variable_values_, &objective_value_, &best_bound_);
1156 status = InternalSolve(*lp, parameters_, initial_solution,
time_limit,
1157 &variable_values_, &objective_value_, &best_bound_);
static std::unique_ptr< TimeLimit > FromParameters(const Parameters ¶meters)
ABSL_MUST_USE_RESULT BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram &linear_problem, TimeLimit *time_limit)
ABSL_MUST_USE_RESULT BopSolveStatus Solve(const glop::LinearProgram &linear_problem)
Solves the given linear program and returns the solve status.
const LinearProgram & original_problem() const ABSL_LOCKS_EXCLUDED(mutex_)
Returns the original problem, i.e. as it was before any decomposition.
DenseRow ExtractLocalAssignment(int problem_index, const DenseRow &assignment) ABSL_LOCKS_EXCLUDED(mutex_)
void ExtractLocalProblem(int problem_index, LinearProgram *lp) ABSL_LOCKS_EXCLUDED(mutex_)
const DenseRow & objective_coefficients() const
Returns the objective coefficients (or cost) of variables as a row vector.
std::string GetConstraintName(RowIndex row) const
const std::string & name() const
const DenseColumn & constraint_lower_bounds() const
const SparseMatrix & GetTransposeSparseMatrix() const
const DenseRow & variable_lower_bounds() const
bool IsMaximizationProblem() const
const SparseMatrix & GetSparseMatrix() const
std::string GetVariableName(ColIndex col) const
const DenseRow & variable_upper_bounds() const
const DenseColumn & constraint_upper_bounds() const
bool IsVariableInteger(ColIndex col) const
Returns whether the variable at column col is constrained to be integer.
ColIndex num_variables() const
Returns the number of variables.
Fractional objective_offset() const
Returns the objective offset and scaling factor.
RowIndex num_constraints() const
Returns the number of constraints.
const SparseColumn & column(ColIndex col) const
Access the underlying sparse columns.
DenseRow AggregateAssignments(absl::Span< const DenseRow > assignments) const ABSL_LOCKS_EXCLUDED(mutex_)
int GetNumberOfProblems() const ABSL_LOCKS_EXCLUDED(mutex_)
Returns the number of independent problems generated by Decompose().
void Decompose(const LinearProgram *linear_problem) ABSL_LOCKS_EXCLUDED(mutex_)
ColIndex num_variables() const
Returns the number of variables.
Fractional objective_offset() const
Returns the objective offset and scaling factor.
typename Iterator::Entry Entry
constexpr double kInfinity
Infinity for type Fractional.
BopSolveStatus
Status of the solve of Bop.
@ INVALID_PROBLEM
The problem is invalid.
bool CheckSolution(const Model &model, const std::function< int64_t(Variable *)> &evaluator, SolverLogger *logger)
constexpr double kInfinity
Infinity for type Fractional.
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
VariableType
Different types of variables.
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
constexpr double kTolerance
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
In SWIG mode, we don't want anything besides these top-level includes.
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
double GetBestScalingOfDoublesToInt64(absl::Span< const double > input, absl::Span< const double > lb, absl::Span< const double > ub, int64_t max_absolute_sum)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
int64_t ComputeGcdOfRoundedDoubles(absl::Span< const double > x, double scaling_factor)
int MostSignificantBitPosition64(uint64_t n)