32 using ::operations_research::glop::ColIndex;
36 using ::operations_research::glop::LinearProgram;
37 using ::operations_research::glop::LPDecomposer;
38 using ::operations_research::glop::RowIndex;
39 using ::operations_research::glop::SparseColumn;
40 using ::operations_research::glop::SparseMatrix;
41 using ::operations_research::sat::LinearBooleanConstraint;
42 using ::operations_research::sat::LinearBooleanProblem;
43 using ::operations_research::sat::LinearObjective;
48 const double kTolerance = 1e-10;
49 return std::abs(x - round(x)) <= kTolerance;
55 bool ProblemIsBooleanAndHasOnlyIntegralConstraints(
56 const LinearProgram& linear_problem) {
57 const glop::SparseMatrix& matrix = linear_problem.GetSparseMatrix();
59 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
63 if (lower_bound <= -1.0 || upper_bound >= 2.0) {
68 for (
const SparseColumn::Entry e : matrix.column(
col)) {
81 void BuildBooleanProblemWithIntegralConstraints(
82 const LinearProgram& linear_problem,
const DenseRow& initial_solution,
83 LinearBooleanProblem* boolean_problem,
84 std::vector<bool>* boolean_initial_solution) {
85 CHECK(boolean_problem !=
nullptr);
86 boolean_problem->Clear();
88 const glop::SparseMatrix& matrix = linear_problem.GetSparseMatrix();
90 for (ColIndex
col(0);
col < matrix.num_cols(); ++
col) {
91 boolean_problem->add_var_names(linear_problem.GetVariableName(
col));
93 boolean_problem->set_num_variables(matrix.num_cols().value());
94 boolean_problem->set_name(linear_problem.name());
97 for (RowIndex
row(0);
row < matrix.num_rows(); ++
row) {
98 LinearBooleanConstraint*
const constraint =
99 boolean_problem->add_constraints();
100 constraint->set_name(linear_problem.GetConstraintName(
row));
101 if (linear_problem.constraint_lower_bounds()[
row] != -
kInfinity) {
102 constraint->set_lower_bound(
103 linear_problem.constraint_lower_bounds()[
row]);
105 if (linear_problem.constraint_upper_bounds()[
row] !=
kInfinity) {
106 constraint->set_upper_bound(
107 linear_problem.constraint_upper_bounds()[
row]);
112 for (ColIndex
col(0);
col < matrix.num_cols(); ++
col) {
113 for (
const SparseColumn::Entry e : matrix.column(
col)) {
114 LinearBooleanConstraint*
const constraint =
115 boolean_problem->mutable_constraints(e.row().value());
116 constraint->add_literals(
col.value() + 1);
117 constraint->add_coefficients(e.coefficient());
123 for (ColIndex
col(0);
col < matrix.num_cols(); ++
col) {
125 const int lb = std::round(linear_problem.variable_lower_bounds()[
col]);
126 const int ub = std::round(linear_problem.variable_upper_bounds()[
col]);
128 LinearBooleanConstraint*
ct = boolean_problem->add_constraints();
129 ct->set_lower_bound(ub);
130 ct->set_upper_bound(ub);
131 ct->add_literals(
col.value() + 1);
132 ct->add_coefficients(1.0);
138 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
139 const Fractional coeff = linear_problem.objective_coefficients()[
col];
142 double scaling_factor = 0.0;
143 double relative_error = 0.0;
146 &scaling_factor, &relative_error);
148 LinearObjective*
const objective = boolean_problem->mutable_objective();
149 objective->set_offset(linear_problem.objective_offset() * scaling_factor /
154 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
155 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
156 const Fractional coeff = linear_problem.objective_coefficients()[
col];
157 const int64_t
value =
158 static_cast<int64_t
>(round(coeff * scaling_factor)) / gcd;
160 objective->add_literals(
col.value() + 1);
161 objective->add_coefficients(
value);
166 if (linear_problem.IsMaximizationProblem()) {
171 if (!initial_solution.empty()) {
172 CHECK(boolean_initial_solution !=
nullptr);
173 CHECK_EQ(boolean_problem->num_variables(), initial_solution.size());
174 boolean_initial_solution->assign(boolean_problem->num_variables(),
false);
175 for (
int i = 0; i < initial_solution.size(); ++i) {
176 (*boolean_initial_solution)[i] = (initial_solution[ColIndex(i)] != 0);
189 class IntegralVariable {
202 void set_offset(int64_t offset) {
offset_ = offset; }
203 void set_weight(VariableIndex
var, int64_t
weight);
205 int GetNumberOfBooleanVariables()
const {
return bits_.size(); }
207 const std::vector<VariableIndex>& bits()
const {
return bits_; }
208 const std::vector<int64_t>& weights()
const {
return weights_; }
209 int64_t offset()
const {
return offset_; }
213 int64_t GetSolutionValue(
const BopSolution& solution)
const;
219 std::vector<bool> GetBooleanSolutionValues(int64_t integral_value)
const;
221 std::string DebugString()
const;
227 std::vector<VariableIndex> bits_;
228 std::vector<int64_t> weights_;
234 bool can_be_reversed_;
237 IntegralVariable::IntegralVariable()
238 : bits_(), weights_(),
offset_(0), can_be_reversed_(true) {}
240 void IntegralVariable::BuildFromRange(
int start_var_index,
250 const int64_t integral_lower_bound =
static_cast<int64_t
>(ceil(
lower_bound));
251 const int64_t integral_upper_bound =
static_cast<int64_t
>(floor(
upper_bound));
252 offset_ = integral_lower_bound;
253 const int64_t
delta = integral_upper_bound - integral_lower_bound;
255 for (
int i = 0; i < num_used_bits; ++i) {
256 bits_.push_back(VariableIndex(start_var_index + i));
257 weights_.push_back(1ULL << i);
261 void IntegralVariable::Clear() {
265 can_be_reversed_ =
true;
268 void IntegralVariable::set_weight(VariableIndex
var, int64_t
weight) {
269 bits_.push_back(
var);
270 weights_.push_back(
weight);
271 can_be_reversed_ =
false;
274 int64_t IntegralVariable::GetSolutionValue(
const BopSolution& solution)
const {
276 for (
int i = 0; i < bits_.size(); ++i) {
277 value += weights_[i] * solution.Value(bits_[i]);
282 std::vector<bool> IntegralVariable::GetBooleanSolutionValues(
283 int64_t integral_value)
const {
284 if (can_be_reversed_) {
285 DCHECK(std::is_sorted(weights_.begin(), weights_.end()));
286 std::vector<bool> boolean_values(weights_.size(),
false);
287 int64_t remaining_value = integral_value -
offset_;
288 for (
int i = weights_.size() - 1; i >= 0; --i) {
289 if (remaining_value >= weights_[i]) {
290 boolean_values[i] =
true;
291 remaining_value -= weights_[i];
294 CHECK_EQ(0, remaining_value)
295 <<
"Couldn't map integral value to boolean variables.";
296 return boolean_values;
298 return std::vector<bool>();
301 std::string IntegralVariable::DebugString()
const {
303 CHECK_EQ(bits_.size(), weights_.size());
304 for (
int i = 0; i < bits_.size(); ++i) {
305 str += absl::StrFormat(
"%d [%d] ", weights_[i], bits_[i].
value());
307 str += absl::StrFormat(
" Offset: %d",
offset_);
328 class IntegralProblemConverter {
330 IntegralProblemConverter();
336 bool ConvertToBooleanProblem(
const LinearProgram& linear_problem,
338 LinearBooleanProblem* boolean_problem,
339 std::vector<bool>* boolean_initial_solution);
343 int64_t GetSolutionValue(ColIndex global_col,
344 const BopSolution& solution)
const;
350 bool CheckProblem(
const LinearProgram& linear_problem)
const;
353 void InitVariableTypes(
const LinearProgram& linear_problem,
354 LinearBooleanProblem* boolean_problem);
357 void ConvertAllVariables(
const LinearProgram& linear_problem,
358 LinearBooleanProblem* boolean_problem);
361 void AddVariableConstraints(
const LinearProgram& linear_problem,
362 LinearBooleanProblem* boolean_problem);
365 void ConvertAllConstraints(
const LinearProgram& linear_problem,
366 LinearBooleanProblem* boolean_problem);
369 void ConvertObjective(
const LinearProgram& linear_problem,
370 LinearBooleanProblem* boolean_problem);
376 bool ConvertUsingExistingBooleans(
const LinearProgram& linear_problem,
378 IntegralVariable* integral_var);
389 bool CreateVariableUsingConstraint(
const LinearProgram& linear_problem,
391 IntegralVariable* integral_var);
405 double ScaleAndSparsifyWeights(
406 double scaling_factor, int64_t gcd,
410 bool HasNonZeroWeights(
413 bool problem_is_boolean_and_has_only_integral_constraints_;
420 std::vector<IntegralVariable> integral_variables_;
421 std::vector<ColIndex> integral_indices_;
422 int num_boolean_variables_;
424 enum VariableType { BOOLEAN, INTEGRAL, INTEGRAL_EXPRESSED_AS_BOOLEAN };
428 IntegralProblemConverter::IntegralProblemConverter()
429 : global_to_boolean_(),
430 integral_variables_(),
432 num_boolean_variables_(0),
435 bool IntegralProblemConverter::ConvertToBooleanProblem(
436 const LinearProgram& linear_problem,
const DenseRow& initial_solution,
437 LinearBooleanProblem* boolean_problem,
438 std::vector<bool>* boolean_initial_solution) {
439 bool use_initial_solution = (initial_solution.size() > 0);
440 if (use_initial_solution) {
441 CHECK_EQ(initial_solution.size(), linear_problem.num_variables())
442 <<
"The initial solution should have the same number of variables as "
443 "the LinearProgram.";
444 CHECK(boolean_initial_solution !=
nullptr);
446 if (!CheckProblem(linear_problem)) {
450 problem_is_boolean_and_has_only_integral_constraints_ =
451 ProblemIsBooleanAndHasOnlyIntegralConstraints(linear_problem);
452 if (problem_is_boolean_and_has_only_integral_constraints_) {
453 BuildBooleanProblemWithIntegralConstraints(linear_problem, initial_solution,
455 boolean_initial_solution);
459 InitVariableTypes(linear_problem, boolean_problem);
460 ConvertAllVariables(linear_problem, boolean_problem);
461 boolean_problem->set_num_variables(num_boolean_variables_);
462 boolean_problem->set_name(linear_problem.name());
464 AddVariableConstraints(linear_problem, boolean_problem);
465 ConvertAllConstraints(linear_problem, boolean_problem);
466 ConvertObjective(linear_problem, boolean_problem);
469 if (linear_problem.IsMaximizationProblem()) {
473 if (use_initial_solution) {
474 boolean_initial_solution->assign(boolean_problem->num_variables(),
false);
475 for (ColIndex global_col(0); global_col < global_to_boolean_.size();
477 const int col = global_to_boolean_[global_col];
479 (*boolean_initial_solution)[
col] = (initial_solution[global_col] != 0);
481 const IntegralVariable& integral_variable =
482 integral_variables_[-
col - 1];
483 const std::vector<VariableIndex>& boolean_cols =
484 integral_variable.bits();
485 const std::vector<bool>& boolean_values =
486 integral_variable.GetBooleanSolutionValues(
487 round(initial_solution[global_col]));
488 if (!boolean_values.empty()) {
489 CHECK_EQ(boolean_cols.size(), boolean_values.size());
490 for (
int i = 0; i < boolean_values.size(); ++i) {
491 const int boolean_col = boolean_cols[i].value();
492 (*boolean_initial_solution)[boolean_col] = boolean_values[i];
502 int64_t IntegralProblemConverter::GetSolutionValue(
503 ColIndex global_col,
const BopSolution& solution)
const {
504 if (problem_is_boolean_and_has_only_integral_constraints_) {
505 return solution.Value(VariableIndex(global_col.value()));
508 const int pos = global_to_boolean_[global_col];
509 return pos >= 0 ? solution.Value(VariableIndex(pos))
510 : integral_variables_[-pos - 1].GetSolutionValue(solution);
513 bool IntegralProblemConverter::CheckProblem(
514 const LinearProgram& linear_problem)
const {
515 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
516 if (!linear_problem.IsVariableInteger(
col)) {
517 LOG(ERROR) <<
"Variable " << linear_problem.GetVariableName(
col)
518 <<
" is continuous. This is not supported by BOP.";
521 if (linear_problem.variable_lower_bounds()[
col] == -
kInfinity) {
522 LOG(ERROR) <<
"Variable " << linear_problem.GetVariableName(
col)
523 <<
" has no lower bound. This is not supported by BOP.";
526 if (linear_problem.variable_upper_bounds()[
col] ==
kInfinity) {
527 LOG(ERROR) <<
"Variable " << linear_problem.GetVariableName(
col)
528 <<
" has no upper bound. This is not supported by BOP.";
535 void IntegralProblemConverter::InitVariableTypes(
536 const LinearProgram& linear_problem,
537 LinearBooleanProblem* boolean_problem) {
538 global_to_boolean_.assign(linear_problem.num_variables().value(), 0);
539 variable_types_.assign(linear_problem.num_variables().value(), INTEGRAL);
540 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
546 variable_types_[
col] = BOOLEAN;
547 global_to_boolean_[
col] = num_boolean_variables_;
548 ++num_boolean_variables_;
549 boolean_problem->add_var_names(linear_problem.GetVariableName(
col));
552 variable_types_[
col] = INTEGRAL;
553 integral_indices_.push_back(
col);
558 void IntegralProblemConverter::ConvertAllVariables(
559 const LinearProgram& linear_problem,
560 LinearBooleanProblem* boolean_problem) {
561 for (
const ColIndex
col : integral_indices_) {
562 CHECK_EQ(INTEGRAL, variable_types_[
col]);
563 IntegralVariable integral_var;
564 if (!ConvertUsingExistingBooleans(linear_problem,
col, &integral_var)) {
566 linear_problem.variable_lower_bounds()[
col];
568 linear_problem.variable_upper_bounds()[
col];
569 integral_var.BuildFromRange(num_boolean_variables_,
lower_bound,
571 num_boolean_variables_ += integral_var.GetNumberOfBooleanVariables();
572 const std::string var_name = linear_problem.GetVariableName(
col);
573 for (
int i = 0; i < integral_var.bits().size(); ++i) {
574 boolean_problem->add_var_names(var_name + absl::StrFormat(
"_%d", i));
577 integral_variables_.push_back(integral_var);
578 global_to_boolean_[
col] = -integral_variables_.size();
579 variable_types_[
col] = INTEGRAL_EXPRESSED_AS_BOOLEAN;
583 void IntegralProblemConverter::ConvertAllConstraints(
584 const LinearProgram& linear_problem,
585 LinearBooleanProblem* boolean_problem) {
588 glop::SparseMatrix transpose;
589 transpose.PopulateFromTranspose(linear_problem.GetSparseMatrix());
591 double max_relative_error = 0.0;
592 double max_bound_error = 0.0;
594 double relative_error = 0.0;
595 double scaling_factor = 0.0;
597 for (RowIndex
row(0);
row < linear_problem.num_constraints(); ++
row) {
600 num_boolean_variables_, 0.0);
603 offset += AddWeightedIntegralVariable(
RowToColIndex(e.row()),
604 e.coefficient(), &dense_weights);
606 if (!HasNonZeroWeights(dense_weights)) {
612 for (VariableIndex
var(0);
var < num_boolean_variables_; ++
var) {
613 if (dense_weights[
var] != 0.0) {
619 &scaling_factor, &relative_error);
622 max_relative_error =
std::max(relative_error, max_relative_error);
625 LinearBooleanConstraint* constraint = boolean_problem->add_constraints();
626 constraint->set_name(linear_problem.GetConstraintName(
row));
627 const double bound_error =
628 ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, constraint);
629 max_bound_error =
std::max(max_bound_error, bound_error);
632 linear_problem.constraint_lower_bounds()[
row];
635 const double offset_scaled_lower_bound =
636 round(offset_lower_bound * scaling_factor - bound_error);
637 if (offset_scaled_lower_bound >=
639 LOG(WARNING) <<
"A constraint is trivially unsatisfiable.";
642 if (offset_scaled_lower_bound >
645 constraint->set_lower_bound(
646 static_cast<int64_t
>(offset_scaled_lower_bound) / gcd);
650 linear_problem.constraint_upper_bounds()[
row];
653 const double offset_scaled_upper_bound =
654 round(offset_upper_bound * scaling_factor + bound_error);
655 if (offset_scaled_upper_bound <=
657 LOG(WARNING) <<
"A constraint is trivially unsatisfiable.";
660 if (offset_scaled_upper_bound <
663 constraint->set_upper_bound(
664 static_cast<int64_t
>(offset_scaled_upper_bound) / gcd);
670 void IntegralProblemConverter::ConvertObjective(
671 const LinearProgram& linear_problem,
672 LinearBooleanProblem* boolean_problem) {
673 LinearObjective* objective = boolean_problem->mutable_objective();
676 num_boolean_variables_, 0.0);
678 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
679 offset += AddWeightedIntegralVariable(
680 col, linear_problem.objective_coefficients()[
col], &dense_weights);
685 for (VariableIndex
var(0);
var < num_boolean_variables_; ++
var) {
686 if (dense_weights[
var] != 0.0) {
690 double scaling_factor = 0.0;
691 double max_relative_error = 0.0;
692 double relative_error = 0.0;
695 &scaling_factor, &relative_error);
697 max_relative_error =
std::max(relative_error, max_relative_error);
698 VLOG(1) <<
"objective relative error: " << relative_error;
699 VLOG(1) <<
"objective scaling factor: " << scaling_factor / gcd;
701 ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, objective);
705 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
706 objective->set_offset((linear_problem.objective_offset() + offset) *
707 scaling_factor / gcd);
710 void IntegralProblemConverter::AddVariableConstraints(
711 const LinearProgram& linear_problem,
712 LinearBooleanProblem* boolean_problem) {
713 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
716 const int pos = global_to_boolean_[
col];
719 CHECK_EQ(BOOLEAN, variable_types_[
col]);
725 LinearBooleanConstraint* constraint =
726 boolean_problem->add_constraints();
727 constraint->set_lower_bound(fixed_value);
728 constraint->set_upper_bound(fixed_value);
729 constraint->add_literals(pos + 1);
730 constraint->add_coefficients(1);
733 CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[
col]);
736 const IntegralVariable& integral_var = integral_variables_[-pos - 1];
737 LinearBooleanConstraint* constraint =
738 boolean_problem->add_constraints();
739 for (
int i = 0; i < integral_var.bits().size(); ++i) {
740 constraint->add_literals(integral_var.bits()[i].value() + 1);
741 constraint->add_coefficients(integral_var.weights()[i]);
744 constraint->set_lower_bound(
static_cast<int64_t
>(ceil(
lower_bound)) -
745 integral_var.offset());
748 constraint->set_upper_bound(
static_cast<int64_t
>(floor(
upper_bound)) -
749 integral_var.offset());
756 bool IntegralProblemConverter::ConvertUsingExistingBooleans(
757 const LinearProgram& linear_problem, ColIndex
col,
758 IntegralVariable* integral_var) {
759 CHECK(
nullptr != integral_var);
760 CHECK_EQ(INTEGRAL, variable_types_[
col]);
762 const SparseMatrix& matrix = linear_problem.GetSparseMatrix();
763 const SparseMatrix& transpose = linear_problem.GetTransposeSparseMatrix();
764 for (
const SparseColumn::Entry var_entry : matrix.column(
col)) {
765 const RowIndex constraint = var_entry.row();
766 const Fractional lb = linear_problem.constraint_lower_bounds()[constraint];
767 const Fractional ub = linear_problem.constraint_upper_bounds()[constraint];
783 bool only_one_integral_variable =
true;
784 for (
const SparseColumn::Entry constraint_entry :
786 const ColIndex var_index =
RowToColIndex(constraint_entry.row());
787 if (var_index !=
col && variable_types_[var_index] == INTEGRAL) {
788 only_one_integral_variable =
false;
792 if (only_one_integral_variable &&
793 CreateVariableUsingConstraint(linear_problem, constraint,
799 integral_var->Clear();
803 bool IntegralProblemConverter::CreateVariableUsingConstraint(
804 const LinearProgram& linear_problem, RowIndex constraint,
805 IntegralVariable* integral_var) {
806 CHECK(
nullptr != integral_var);
807 integral_var->Clear();
809 const SparseMatrix& transpose = linear_problem.GetTransposeSparseMatrix();
811 num_boolean_variables_, 0.0);
813 int64_t variable_offset = 0;
814 for (
const SparseColumn::Entry constraint_entry :
817 if (variable_types_[
col] == INTEGRAL) {
818 scale = constraint_entry.coefficient();
819 }
else if (variable_types_[
col] == BOOLEAN) {
820 const int pos = global_to_boolean_[
col];
822 dense_weights[VariableIndex(pos)] -= constraint_entry.coefficient();
824 CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[
col]);
825 const int pos = global_to_boolean_[
col];
827 const IntegralVariable& local_integral_var =
828 integral_variables_[-pos - 1];
830 constraint_entry.coefficient() * local_integral_var.offset();
831 for (
int i = 0; i < local_integral_var.bits().size(); ++i) {
832 dense_weights[local_integral_var.bits()[i]] -=
833 constraint_entry.coefficient() * local_integral_var.weights()[i];
839 const Fractional lb = linear_problem.constraint_lower_bounds()[constraint];
840 const Fractional offset = (lb + variable_offset) / scale;
844 integral_var->set_offset(
static_cast<int64_t
>(offset));
846 for (VariableIndex
var(0);
var < dense_weights.size(); ++
var) {
847 if (dense_weights[
var] != 0.0) {
852 integral_var->set_weight(
var,
static_cast<int64_t
>(
weight));
859 Fractional IntegralProblemConverter::AddWeightedIntegralVariable(
862 CHECK(
nullptr != dense_weights);
869 const int pos = global_to_boolean_[
col];
872 (*dense_weights)[VariableIndex(pos)] +=
weight;
875 const IntegralVariable& integral_var = integral_variables_[-pos - 1];
876 for (
int i = 0; i < integral_var.bits().size(); ++i) {
877 (*dense_weights)[integral_var.bits()[i]] +=
878 integral_var.weights()[i] *
weight;
880 offset +=
weight * integral_var.offset();
886 double IntegralProblemConverter::ScaleAndSparsifyWeights(
887 double scaling_factor, int64_t gcd,
891 double bound_error = 0.0;
892 for (VariableIndex
var(0);
var < dense_weights.
size(); ++
var) {
893 if (dense_weights[
var] != 0.0) {
894 const double scaled_weight = dense_weights[
var] * scaling_factor;
895 bound_error += fabs(round(scaled_weight) - scaled_weight);
896 t->add_literals(
var.value() + 1);
897 t->add_coefficients(
static_cast<int64_t
>(round(scaled_weight)) / gcd);
903 bool IntegralProblemConverter::HasNonZeroWeights(
917 const SparseMatrix& matrix = linear_problem.GetSparseMatrix();
918 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
923 LOG(ERROR) <<
"Variable " <<
col <<
" out of bound: " <<
value
928 for (
const SparseColumn::Entry entry : matrix.column(
col)) {
929 constraint_values[entry.row()] += entry.coefficient() *
value;
933 for (RowIndex
row(0);
row < linear_problem.num_constraints(); ++
row) {
934 const Fractional lb = linear_problem.constraint_lower_bounds()[
row];
935 const Fractional ub = linear_problem.constraint_upper_bounds()[
row];
938 LOG(ERROR) <<
"Constraint " <<
row <<
" out of bound: " <<
value
939 <<
" should be in " << lb <<
" .. " << ub;
954 CHECK(variable_values !=
nullptr);
956 CHECK(best_bound !=
nullptr);
957 const bool use_initial_solution = (initial_solution.size() > 0);
958 if (use_initial_solution) {
959 CHECK_EQ(initial_solution.size(), linear_problem.num_variables());
965 variable_values->resize(linear_problem.num_variables(), 0);
967 LinearBooleanProblem boolean_problem;
968 std::vector<bool> boolean_initial_solution;
969 IntegralProblemConverter converter;
970 if (!converter.ConvertToBooleanProblem(linear_problem, initial_solution,
972 &boolean_initial_solution)) {
973 return BopSolveStatus::INVALID_PROBLEM;
976 BopSolver bop_solver(boolean_problem);
979 if (use_initial_solution) {
980 BopSolution bop_solution(boolean_problem,
"InitialSolution");
981 CHECK_EQ(boolean_initial_solution.size(), boolean_problem.num_variables());
982 for (
int i = 0; i < boolean_initial_solution.size(); ++i) {
983 bop_solution.SetValue(VariableIndex(i), boolean_initial_solution[i]);
989 if (
status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND ||
990 status == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
992 const BopSolution& solution = bop_solver.best_solution();
993 CHECK(solution.IsFeasible());
996 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
997 const int64_t
value = converter.GetSolutionValue(
col, solution);
1006 *best_bound =
status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND
1008 : bop_solver.GetScaledBestBound();
1013 void RunOneBop(
const BopParameters&
parameters,
int problem_index,
1015 LPDecomposer* decomposer,
DenseRow* variable_values,
1018 CHECK(decomposer !=
nullptr);
1019 CHECK(variable_values !=
nullptr);
1021 CHECK(best_bound !=
nullptr);
1022 CHECK(
status !=
nullptr);
1024 LinearProgram problem;
1025 decomposer->ExtractLocalProblem(problem_index, &problem);
1027 if (initial_solution.size() > 0) {
1028 local_initial_solution =
1029 decomposer->ExtractLocalAssignment(problem_index, initial_solution);
1033 const double total_num_variables =
std::max(
1034 1.0,
static_cast<double>(
1035 decomposer->original_problem().num_variables().value()));
1036 const double time_per_variable =
1037 parameters.max_time_in_seconds() / total_num_variables;
1038 const double deterministic_time_per_variable =
1039 parameters.max_deterministic_time() / total_num_variables;
1040 const int local_num_variables =
std::max(1, problem.num_variables().value());
1042 NestedTimeLimit subproblem_time_limit(
1044 std::max(time_per_variable * local_num_variables,
1045 parameters.decomposed_problem_min_time_in_seconds()),
1046 deterministic_time_per_variable * local_num_variables);
1049 subproblem_time_limit.GetTimeLimit(), variable_values,
1054 IntegralSolver::IntegralSolver()
1055 : parameters_(), variable_values_(), objective_value_(0.0) {}
1067 const LinearProgram& linear_problem,
1068 const DenseRow& user_provided_initial_solution) {
1076 const LinearProgram& linear_problem,
1079 DenseRow initial_solution = user_provided_initial_solution;
1080 if (initial_solution.size() > 0) {
1081 CHECK_EQ(initial_solution.size(), linear_problem.num_variables())
1082 <<
"The initial solution should have the same number of variables as "
1083 "the LinearProgram.";
1088 LinearProgram
const* lp = &linear_problem;
1091 if (lp->num_variables() >= parameters_.decomposer_num_variables_threshold()) {
1092 LPDecomposer decomposer;
1093 decomposer.Decompose(lp);
1094 const int num_sub_problems = decomposer.GetNumberOfProblems();
1095 VLOG(1) <<
"Problem is decomposable into " << num_sub_problems
1097 if (num_sub_problems > 1) {
1101 std::vector<Fractional> objective_values(num_sub_problems,
1103 std::vector<Fractional> best_bounds(num_sub_problems,
Fractional(0.0));
1104 std::vector<BopSolveStatus> statuses(num_sub_problems,
1107 for (
int i = 0; i < num_sub_problems; ++i) {
1108 RunOneBop(parameters_, i, initial_solution,
time_limit, &decomposer,
1110 &(best_bounds[i]), &(statuses[i]));
1115 objective_value_ = lp->objective_offset();
1117 for (
int i = 0; i < num_sub_problems; ++i) {
1118 objective_value_ += objective_values[i];
1119 best_bound_ += best_bounds[i];
1134 InternalSolve(*lp, parameters_, initial_solution,
time_limit,
1135 &variable_values_, &objective_value_, &best_bound_);
1139 &variable_values_, &objective_value_, &best_bound_);
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
static std::unique_ptr< TimeLimit > FromParameters(const Parameters ¶meters)
Creates a time limit object initialized from an object that provides methods max_time_in_seconds() an...
ABSL_MUST_USE_RESULT BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram &linear_problem, TimeLimit *time_limit)
const glop::DenseRow & variable_values() const
ABSL_MUST_USE_RESULT BopSolveStatus Solve(const glop::LinearProgram &linear_problem)
ModelSharedTimeLimit * time_limit
absl::Span< const double > coefficients
@ FEASIBLE_SOLUTION_FOUND
bool CheckSolution(const Model &model, const std::function< int64_t(Variable *)> &evaluator, SolverLogger *logger)
StrictITIVector< ColIndex, Fractional > DenseRow
ColIndex RowToColIndex(RowIndex row)
StrictITIVector< RowIndex, Fractional > DenseColumn
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
Collection of objects used to extend the Constraint Solver library.
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
int64_t ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
double GetBestScalingOfDoublesToInt64(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, int64_t max_absolute_sum)
int MostSignificantBitPosition64(uint64_t n)
double max_scaling_factor
constexpr double kInfinity
#define VLOG(verboselevel)