26 using ::operations_research::glop::ColIndex;
30 using ::operations_research::glop::LinearProgram;
31 using ::operations_research::glop::LPDecomposer;
32 using ::operations_research::glop::RowIndex;
33 using ::operations_research::glop::SparseColumn;
34 using ::operations_research::glop::SparseMatrix;
35 using ::operations_research::sat::LinearBooleanConstraint;
36 using ::operations_research::sat::LinearBooleanProblem;
37 using ::operations_research::sat::LinearObjective;
42 const double kTolerance = 1e-10;
43 return std::abs(x - round(x)) <= kTolerance;
49 bool ProblemIsBooleanAndHasOnlyIntegralConstraints(
50 const LinearProgram& linear_problem) {
51 const glop::SparseMatrix& matrix = linear_problem.GetSparseMatrix();
53 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
54 const Fractional lower_bound = linear_problem.variable_lower_bounds()[
col];
55 const Fractional upper_bound = linear_problem.variable_upper_bounds()[
col];
57 if (lower_bound <= -1.0 || upper_bound >= 2.0) {
62 for (
const SparseColumn::Entry e : matrix.column(
col)) {
75 void BuildBooleanProblemWithIntegralConstraints(
76 const LinearProgram& linear_problem,
const DenseRow& initial_solution,
77 LinearBooleanProblem* boolean_problem,
78 std::vector<bool>* boolean_initial_solution) {
79 CHECK(boolean_problem !=
nullptr);
80 boolean_problem->Clear();
82 const glop::SparseMatrix& matrix = linear_problem.GetSparseMatrix();
84 for (ColIndex
col(0);
col < matrix.num_cols(); ++
col) {
85 boolean_problem->add_var_names(linear_problem.GetVariableName(
col));
87 boolean_problem->set_num_variables(matrix.num_cols().value());
88 boolean_problem->set_name(linear_problem.name());
91 for (RowIndex
row(0);
row < matrix.num_rows(); ++
row) {
92 LinearBooleanConstraint*
const constraint =
93 boolean_problem->add_constraints();
94 constraint->set_name(linear_problem.GetConstraintName(
row));
95 if (linear_problem.constraint_lower_bounds()[
row] != -
kInfinity) {
96 constraint->set_lower_bound(
97 linear_problem.constraint_lower_bounds()[
row]);
99 if (linear_problem.constraint_upper_bounds()[
row] !=
kInfinity) {
100 constraint->set_upper_bound(
101 linear_problem.constraint_upper_bounds()[
row]);
106 for (ColIndex
col(0);
col < matrix.num_cols(); ++
col) {
107 for (
const SparseColumn::Entry e : matrix.column(
col)) {
108 LinearBooleanConstraint*
const constraint =
109 boolean_problem->mutable_constraints(e.row().value());
110 constraint->add_literals(
col.value() + 1);
111 constraint->add_coefficients(e.coefficient());
117 for (ColIndex
col(0);
col < matrix.num_cols(); ++
col) {
119 const int lb = std::round(linear_problem.variable_lower_bounds()[
col]);
120 const int ub = std::round(linear_problem.variable_upper_bounds()[
col]);
122 LinearBooleanConstraint*
ct = boolean_problem->add_constraints();
123 ct->set_lower_bound(ub);
124 ct->set_upper_bound(ub);
125 ct->add_literals(
col.value() + 1);
126 ct->add_coefficients(1.0);
132 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
133 const Fractional coeff = linear_problem.objective_coefficients()[
col];
136 double scaling_factor = 0.0;
137 double relative_error = 0.0;
141 LinearObjective*
const objective = boolean_problem->mutable_objective();
142 objective->set_offset(linear_problem.objective_offset() * scaling_factor /
147 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
148 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
149 const Fractional coeff = linear_problem.objective_coefficients()[
col];
150 const int64 value =
static_cast<int64>(round(coeff * scaling_factor)) / gcd;
152 objective->add_literals(
col.value() + 1);
153 objective->add_coefficients(
value);
158 if (linear_problem.IsMaximizationProblem()) {
163 if (!initial_solution.empty()) {
164 CHECK(boolean_initial_solution !=
nullptr);
165 CHECK_EQ(boolean_problem->num_variables(), initial_solution.size());
166 boolean_initial_solution->assign(boolean_problem->num_variables(),
false);
167 for (
int i = 0; i < initial_solution.size(); ++i) {
168 (*boolean_initial_solution)[i] = (initial_solution[ColIndex(i)] != 0);
181 class IntegralVariable {
190 void BuildFromRange(
int start_var_index,
Fractional lower_bound,
197 int GetNumberOfBooleanVariables()
const {
return bits_.size(); }
199 const std::vector<VariableIndex>& bits()
const {
return bits_; }
200 const std::vector<int64>& weights()
const {
return weights_; }
205 int64 GetSolutionValue(
const BopSolution& solution)
const;
211 std::vector<bool> GetBooleanSolutionValues(
int64 integral_value)
const;
213 std::string DebugString()
const;
219 std::vector<VariableIndex> bits_;
220 std::vector<int64> weights_;
226 bool can_be_reversed_;
229 IntegralVariable::IntegralVariable()
230 : bits_(), weights_(),
offset_(0), can_be_reversed_(true) {}
232 void IntegralVariable::BuildFromRange(
int start_var_index,
242 const int64 integral_lower_bound =
static_cast<int64>(ceil(lower_bound));
243 const int64 integral_upper_bound =
static_cast<int64>(floor(upper_bound));
244 offset_ = integral_lower_bound;
245 const int64 delta = integral_upper_bound - integral_lower_bound;
247 for (
int i = 0; i < num_used_bits; ++i) {
248 bits_.push_back(VariableIndex(start_var_index + i));
249 weights_.push_back(1ULL << i);
253 void IntegralVariable::Clear() {
257 can_be_reversed_ =
true;
260 void IntegralVariable::set_weight(VariableIndex
var,
int64 weight) {
261 bits_.push_back(
var);
262 weights_.push_back(
weight);
263 can_be_reversed_ =
false;
266 int64 IntegralVariable::GetSolutionValue(
const BopSolution& solution)
const {
268 for (
int i = 0; i < bits_.size(); ++i) {
269 value += weights_[i] * solution.Value(bits_[i]);
274 std::vector<bool> IntegralVariable::GetBooleanSolutionValues(
275 int64 integral_value)
const {
276 if (can_be_reversed_) {
277 DCHECK(std::is_sorted(weights_.begin(), weights_.end()));
278 std::vector<bool> boolean_values(weights_.size(),
false);
280 for (
int i = weights_.size() - 1; i >= 0; --i) {
281 if (remaining_value >= weights_[i]) {
282 boolean_values[i] =
true;
283 remaining_value -= weights_[i];
287 <<
"Couldn't map integral value to boolean variables.";
288 return boolean_values;
290 return std::vector<bool>();
293 std::string IntegralVariable::DebugString()
const {
295 CHECK_EQ(bits_.size(), weights_.size());
296 for (
int i = 0; i < bits_.size(); ++i) {
297 str += absl::StrFormat(
"%d [%d] ", weights_[i], bits_[i].
value());
299 str += absl::StrFormat(
" Offset: %d",
offset_);
320 class IntegralProblemConverter {
322 IntegralProblemConverter();
328 bool ConvertToBooleanProblem(
const LinearProgram& linear_problem,
330 LinearBooleanProblem* boolean_problem,
331 std::vector<bool>* boolean_initial_solution);
335 int64 GetSolutionValue(ColIndex global_col,
336 const BopSolution& solution)
const;
342 bool CheckProblem(
const LinearProgram& linear_problem)
const;
345 void InitVariableTypes(
const LinearProgram& linear_problem,
346 LinearBooleanProblem* boolean_problem);
349 void ConvertAllVariables(
const LinearProgram& linear_problem,
350 LinearBooleanProblem* boolean_problem);
353 void AddVariableConstraints(
const LinearProgram& linear_problem,
354 LinearBooleanProblem* boolean_problem);
357 void ConvertAllConstraints(
const LinearProgram& linear_problem,
358 LinearBooleanProblem* boolean_problem);
361 void ConvertObjective(
const LinearProgram& linear_problem,
362 LinearBooleanProblem* boolean_problem);
368 bool ConvertUsingExistingBooleans(
const LinearProgram& linear_problem,
370 IntegralVariable* integral_var);
381 bool CreateVariableUsingConstraint(
const LinearProgram& linear_problem,
383 IntegralVariable* integral_var);
397 double ScaleAndSparsifyWeights(
398 double scaling_factor,
int64 gcd,
402 bool HasNonZeroWeights(
405 bool problem_is_boolean_and_has_only_integral_constraints_;
412 std::vector<IntegralVariable> integral_variables_;
413 std::vector<ColIndex> integral_indices_;
414 int num_boolean_variables_;
416 enum VariableType { BOOLEAN, INTEGRAL, INTEGRAL_EXPRESSED_AS_BOOLEAN };
420 IntegralProblemConverter::IntegralProblemConverter()
421 : global_to_boolean_(),
422 integral_variables_(),
424 num_boolean_variables_(0),
427 bool IntegralProblemConverter::ConvertToBooleanProblem(
428 const LinearProgram& linear_problem,
const DenseRow& initial_solution,
429 LinearBooleanProblem* boolean_problem,
430 std::vector<bool>* boolean_initial_solution) {
431 bool use_initial_solution = (initial_solution.size() > 0);
432 if (use_initial_solution) {
433 CHECK_EQ(initial_solution.size(), linear_problem.num_variables())
434 <<
"The initial solution should have the same number of variables as "
435 "the LinearProgram.";
436 CHECK(boolean_initial_solution !=
nullptr);
438 if (!CheckProblem(linear_problem)) {
442 problem_is_boolean_and_has_only_integral_constraints_ =
443 ProblemIsBooleanAndHasOnlyIntegralConstraints(linear_problem);
444 if (problem_is_boolean_and_has_only_integral_constraints_) {
445 BuildBooleanProblemWithIntegralConstraints(linear_problem, initial_solution,
447 boolean_initial_solution);
451 InitVariableTypes(linear_problem, boolean_problem);
452 ConvertAllVariables(linear_problem, boolean_problem);
453 boolean_problem->set_num_variables(num_boolean_variables_);
454 boolean_problem->set_name(linear_problem.name());
456 AddVariableConstraints(linear_problem, boolean_problem);
457 ConvertAllConstraints(linear_problem, boolean_problem);
458 ConvertObjective(linear_problem, boolean_problem);
461 if (linear_problem.IsMaximizationProblem()) {
465 if (use_initial_solution) {
466 boolean_initial_solution->assign(boolean_problem->num_variables(),
false);
467 for (ColIndex global_col(0); global_col < global_to_boolean_.size();
469 const int col = global_to_boolean_[global_col];
471 (*boolean_initial_solution)[
col] = (initial_solution[global_col] != 0);
473 const IntegralVariable& integral_variable =
474 integral_variables_[-
col - 1];
475 const std::vector<VariableIndex>& boolean_cols =
476 integral_variable.bits();
477 const std::vector<bool>& boolean_values =
478 integral_variable.GetBooleanSolutionValues(
479 round(initial_solution[global_col]));
480 if (!boolean_values.empty()) {
481 CHECK_EQ(boolean_cols.size(), boolean_values.size());
482 for (
int i = 0; i < boolean_values.size(); ++i) {
483 const int boolean_col = boolean_cols[i].value();
484 (*boolean_initial_solution)[boolean_col] = boolean_values[i];
494 int64 IntegralProblemConverter::GetSolutionValue(
495 ColIndex global_col,
const BopSolution& solution)
const {
496 if (problem_is_boolean_and_has_only_integral_constraints_) {
497 return solution.Value(VariableIndex(global_col.value()));
500 const int pos = global_to_boolean_[global_col];
501 return pos >= 0 ? solution.Value(VariableIndex(pos))
502 : integral_variables_[-pos - 1].GetSolutionValue(solution);
505 bool IntegralProblemConverter::CheckProblem(
506 const LinearProgram& linear_problem)
const {
507 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
508 if (!linear_problem.IsVariableInteger(
col)) {
509 LOG(
ERROR) <<
"Variable " << linear_problem.GetVariableName(
col)
510 <<
" is continuous. This is not supported by BOP.";
513 if (linear_problem.variable_lower_bounds()[
col] == -
kInfinity) {
514 LOG(
ERROR) <<
"Variable " << linear_problem.GetVariableName(
col)
515 <<
" has no lower bound. This is not supported by BOP.";
518 if (linear_problem.variable_upper_bounds()[
col] ==
kInfinity) {
519 LOG(
ERROR) <<
"Variable " << linear_problem.GetVariableName(
col)
520 <<
" has no upper bound. This is not supported by BOP.";
527 void IntegralProblemConverter::InitVariableTypes(
528 const LinearProgram& linear_problem,
529 LinearBooleanProblem* boolean_problem) {
530 global_to_boolean_.assign(linear_problem.num_variables().value(), 0);
531 variable_types_.assign(linear_problem.num_variables().value(), INTEGRAL);
532 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
533 const Fractional lower_bound = linear_problem.variable_lower_bounds()[
col];
534 const Fractional upper_bound = linear_problem.variable_upper_bounds()[
col];
536 if (lower_bound > -1.0 && upper_bound < 2.0) {
538 variable_types_[
col] = BOOLEAN;
539 global_to_boolean_[
col] = num_boolean_variables_;
540 ++num_boolean_variables_;
541 boolean_problem->add_var_names(linear_problem.GetVariableName(
col));
544 variable_types_[
col] = INTEGRAL;
545 integral_indices_.push_back(
col);
550 void IntegralProblemConverter::ConvertAllVariables(
551 const LinearProgram& linear_problem,
552 LinearBooleanProblem* boolean_problem) {
553 for (
const ColIndex
col : integral_indices_) {
555 IntegralVariable integral_var;
556 if (!ConvertUsingExistingBooleans(linear_problem,
col, &integral_var)) {
558 linear_problem.variable_lower_bounds()[
col];
560 linear_problem.variable_upper_bounds()[
col];
561 integral_var.BuildFromRange(num_boolean_variables_, lower_bound,
563 num_boolean_variables_ += integral_var.GetNumberOfBooleanVariables();
564 const std::string var_name = linear_problem.GetVariableName(
col);
565 for (
int i = 0; i < integral_var.bits().size(); ++i) {
566 boolean_problem->add_var_names(var_name + absl::StrFormat(
"_%d", i));
569 integral_variables_.push_back(integral_var);
570 global_to_boolean_[
col] = -integral_variables_.size();
571 variable_types_[
col] = INTEGRAL_EXPRESSED_AS_BOOLEAN;
575 void IntegralProblemConverter::ConvertAllConstraints(
576 const LinearProgram& linear_problem,
577 LinearBooleanProblem* boolean_problem) {
580 glop::SparseMatrix transpose;
581 transpose.PopulateFromTranspose(linear_problem.GetSparseMatrix());
583 double max_relative_error = 0.0;
584 double max_bound_error = 0.0;
586 double relative_error = 0.0;
587 double scaling_factor = 0.0;
589 for (RowIndex
row(0);
row < linear_problem.num_constraints(); ++
row) {
592 num_boolean_variables_, 0.0);
595 offset += AddWeightedIntegralVariable(
RowToColIndex(e.row()),
596 e.coefficient(), &dense_weights);
598 if (!HasNonZeroWeights(dense_weights)) {
604 for (VariableIndex
var(0);
var < num_boolean_variables_; ++
var) {
605 if (dense_weights[
var] != 0.0) {
612 max_relative_error =
std::max(relative_error, max_relative_error);
615 LinearBooleanConstraint* constraint = boolean_problem->add_constraints();
616 constraint->set_name(linear_problem.GetConstraintName(
row));
617 const double bound_error =
618 ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, constraint);
619 max_bound_error =
std::max(max_bound_error, bound_error);
622 linear_problem.constraint_lower_bounds()[
row];
624 const Fractional offset_lower_bound = lower_bound - offset;
625 const double offset_scaled_lower_bound =
626 round(offset_lower_bound * scaling_factor - bound_error);
627 if (offset_scaled_lower_bound >=
static_cast<double>(
kint64max)) {
628 LOG(
WARNING) <<
"A constraint is trivially unsatisfiable.";
631 if (offset_scaled_lower_bound > -
static_cast<double>(
kint64max)) {
633 constraint->set_lower_bound(
634 static_cast<int64>(offset_scaled_lower_bound) / gcd);
638 linear_problem.constraint_upper_bounds()[
row];
640 const Fractional offset_upper_bound = upper_bound - offset;
641 const double offset_scaled_upper_bound =
642 round(offset_upper_bound * scaling_factor + bound_error);
643 if (offset_scaled_upper_bound <= -
static_cast<double>(
kint64max)) {
644 LOG(
WARNING) <<
"A constraint is trivially unsatisfiable.";
647 if (offset_scaled_upper_bound <
static_cast<double>(
kint64max)) {
649 constraint->set_upper_bound(
650 static_cast<int64>(offset_scaled_upper_bound) / gcd);
656 void IntegralProblemConverter::ConvertObjective(
657 const LinearProgram& linear_problem,
658 LinearBooleanProblem* boolean_problem) {
659 LinearObjective* objective = boolean_problem->mutable_objective();
662 num_boolean_variables_, 0.0);
664 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
665 offset += AddWeightedIntegralVariable(
666 col, linear_problem.objective_coefficients()[
col], &dense_weights);
671 for (VariableIndex
var(0);
var < num_boolean_variables_; ++
var) {
672 if (dense_weights[
var] != 0.0) {
676 double scaling_factor = 0.0;
677 double max_relative_error = 0.0;
678 double relative_error = 0.0;
682 max_relative_error =
std::max(relative_error, max_relative_error);
683 VLOG(1) <<
"objective relative error: " << relative_error;
684 VLOG(1) <<
"objective scaling factor: " << scaling_factor / gcd;
686 ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, objective);
690 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
691 objective->set_offset((linear_problem.objective_offset() + offset) *
692 scaling_factor / gcd);
695 void IntegralProblemConverter::AddVariableConstraints(
696 const LinearProgram& linear_problem,
697 LinearBooleanProblem* boolean_problem) {
698 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
699 const Fractional lower_bound = linear_problem.variable_lower_bounds()[
col];
700 const Fractional upper_bound = linear_problem.variable_upper_bounds()[
col];
701 const int pos = global_to_boolean_[
col];
705 const bool is_fixed = (lower_bound > -1.0 && upper_bound < 1.0) ||
706 (lower_bound > 0.0 && upper_bound < 2.0);
709 const int fixed_value = lower_bound > -1.0 && upper_bound < 1.0 ? 0 : 1;
710 LinearBooleanConstraint* constraint =
711 boolean_problem->add_constraints();
712 constraint->set_lower_bound(fixed_value);
713 constraint->set_upper_bound(fixed_value);
714 constraint->add_literals(pos + 1);
715 constraint->add_coefficients(1);
718 CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[
col]);
721 const IntegralVariable& integral_var = integral_variables_[-pos - 1];
722 LinearBooleanConstraint* constraint =
723 boolean_problem->add_constraints();
724 for (
int i = 0; i < integral_var.bits().size(); ++i) {
725 constraint->add_literals(integral_var.bits()[i].value() + 1);
726 constraint->add_coefficients(integral_var.weights()[i]);
729 constraint->set_lower_bound(
static_cast<int64>(ceil(lower_bound)) -
730 integral_var.offset());
733 constraint->set_upper_bound(
static_cast<int64>(floor(upper_bound)) -
734 integral_var.offset());
741 bool IntegralProblemConverter::ConvertUsingExistingBooleans(
742 const LinearProgram& linear_problem, ColIndex
col,
743 IntegralVariable* integral_var) {
744 CHECK(
nullptr != integral_var);
747 const SparseMatrix& matrix = linear_problem.GetSparseMatrix();
748 const SparseMatrix& transpose = linear_problem.GetTransposeSparseMatrix();
749 for (
const SparseColumn::Entry var_entry : matrix.column(
col)) {
750 const RowIndex constraint = var_entry.row();
751 const Fractional lb = linear_problem.constraint_lower_bounds()[constraint];
752 const Fractional ub = linear_problem.constraint_upper_bounds()[constraint];
768 bool only_one_integral_variable =
true;
769 for (
const SparseColumn::Entry constraint_entry :
771 const ColIndex var_index =
RowToColIndex(constraint_entry.row());
772 if (var_index !=
col && variable_types_[var_index] == INTEGRAL) {
773 only_one_integral_variable =
false;
777 if (only_one_integral_variable &&
778 CreateVariableUsingConstraint(linear_problem, constraint,
784 integral_var->Clear();
788 bool IntegralProblemConverter::CreateVariableUsingConstraint(
789 const LinearProgram& linear_problem, RowIndex constraint,
790 IntegralVariable* integral_var) {
791 CHECK(
nullptr != integral_var);
792 integral_var->Clear();
794 const SparseMatrix& transpose = linear_problem.GetTransposeSparseMatrix();
796 num_boolean_variables_, 0.0);
798 int64 variable_offset = 0;
799 for (
const SparseColumn::Entry constraint_entry :
802 if (variable_types_[
col] == INTEGRAL) {
803 scale = constraint_entry.coefficient();
804 }
else if (variable_types_[
col] == BOOLEAN) {
805 const int pos = global_to_boolean_[
col];
807 dense_weights[VariableIndex(pos)] -= constraint_entry.coefficient();
809 CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[
col]);
810 const int pos = global_to_boolean_[
col];
812 const IntegralVariable& local_integral_var =
813 integral_variables_[-pos - 1];
815 constraint_entry.coefficient() * local_integral_var.offset();
816 for (
int i = 0; i < local_integral_var.bits().size(); ++i) {
817 dense_weights[local_integral_var.bits()[i]] -=
818 constraint_entry.coefficient() * local_integral_var.weights()[i];
824 const Fractional lb = linear_problem.constraint_lower_bounds()[constraint];
825 const Fractional offset = (lb + variable_offset) / scale;
829 integral_var->set_offset(
static_cast<int64>(offset));
831 for (VariableIndex
var(0);
var < dense_weights.size(); ++
var) {
832 if (dense_weights[
var] != 0.0) {
844 Fractional IntegralProblemConverter::AddWeightedIntegralVariable(
847 CHECK(
nullptr != dense_weights);
854 const int pos = global_to_boolean_[
col];
857 (*dense_weights)[VariableIndex(pos)] +=
weight;
860 const IntegralVariable& integral_var = integral_variables_[-pos - 1];
861 for (
int i = 0; i < integral_var.bits().size(); ++i) {
862 (*dense_weights)[integral_var.bits()[i]] +=
863 integral_var.weights()[i] *
weight;
865 offset +=
weight * integral_var.offset();
871 double IntegralProblemConverter::ScaleAndSparsifyWeights(
872 double scaling_factor,
int64 gcd,
876 double bound_error = 0.0;
877 for (VariableIndex
var(0);
var < dense_weights.
size(); ++
var) {
878 if (dense_weights[
var] != 0.0) {
879 const double scaled_weight = dense_weights[
var] * scaling_factor;
880 bound_error += fabs(round(scaled_weight) - scaled_weight);
881 t->add_literals(
var.value() + 1);
882 t->add_coefficients(
static_cast<int64>(round(scaled_weight)) / gcd);
888 bool IntegralProblemConverter::HasNonZeroWeights(
902 const SparseMatrix& matrix = linear_problem.GetSparseMatrix();
903 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
904 const Fractional lower_bound = linear_problem.variable_lower_bounds()[
col];
905 const Fractional upper_bound = linear_problem.variable_upper_bounds()[
col];
907 if (lower_bound >
value || upper_bound <
value) {
909 <<
" should be in " << lower_bound <<
" .. " << upper_bound;
913 for (
const SparseColumn::Entry entry : matrix.column(
col)) {
914 constraint_values[entry.row()] += entry.coefficient() *
value;
918 for (RowIndex
row(0);
row < linear_problem.num_constraints(); ++
row) {
919 const Fractional lb = linear_problem.constraint_lower_bounds()[
row];
920 const Fractional ub = linear_problem.constraint_upper_bounds()[
row];
924 <<
" should be in " << lb <<
" .. " << ub;
939 CHECK(variable_values !=
nullptr);
940 CHECK(objective_value !=
nullptr);
941 CHECK(best_bound !=
nullptr);
942 const bool use_initial_solution = (initial_solution.size() > 0);
943 if (use_initial_solution) {
944 CHECK_EQ(initial_solution.size(), linear_problem.num_variables());
950 variable_values->resize(linear_problem.num_variables(), 0);
952 LinearBooleanProblem boolean_problem;
953 std::vector<bool> boolean_initial_solution;
954 IntegralProblemConverter converter;
955 if (!converter.ConvertToBooleanProblem(linear_problem, initial_solution,
957 &boolean_initial_solution)) {
958 return BopSolveStatus::INVALID_PROBLEM;
961 BopSolver bop_solver(boolean_problem);
964 if (use_initial_solution) {
965 BopSolution bop_solution(boolean_problem,
"InitialSolution");
966 CHECK_EQ(boolean_initial_solution.size(), boolean_problem.num_variables());
967 for (
int i = 0; i < boolean_initial_solution.size(); ++i) {
968 bop_solution.SetValue(VariableIndex(i), boolean_initial_solution[i]);
970 status = bop_solver.SolveWithTimeLimit(bop_solution,
time_limit);
972 status = bop_solver.SolveWithTimeLimit(
time_limit);
974 if (status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND ||
975 status == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
977 const BopSolution& solution = bop_solver.best_solution();
978 CHECK(solution.IsFeasible());
980 *objective_value = linear_problem.objective_offset();
981 for (ColIndex
col(0);
col < linear_problem.num_variables(); ++
col) {
982 const int64 value = converter.GetSolutionValue(
col, solution);
984 *objective_value +=
value * linear_problem.objective_coefficients()[
col];
991 *best_bound = status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND
993 : bop_solver.GetScaledBestBound();
998 void RunOneBop(
const BopParameters&
parameters,
int problem_index,
1000 LPDecomposer* decomposer,
DenseRow* variable_values,
1003 CHECK(decomposer !=
nullptr);
1004 CHECK(variable_values !=
nullptr);
1005 CHECK(objective_value !=
nullptr);
1006 CHECK(best_bound !=
nullptr);
1007 CHECK(status !=
nullptr);
1009 LinearProgram problem;
1010 decomposer->ExtractLocalProblem(problem_index, &problem);
1012 if (initial_solution.size() > 0) {
1013 local_initial_solution =
1014 decomposer->ExtractLocalAssignment(problem_index, initial_solution);
1018 const double total_num_variables =
std::max(
1019 1.0,
static_cast<double>(
1020 decomposer->original_problem().num_variables().value()));
1021 const double time_per_variable =
1022 parameters.max_time_in_seconds() / total_num_variables;
1023 const double deterministic_time_per_variable =
1024 parameters.max_deterministic_time() / total_num_variables;
1025 const int local_num_variables =
std::max(1, problem.num_variables().value());
1027 NestedTimeLimit subproblem_time_limit(
1029 std::max(time_per_variable * local_num_variables,
1030 parameters.decomposed_problem_min_time_in_seconds()),
1031 deterministic_time_per_variable * local_num_variables);
1033 *status = InternalSolve(problem,
parameters, local_initial_solution,
1034 subproblem_time_limit.GetTimeLimit(), variable_values,
1035 objective_value, best_bound);
1039 IntegralSolver::IntegralSolver()
1040 : parameters_(), variable_values_(), objective_value_(0.0) {}
1052 const LinearProgram& linear_problem,
1053 const DenseRow& user_provided_initial_solution) {
1061 const LinearProgram& linear_problem,
1064 DenseRow initial_solution = user_provided_initial_solution;
1065 if (initial_solution.size() > 0) {
1066 CHECK_EQ(initial_solution.size(), linear_problem.num_variables())
1067 <<
"The initial solution should have the same number of variables as "
1068 "the LinearProgram.";
1073 LinearProgram
const* lp = &linear_problem;
1076 if (lp->num_variables() >= parameters_.decomposer_num_variables_threshold()) {
1077 LPDecomposer decomposer;
1078 decomposer.Decompose(lp);
1079 const int num_sub_problems = decomposer.GetNumberOfProblems();
1080 VLOG(1) <<
"Problem is decomposable into " << num_sub_problems
1082 if (num_sub_problems > 1) {
1086 std::vector<Fractional> objective_values(num_sub_problems,
1088 std::vector<Fractional> best_bounds(num_sub_problems,
Fractional(0.0));
1089 std::vector<BopSolveStatus> statuses(num_sub_problems,
1092 for (
int i = 0; i < num_sub_problems; ++i) {
1093 RunOneBop(parameters_, i, initial_solution,
time_limit, &decomposer,
1095 &(best_bounds[i]), &(statuses[i]));
1100 objective_value_ = lp->objective_offset();
1102 for (
int i = 0; i < num_sub_problems; ++i) {
1103 objective_value_ += objective_values[i];
1104 best_bound_ += best_bounds[i];
1105 if (statuses[i] == BopSolveStatus::NO_SOLUTION_FOUND ||
1106 statuses[i] == BopSolveStatus::INFEASIBLE_PROBLEM ||
1111 if (statuses[i] == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
1112 status = BopSolveStatus::FEASIBLE_SOLUTION_FOUND;
1119 InternalSolve(*lp, parameters_, initial_solution,
time_limit,
1120 &variable_values_, &objective_value_, &best_bound_);
1123 status = InternalSolve(*lp, parameters_, initial_solution,
time_limit,
1124 &variable_values_, &objective_value_, &best_bound_);