24 #include "absl/container/flat_hash_map.h"
25 #include "absl/numeric/int128.h"
50 const double LinearProgrammingConstraint::kCpEpsilon = 1e-4;
51 const double LinearProgrammingConstraint::kLpEpsilon = 1e-6;
55 for (
const glop::ColIndex
col : non_zeros_) {
56 dense_vector_[
col] = IntegerValue(0);
58 dense_vector_.
resize(size, IntegerValue(0));
60 dense_vector_.
assign(size, IntegerValue(0));
62 for (
const glop::ColIndex
col : non_zeros_) {
63 is_zeros_[
col] =
true;
65 is_zeros_.
resize(size,
true);
73 dense_vector_[
col] = IntegerValue(add);
74 if (is_sparse_ && is_zeros_[
col]) {
75 is_zeros_[
col] =
false;
76 non_zeros_.push_back(
col);
82 IntegerValue multiplier,
83 const std::vector<std::pair<glop::ColIndex, IntegerValue>>& terms) {
84 const double threshold = 0.1 *
static_cast<double>(dense_vector_.
size());
85 if (is_sparse_ &&
static_cast<double>(terms.size()) < threshold) {
86 for (
const std::pair<glop::ColIndex, IntegerValue> term : terms) {
87 if (is_zeros_[term.first]) {
88 is_zeros_[term.first] =
false;
89 non_zeros_.push_back(term.first);
91 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
95 if (
static_cast<double>(non_zeros_.size()) < threshold) {
100 for (
const std::pair<glop::ColIndex, IntegerValue> term : terms) {
101 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
110 const std::vector<IntegerVariable>& integer_variables,
112 result->
vars.clear();
115 std::sort(non_zeros_.begin(), non_zeros_.end());
116 for (
const glop::ColIndex
col : non_zeros_) {
117 const IntegerValue coeff = dense_vector_[
col];
118 if (coeff == 0)
continue;
119 result->
vars.push_back(integer_variables[
col.value()]);
120 result->
coeffs.push_back(coeff);
123 const int size = dense_vector_.
size();
124 for (glop::ColIndex
col(0);
col < size; ++
col) {
125 const IntegerValue coeff = dense_vector_[
col];
126 if (coeff == 0)
continue;
127 result->
vars.push_back(integer_variables[
col.value()]);
128 result->
coeffs.push_back(coeff);
132 result->
ub = upper_bound;
135 std::vector<std::pair<glop::ColIndex, IntegerValue>>
137 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
139 std::sort(non_zeros_.begin(), non_zeros_.end());
140 for (
const glop::ColIndex
col : non_zeros_) {
141 const IntegerValue coeff = dense_vector_[
col];
142 if (coeff != 0) result.push_back({
col, coeff});
145 const int size = dense_vector_.
size();
146 for (glop::ColIndex
col(0);
col < size; ++
col) {
147 const IntegerValue coeff = dense_vector_[
col];
148 if (coeff != 0) result.push_back({
col, coeff});
157 : constraint_manager_(
model),
158 sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
165 implied_bounds_processor_({}, integer_trail_,
168 expanded_lp_solution_(
174 if (sat_parameters_.use_branching_in_lp() ||
175 sat_parameters_.search_branching() == SatParameters::LP_SEARCH) {
176 compute_reduced_cost_averages_ =
true;
180 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
184 VLOG(1) <<
"Total number of simplex iterations: "
185 << total_num_simplex_iterations_;
186 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
187 if (num_solves_by_status_[i] == 0)
continue;
189 << num_solves_by_status_[i];
195 DCHECK(!lp_constraint_is_registered_);
196 constraint_manager_.
Add(
ct);
203 for (
const IntegerVariable
var :
ct.vars) {
208 glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
209 IntegerVariable positive_variable) {
211 const auto it = mirror_lp_variable_.find(positive_variable);
212 if (it == mirror_lp_variable_.end()) {
213 const glop::ColIndex
col(integer_variables_.size());
215 mirror_lp_variable_[positive_variable] =
col;
216 integer_variables_.push_back(positive_variable);
217 lp_solution_.push_back(std::numeric_limits<double>::infinity());
218 lp_reduced_cost_.push_back(0.0);
219 (*dispatcher_)[positive_variable] =
this;
223 if (
index >= expanded_lp_solution_.
size()) {
232 IntegerValue coeff) {
233 CHECK(!lp_constraint_is_registered_);
234 objective_is_defined_ =
true;
236 if (ivar != pos_var) coeff = -coeff;
239 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
240 integer_objective_.push_back({
col, coeff});
241 objective_infinity_norm_ =
258 bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
261 infinity_norms_.
clear();
262 const auto& all_constraints = constraint_manager_.
AllConstraints();
266 integer_lp_.
push_back(LinearConstraintInternal());
267 LinearConstraintInternal& new_ct = integer_lp_.
back();
270 const int size =
ct.vars.size();
271 IntegerValue infinity_norm(0);
273 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
282 for (
int i = 0; i < size; ++i) {
284 IntegerVariable
var =
ct.vars[i];
285 IntegerValue coeff =
ct.coeffs[i];
291 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var), coeff});
293 infinity_norms_.
push_back(infinity_norm);
296 std::sort(new_ct.terms.begin(), new_ct.terms.end());
301 for (
int i = 0; i < integer_variables_.size(); ++i) {
308 objective_infinity_norm_ = 0;
309 for (
const auto entry : integer_objective_) {
310 const IntegerVariable
var = integer_variables_[entry.first.value()];
312 integer_objective_offset_ +=
316 objective_infinity_norm_ =
318 integer_objective_[new_size++] = entry;
321 objective_infinity_norm_ =
323 integer_objective_.resize(new_size);
326 for (
const LinearConstraintInternal&
ct : integer_lp_) {
329 for (
const auto& term :
ct.terms) {
341 const int num_vars = integer_variables_.size();
342 for (
int i = 0; i < num_vars; i++) {
343 const IntegerVariable cp_var = integer_variables_[i];
351 glop::GlopParameters params;
352 params.set_cost_scaling(glop::GlopParameters::MEAN_COST_SCALING);
353 scaler_.
Scale(params, &lp_data_);
354 UpdateBoundsOfLpVariables();
359 if (sat_parameters_.polish_lp_solution()) {
361 for (
int i = 0; i < num_vars; ++i) {
362 const IntegerVariable cp_var = integer_variables_[i];
365 if (lb != 0 || ub != 1)
continue;
376 <<
" Managed constraints.";
380 LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
382 glop::BasisState basis_state = simplex_.
GetState();
384 const glop::Status status = simplex_.
Solve(lp_data_, time_limit_);
388 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
394 info.status == glop::ProblemStatus::DUAL_FEASIBLE) {
397 info.new_obj_bound = IntegerValue(
398 static_cast<int64>(std::ceil(info.lp_objective - kCpEpsilon)));
403 void LinearProgrammingConstraint::FillReducedCostReasonIn(
405 std::vector<IntegerLiteral>* integer_reason) {
406 integer_reason->clear();
407 const int num_vars = integer_variables_.size();
408 for (
int i = 0; i < num_vars; i++) {
409 const double rc = reduced_costs[glop::ColIndex(i)];
410 if (rc > kLpEpsilon) {
411 integer_reason->push_back(
413 }
else if (rc < -kLpEpsilon) {
414 integer_reason->push_back(
422 bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
424 DCHECK(lp_solution_is_set_);
426 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
429 integer_reason_.clear();
431 bool deductions_were_made =
false;
433 UpdateBoundsOfLpVariables();
435 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
439 const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
443 if (current_value < current_lb || current_value > current_ub) {
448 const double new_ub = std::floor(current_value);
451 LPSolveInfo lower_branch_info = SolveLpForBranching();
453 lower_branch_info.status != glop::ProblemStatus::DUAL_FEASIBLE &&
454 lower_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
458 if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
461 positive_var, IntegerValue(std::ceil(current_value)));
462 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
465 deductions_were_made =
true;
466 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
471 const double new_lb = std::ceil(current_value);
474 LPSolveInfo upper_branch_info = SolveLpForBranching();
476 upper_branch_info.status != glop::ProblemStatus::DUAL_FEASIBLE &&
477 upper_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
478 return deductions_were_made;
481 if (upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
483 if (lower_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
485 positive_var, IntegerValue(std::floor(current_value)));
486 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
487 return deductions_were_made;
489 deductions_were_made =
true;
491 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
492 return deductions_were_made;
497 if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED &&
498 upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
500 }
else if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
501 approximate_obj_lb = upper_branch_info.new_obj_bound;
502 }
else if (upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
503 approximate_obj_lb = lower_branch_info.new_obj_bound;
505 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
506 upper_branch_info.new_obj_bound);
511 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
514 const IntegerLiteral deduction =
516 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
517 return deductions_were_made;
524 DCHECK(!lp_constraint_is_registered_);
525 lp_constraint_is_registered_ =
true;
530 std::sort(integer_objective_.begin(), integer_objective_.end());
533 if (!sat_parameters_.add_lp_constraints_lazily()) {
536 if (!CreateLpFromConstraintManager()) {
542 const int watcher_id = watcher->
Register(
this);
543 const int num_vars = integer_variables_.size();
544 for (
int i = 0; i < num_vars; i++) {
547 if (objective_is_defined_) {
560 optimal_constraints_.resize(rev_optimal_constraints_size_);
561 if (lp_solution_is_set_ && level < lp_solution_level_) {
562 lp_solution_is_set_ =
false;
570 if (level == 0 && !level_zero_lp_solution_.empty()) {
571 lp_solution_is_set_ =
true;
572 lp_solution_ = level_zero_lp_solution_;
573 lp_solution_level_ = 0;
574 for (
int i = 0; i < lp_solution_.size(); i++) {
575 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
576 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
583 for (
const IntegerVariable
var : generator.
vars) {
586 cut_generators_.push_back(std::move(generator));
590 const std::vector<int>& watch_indices) {
591 if (!lp_solution_is_set_)
return Propagate();
601 for (
const int index : watch_indices) {
607 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
622 glop::ColIndex
var) {
627 IntegerVariable variable)
const {
628 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
632 IntegerVariable variable)
const {
633 return lp_reduced_cost_[
gtl::FindOrDie(mirror_lp_variable_, variable)
637 void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
638 const int num_vars = integer_variables_.size();
639 for (
int i = 0; i < num_vars; i++) {
640 const IntegerVariable cp_var = integer_variables_[i];
648 bool LinearProgrammingConstraint::SolveLp() {
650 lp_at_level_zero_is_final_ =
false;
653 const auto status = simplex_.
Solve(lp_data_, time_limit_);
656 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
660 average_degeneracy_.
AddData(CalculateDegeneracy());
662 VLOG(2) <<
"High average degeneracy: "
667 if (status_as_int >= num_solves_by_status_.size()) {
668 num_solves_by_status_.resize(status_as_int + 1);
670 num_solves_by_status_[status_as_int]++;
677 lp_solution_is_set_ =
true;
679 const int num_vars = integer_variables_.size();
680 for (
int i = 0; i < num_vars; i++) {
682 GetVariableValueAtCpScale(glop::ColIndex(i));
683 lp_solution_[i] =
value;
684 expanded_lp_solution_[integer_variables_[i]] =
value;
688 if (lp_solution_level_ == 0) {
689 level_zero_lp_solution_ = lp_solution_;
695 bool LinearProgrammingConstraint::AddCutFromConstraints(
696 const std::string&
name,
697 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
708 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
710 VLOG(1) <<
"Issue, overflow!";
729 if (std::abs(activity -
ToDouble(cut_.
ub)) / norm > 1e-4) {
730 VLOG(1) <<
"Cut not tight " << activity <<
" <= " <<
ToDouble(cut_.
ub);
740 const IntegerVariable first_new_var(expanded_lp_solution_.
size());
741 CHECK_EQ(first_new_var.value() % 2, 0);
743 LinearConstraint copy_in_debug;
745 copy_in_debug = cut_;
755 std::vector<ImpliedBoundsProcessor::SlackInfo> ib_slack_infos;
757 false, first_new_var,
758 expanded_lp_solution_, &cut_, &ib_slack_infos);
760 cut_, ib_slack_infos));
768 tmp_lp_values_.clear();
769 tmp_var_lbs_.clear();
770 tmp_var_ubs_.clear();
771 for (
const IntegerVariable
var : cut_.
vars) {
772 if (
var >= first_new_var) {
775 ib_slack_infos[(
var.value() - first_new_var.value()) / 2];
776 tmp_lp_values_.push_back(info.lp_value);
777 tmp_var_lbs_.push_back(info.lb);
778 tmp_var_ubs_.push_back(info.ub);
780 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
788 const IntegerVariable first_slack(first_new_var +
789 IntegerVariable(2 * ib_slack_infos.size()));
790 tmp_slack_rows_.clear();
791 tmp_slack_bounds_.clear();
792 for (
const auto pair : integer_multipliers) {
793 const RowIndex
row = pair.first;
794 const IntegerValue coeff = pair.second;
798 tmp_lp_values_.push_back(0.0);
799 cut_.
vars.push_back(first_slack +
800 2 * IntegerVariable(tmp_slack_rows_.size()));
801 tmp_slack_rows_.push_back(
row);
802 cut_.
coeffs.push_back(coeff);
804 const IntegerValue diff(
805 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value()));
807 tmp_slack_bounds_.push_back(integer_lp_[
row].ub);
808 tmp_var_lbs_.push_back(IntegerValue(0));
809 tmp_var_ubs_.push_back(diff);
811 tmp_slack_bounds_.push_back(integer_lp_[
row].lb);
812 tmp_var_lbs_.push_back(-diff);
813 tmp_var_ubs_.push_back(IntegerValue(0));
817 bool at_least_one_added =
false;
823 at_least_one_added |= PostprocessAndAddCut(
824 absl::StrCat(
name,
"_K"), cover_cut_helper_.
Info(), first_new_var,
825 first_slack, ib_slack_infos, cover_cut_helper_.
mutable_cut());
831 RoundingOptions options;
832 options.max_scaling = sat_parameters_.max_integer_rounding_scaling();
833 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
834 tmp_var_lbs_, tmp_var_ubs_,
835 &implied_bounds_processor_, &cut_);
836 at_least_one_added |= PostprocessAndAddCut(
838 absl::StrCat(
"num_lifted_booleans=",
840 first_new_var, first_slack, ib_slack_infos, &cut_);
842 return at_least_one_added;
845 bool LinearProgrammingConstraint::PostprocessAndAddCut(
846 const std::string&
name,
const std::string& info,
847 IntegerVariable first_new_var, IntegerVariable first_slack,
848 const std::vector<ImpliedBoundsProcessor::SlackInfo>& ib_slack_infos,
849 LinearConstraint* cut) {
853 double activity = 0.0;
854 for (
int i = 0; i < cut->vars.size(); ++i) {
855 if (cut->vars[i] < first_new_var) {
857 ToDouble(cut->coeffs[i]) * expanded_lp_solution_[cut->vars[i]];
860 const double kMinViolation = 1e-4;
861 const double violation = activity -
ToDouble(cut->ub);
862 if (violation < kMinViolation) {
863 VLOG(3) <<
"Bad cut " << activity <<
" <= " <<
ToDouble(cut->ub);
871 IntegerValue cut_ub = cut->ub;
872 bool overflow =
false;
873 for (
int i = 0; i < cut->vars.size(); ++i) {
874 const IntegerVariable
var = cut->vars[i];
877 if (
var < first_new_var) {
878 const glop::ColIndex
col =
881 tmp_scattered_vector_.
Add(
col, cut->coeffs[i]);
883 tmp_scattered_vector_.
Add(
col, -cut->coeffs[i]);
889 if (
var < first_slack) {
890 const IntegerValue multiplier = cut->coeffs[i];
891 const int index = (
var.value() - first_new_var.value()) / 2;
894 std::vector<std::pair<ColIndex, IntegerValue>> terms;
895 for (
const std::pair<IntegerVariable, IntegerValue>& term :
896 ib_slack_infos[
index].terms) {
916 const int slack_index = (
var.value() - first_slack.value()) / 2;
917 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
918 const IntegerValue multiplier = -cut->coeffs[i];
920 multiplier, integer_lp_[
row].terms)) {
926 if (!
AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
933 VLOG(1) <<
"Overflow in slack removal.";
937 VLOG(3) <<
" num_slack: " << num_slack;
943 const std::string extra_info =
944 absl::StrCat(info,
" num_ib_substitutions=", ib_slack_infos.size());
946 const double new_violation =
948 if (std::abs(violation - new_violation) >= 1e-4) {
949 VLOG(1) <<
"Violation discrepancy after slack removal. "
950 <<
" before = " << violation <<
" after = " << new_violation;
954 return constraint_manager_.
AddCut(*cut,
name, expanded_lp_solution_,
958 void LinearProgrammingConstraint::AddCGCuts() {
960 for (RowIndex
row(0);
row < num_rows; ++
row) {
962 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
967 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
971 if (basis_col >= integer_variables_.size())
continue;
977 double magnitude = 0.0;
978 int num_non_zeros = 0;
979 for (RowIndex
row(0);
row < num_rows; ++
row) {
981 if (std::abs(lp_multipliers[
row]) < 1e-12) {
982 lp_multipliers[
row] = 0.0;
990 VLOG(1) <<
"BASIC row not expected! " << lp_multipliers[
row];
991 lp_multipliers[
row] = 0.0;
994 magnitude =
std::max(magnitude, std::abs(lp_multipliers[
row]));
995 if (lp_multipliers[
row] != 0.0) ++num_non_zeros;
997 if (num_non_zeros == 0)
continue;
1000 for (
int i = 0; i < 2; ++i) {
1006 for (RowIndex
row(0);
row < num_rows; ++
row) {
1007 lp_multipliers[
row] = -lp_multipliers[
row];
1013 const std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
1014 ScaleLpMultiplier(
false,
1015 lp_multipliers, &scaling, 52);
1016 AddCutFromConstraints(
"CG", integer_multipliers);
1024 void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
1025 ModelRandomGenerator* random,
1026 std::vector<std::pair<RowIndex, RowIndex>>* output) {
1027 if (
a.empty() ||
b.empty())
return;
1028 for (
const RowIndex
row :
a) {
1029 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
1031 output->push_back({
row, other});
1036 template <
class ListOfTerms>
1037 IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1038 for (
const auto& term : terms) {
1039 if (term.first ==
col)
return term.second;
1041 return IntegerValue(0);
1046 void LinearProgrammingConstraint::AddMirCuts() {
1063 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1068 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1070 for (RowIndex
row(0);
row < num_rows; ++
row) {
1077 base_rows.push_back({
row, IntegerValue(1)});
1081 base_rows.push_back({
row, IntegerValue(-1)});
1102 std::vector<double> weights;
1104 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1105 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1115 integer_multipliers = {entry};
1116 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1121 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1122 dense_cut[
col] = IntegerValue(0);
1124 non_zeros_.SparseClearAll();
1127 const IntegerValue multiplier = entry.second;
1128 for (
const std::pair<ColIndex, IntegerValue> term :
1129 integer_lp_[entry.first].terms) {
1130 const ColIndex
col = term.first;
1131 const IntegerValue coeff = term.second;
1132 non_zeros_.Set(
col);
1133 dense_cut[
col] += coeff * multiplier;
1136 used_rows.
assign(num_rows.value(),
false);
1137 used_rows[entry.first] =
true;
1142 const int kMaxAggregation = 5;
1143 for (
int i = 0; i < kMaxAggregation; ++i) {
1146 IntegerValue max_magnitude(0);
1148 std::vector<ColIndex> col_candidates;
1149 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1150 if (dense_cut[
col] == 0)
continue;
1153 const int col_degree =
1155 if (col_degree <= 1)
continue;
1160 const IntegerVariable
var = integer_variables_[
col.value()];
1161 const double lp_value = expanded_lp_solution_[
var];
1164 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1165 if (bound_distance > 1e-2) {
1166 weights.push_back(bound_distance);
1167 col_candidates.push_back(
col);
1170 if (col_candidates.empty())
break;
1172 const ColIndex var_to_eliminate =
1173 col_candidates[std::discrete_distribution<>(weights.begin(),
1174 weights.end())(*random_)];
1177 std::vector<RowIndex> possible_rows;
1180 const RowIndex
row = entry.row();
1188 if (used_rows[
row])
continue;
1189 used_rows[
row] =
true;
1197 bool add_row =
false;
1200 if (entry.coefficient() > 0.0) {
1201 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1203 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1208 if (entry.coefficient() > 0.0) {
1209 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1211 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1215 possible_rows.push_back(
row);
1216 weights.push_back(row_weights[
row]);
1219 if (possible_rows.empty())
break;
1221 const RowIndex row_to_combine =
1222 possible_rows[std::discrete_distribution<>(weights.begin(),
1223 weights.end())(*random_)];
1224 const IntegerValue to_combine_coeff =
1225 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1228 IntegerValue mult1 = -to_combine_coeff;
1229 IntegerValue mult2 = dense_cut[var_to_eliminate];
1236 const IntegerValue gcd = IntegerValue(
1246 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1247 max_magnitude =
std::max(max_magnitude, entry.second);
1249 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1251 std::abs(mult2.value()))) ==
kint64max) {
1255 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1256 entry.second *= mult1;
1258 integer_multipliers.push_back({row_to_combine, mult2});
1261 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1262 integer_multipliers)) {
1268 if (i + 1 == kMaxAggregation)
break;
1270 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1271 dense_cut[
col] *= mult1;
1273 for (
const std::pair<ColIndex, IntegerValue> term :
1274 integer_lp_[row_to_combine].terms) {
1275 const ColIndex
col = term.first;
1276 const IntegerValue coeff = term.second;
1277 non_zeros_.Set(
col);
1278 dense_cut[
col] += coeff * mult2;
1284 void LinearProgrammingConstraint::AddZeroHalfCuts() {
1287 tmp_lp_values_.clear();
1288 tmp_var_lbs_.clear();
1289 tmp_var_ubs_.clear();
1290 for (
const IntegerVariable
var : integer_variables_) {
1291 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1299 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1307 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1309 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1317 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1321 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1322 const int64 min_iter,
const int64 max_iter) {
1323 if (sat_parameters_.linearization_level() < 2)
return;
1324 const int64 num_degenerate_columns = CalculateDegeneracy();
1326 if (num_cols <= 0) {
1330 const int64 decrease_factor = (10 * num_degenerate_columns) / num_cols;
1335 if (is_degenerate_) {
1338 next_simplex_iter_ *= 2;
1341 if (is_degenerate_) {
1342 next_simplex_iter_ /=
std::max(
int64{1}, 2 * decrease_factor);
1346 next_simplex_iter_ = num_cols / 40;
1349 next_simplex_iter_ =
1354 UpdateBoundsOfLpVariables();
1359 if ( (
false) && objective_is_defined_) {
1368 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1369 100.0 * kCpEpsilon));
1378 parameters.set_max_number_of_iterations(2000);
1380 parameters.set_max_number_of_iterations(next_simplex_iter_);
1382 if (sat_parameters_.use_exact_lp_reason()) {
1383 parameters.set_change_status_to_imprecise(
false);
1384 parameters.set_primal_feasibility_tolerance(1e-7);
1385 parameters.set_dual_feasibility_tolerance(1e-7);
1390 if (!SolveLp())
return true;
1393 const int max_cuts_rounds =
1395 ? sat_parameters_.max_cut_rounds_at_level_zero()
1399 cuts_round < max_cuts_rounds) {
1403 if (!integer_lp_.empty()) {
1406 expanded_lp_solution_);
1413 if (sat_parameters_.add_mir_cuts()) AddMirCuts();
1414 if (sat_parameters_.add_cg_cuts()) AddCGCuts();
1415 if (sat_parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1419 if (!cut_generators_.empty() &&
1421 !sat_parameters_.only_add_cuts_at_level_zero())) {
1422 for (
const CutGenerator& generator : cut_generators_) {
1423 generator.generate_cuts(expanded_lp_solution_, &constraint_manager_);
1428 expanded_lp_solution_, &constraint_manager_);
1432 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1434 if (!CreateLpFromConstraintManager()) {
1438 if (!SolveLp())
return true;
1440 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1447 lp_at_level_zero_is_final_ =
true;
1455 if (sat_parameters_.use_exact_lp_reason()) {
1456 if (!FillExactDualRayReason())
return true;
1465 UpdateSimplexIterationLimit(10, 1000);
1468 if (objective_is_defined_ &&
1475 const IntegerValue approximate_new_lb(
1476 static_cast<int64>(std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1480 if (sat_parameters_.use_exact_lp_reason()) {
1481 if (!ExactLpReasonning())
return false;
1484 const IntegerValue propagated_lb =
1486 if (approximate_new_lb > propagated_lb) {
1487 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1489 <<
" ] approx_lb += "
1490 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1491 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1494 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1495 const double objective_cp_ub =
1497 ReducedCostStrengtheningDeductions(objective_cp_ub -
1498 relaxed_optimal_objective);
1499 if (!deductions_.empty()) {
1500 deductions_reason_ = integer_reason_;
1501 deductions_reason_.push_back(
1506 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1509 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1515 if (!deductions_.empty()) {
1516 const int trail_index_with_same_reason = integer_trail_->
Index();
1518 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1519 trail_index_with_same_reason)) {
1529 CHECK(lp_solution_is_set_);
1532 lp_solution_is_integer_ =
true;
1533 const int num_vars = integer_variables_.size();
1534 for (
int i = 0; i < num_vars; i++) {
1537 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1539 lp_solution_is_integer_ =
false;
1543 if (compute_reduced_cost_averages_) {
1544 UpdateAverageReducedCosts();
1548 if (sat_parameters_.use_branching_in_lp() && objective_is_defined_ &&
1550 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1551 sat_parameters_.linearization_level() >= 2 &&
1552 compute_reduced_cost_averages_ &&
1554 count_since_last_branching_++;
1555 if (count_since_last_branching_ < branching_frequency_) {
1558 count_since_last_branching_ = 0;
1559 bool branching_successful =
false;
1562 const int max_num_branches = 3;
1563 const int num_vars = integer_variables_.size();
1564 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1565 for (
int i = 0; i < num_vars; ++i) {
1566 const IntegerVariable
var = integer_variables_[i];
1571 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1585 const double cost_i = rc_scores_[i];
1586 std::pair<double, IntegerVariable> branching_var =
1587 std::make_pair(-cost_i, positive_var);
1588 auto iterator = std::lower_bound(branching_vars.begin(),
1589 branching_vars.end(), branching_var);
1591 branching_vars.insert(iterator, branching_var);
1592 if (branching_vars.size() > max_num_branches) {
1593 branching_vars.resize(max_num_branches);
1597 for (
const std::pair<double, IntegerVariable>& branching_var :
1599 const IntegerVariable positive_var = branching_var.second;
1600 VLOG(2) <<
"Branching on: " << positive_var;
1601 if (BranchOnVar(positive_var)) {
1602 VLOG(2) <<
"Branching successful.";
1603 branching_successful =
true;
1608 if (!branching_successful) {
1609 branching_frequency_ *= 2;
1618 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1620 IntegerValue lower_bound(0);
1621 const int size = terms.
vars.size();
1622 for (
int i = 0; i < size; ++i) {
1623 const IntegerVariable
var = terms.
vars[i];
1624 const IntegerValue coeff = terms.
coeffs[i];
1633 bool LinearProgrammingConstraint::PossibleOverflow(
1634 const LinearConstraint& constraint) {
1635 IntegerValue lower_bound(0);
1636 const int size = constraint.vars.size();
1637 for (
int i = 0; i < size; ++i) {
1638 const IntegerVariable
var = constraint.vars[i];
1639 const IntegerValue coeff = constraint.coeffs[i];
1641 const IntegerValue
bound = coeff > 0
1648 const int64 slack =
CapAdd(lower_bound.value(), -constraint.ub.value());
1657 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1658 absl::int128 div128(positive_div.value());
1659 absl::int128 result = x / div128;
1660 if (result * div128 > x)
return result - 1;
1666 void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1672 const int size = constraint->vars.size();
1673 for (
int i = 0; i < size; ++i) {
1674 const IntegerVariable
var = constraint->vars[i];
1675 const double coeff =
ToDouble(constraint->coeffs[i]);
1676 const double prod1 =
1678 const double prod2 =
1683 const double max_value =
std::max({sum_max, -sum_min, sum_max - sum_min});
1685 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1686 if (divisor <= 1)
return;
1701 absl::int128 adjust = 0;
1702 for (
int i = 0; i < size; ++i) {
1703 const IntegerValue old_coeff = constraint->coeffs[i];
1704 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1707 const absl::int128 remainder =
1708 absl::int128(old_coeff.value()) -
1709 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1715 if (new_coeff == 0)
continue;
1716 constraint->vars[new_size] = constraint->vars[i];
1717 constraint->coeffs[new_size] = new_coeff;
1720 constraint->vars.resize(new_size);
1721 constraint->coeffs.resize(new_size);
1723 constraint->ub = IntegerValue(
static_cast<int64>(
1724 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1729 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1730 const LinearConstraint& terms, IntegerValue slack) {
1731 integer_reason_.clear();
1732 std::vector<IntegerValue> magnitudes;
1733 const int size = terms.vars.size();
1734 for (
int i = 0; i < size; ++i) {
1735 const IntegerVariable
var = terms.vars[i];
1736 const IntegerValue coeff = terms.coeffs[i];
1739 magnitudes.push_back(coeff);
1742 magnitudes.push_back(-coeff);
1754 std::vector<std::pair<RowIndex, IntegerValue>>
1755 LinearProgrammingConstraint::ScaleLpMultiplier(
1756 bool take_objective_into_account,
1758 int max_pow)
const {
1759 double max_sum = 0.0;
1760 std::vector<std::pair<RowIndex, Fractional>> cp_multipliers;
1761 for (RowIndex
row(0);
row < dense_lp_multipliers.size(); ++
row) {
1766 if (std::abs(lp_multi) < 1e-12)
continue;
1782 cp_multipliers.push_back({
row, cp_multi});
1783 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1788 if (take_objective_into_account) {
1789 max_sum +=
ToDouble(objective_infinity_norm_);
1793 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1794 if (max_sum == 0.0) {
1796 return integer_multipliers;
1801 const double threshold = std::ldexp(1, max_pow) / max_sum;
1802 if (threshold < 1.0) {
1805 return integer_multipliers;
1807 while (2 * *scaling <= threshold) *scaling *= 2;
1812 for (
const auto entry : cp_multipliers) {
1813 const IntegerValue coeff(std::round(entry.second * (*scaling)));
1814 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1816 return integer_multipliers;
1819 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1820 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1821 ScatteredIntegerVector* scattered_vector, IntegerValue* upper_bound)
const {
1824 scattered_vector->ClearAndResize(integer_variables_.size());
1828 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1829 const RowIndex
row = term.first;
1830 const IntegerValue multiplier = term.second;
1834 if (!scattered_vector->AddLinearExpressionMultiple(
1835 multiplier, integer_lp_[
row].terms)) {
1840 const IntegerValue
bound =
1841 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1849 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1850 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1851 ScatteredIntegerVector* scattered_vector, IntegerValue* upper_bound)
const {
1852 const IntegerValue kMaxWantedCoeff(1e18);
1853 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1854 const RowIndex
row = term.first;
1855 const IntegerValue multiplier = term.second;
1856 if (multiplier == 0)
continue;
1860 IntegerValue negative_limit = kMaxWantedCoeff;
1861 IntegerValue positive_limit = kMaxWantedCoeff;
1865 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1866 if (multiplier > 0) {
1867 negative_limit =
std::min(negative_limit, multiplier);
1869 positive_limit =
std::min(positive_limit, -multiplier);
1874 const IntegerValue row_bound =
1875 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1876 if (row_bound != 0) {
1880 const IntegerValue limit2 =
1882 if (*upper_bound > 0 == row_bound > 0) {
1883 positive_limit =
std::min(positive_limit, limit1);
1884 negative_limit =
std::min(negative_limit, limit2);
1886 negative_limit =
std::min(negative_limit, limit1);
1887 positive_limit =
std::min(positive_limit, limit2);
1899 double positive_diff =
ToDouble(row_bound);
1900 double negative_diff =
ToDouble(row_bound);
1905 for (
const auto entry : integer_lp_[
row].terms) {
1906 const ColIndex
col = entry.first;
1907 const IntegerValue coeff = entry.second;
1908 const IntegerValue abs_coef =
IntTypeAbs(coeff);
1911 const IntegerVariable
var = integer_variables_[
col.value()];
1918 const IntegerValue current = (*scattered_vector)[
col];
1920 const IntegerValue overflow_limit(
1922 positive_limit =
std::min(positive_limit, overflow_limit);
1923 negative_limit =
std::min(negative_limit, overflow_limit);
1940 const IntegerValue current_magnitude =
IntTypeAbs(current);
1941 const IntegerValue other_direction_limit =
FloorRatio(
1943 ? kMaxWantedCoeff +
std::min(current_magnitude,
1945 : current_magnitude,
1947 const IntegerValue same_direction_limit(
FloorRatio(
1948 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
1950 if (current > 0 == coeff > 0) {
1951 negative_limit =
std::min(negative_limit, other_direction_limit);
1952 positive_limit =
std::min(positive_limit, same_direction_limit);
1954 negative_limit =
std::min(negative_limit, same_direction_limit);
1955 positive_limit =
std::min(positive_limit, other_direction_limit);
1959 const IntegerValue implied = current > 0 ? lb : ub;
1970 IntegerValue to_add(0);
1971 if (positive_diff <= -1.0 && positive_limit > 0) {
1972 to_add = positive_limit;
1974 if (negative_diff >= 1.0 && negative_limit > 0) {
1977 std::abs(
ToDouble(negative_limit) * negative_diff) >
1978 std::abs(
ToDouble(positive_limit) * positive_diff)) {
1979 to_add = -negative_limit;
1983 term.second += to_add;
1984 *upper_bound += to_add * row_bound;
1988 CHECK(scattered_vector->AddLinearExpressionMultiple(
1989 to_add, integer_lp_[
row].terms));
2008 bool LinearProgrammingConstraint::ExactLpReasonning() {
2010 integer_reason_.clear();
2011 deductions_.clear();
2012 deductions_reason_.clear();
2019 for (RowIndex
row(0);
row < num_rows; ++
row) {
2024 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2025 ScaleLpMultiplier(
true, lp_multipliers,
2029 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2031 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2037 const IntegerValue obj_scale(std::round(scaling));
2038 if (obj_scale == 0) {
2039 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2043 integer_objective_));
2045 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2050 LinearConstraint new_constraint;
2053 new_constraint.vars.push_back(objective_cp_);
2054 new_constraint.coeffs.push_back(-obj_scale);
2056 PreventOverflow(&new_constraint);
2057 DCHECK(!PossibleOverflow(new_constraint));
2060 IntegerSumLE* cp_constraint =
2061 new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
2062 new_constraint.ub, model_);
2066 optimal_constraints_.clear();
2068 optimal_constraints_.emplace_back(cp_constraint);
2069 rev_optimal_constraints_size_ = optimal_constraints_.size();
2070 if (!cp_constraint->PropagateAtLevelZero())
return false;
2071 return cp_constraint->Propagate();
2074 bool LinearProgrammingConstraint::FillExactDualRayReason() {
2076 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2077 ScaleLpMultiplier(
false,
2080 IntegerValue new_constraint_ub;
2081 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2082 &new_constraint_ub)) {
2083 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2087 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2088 &new_constraint_ub);
2090 LinearConstraint new_constraint;
2092 integer_variables_, new_constraint_ub, &new_constraint);
2094 PreventOverflow(&new_constraint);
2095 DCHECK(!PossibleOverflow(new_constraint));
2098 const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
2099 if (implied_lb <= new_constraint.ub) {
2100 VLOG(1) <<
"LP exact dual ray not infeasible,"
2101 <<
" implied_lb: " << implied_lb.value() / scaling
2102 <<
" ub: " << new_constraint.ub.value() / scaling;
2105 const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
2106 SetImpliedLowerBoundReason(new_constraint, slack);
2110 int64 LinearProgrammingConstraint::CalculateDegeneracy() {
2112 int num_non_basic_with_zero_rc = 0;
2113 for (glop::ColIndex i(0); i < num_vars; ++i) {
2115 if (rc != 0.0)
continue;
2119 num_non_basic_with_zero_rc++;
2122 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2123 return num_non_basic_with_zero_rc;
2126 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2127 double cp_objective_delta) {
2128 deductions_.clear();
2133 const double lp_objective_delta =
2135 const int num_vars = integer_variables_.size();
2136 for (
int i = 0; i < num_vars; i++) {
2137 const IntegerVariable cp_var = integer_variables_[i];
2138 const glop::ColIndex lp_var = glop::ColIndex(i);
2142 if (rc == 0.0)
continue;
2143 const double lp_other_bound =
value + lp_objective_delta / rc;
2144 const double cp_other_bound =
2147 if (rc > kLpEpsilon) {
2149 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2154 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2157 }
else if (rc < -kLpEpsilon) {
2159 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2161 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2162 deductions_.push_back(
2176 void AddOutgoingCut(
int num_nodes,
int subset_size,
2177 const std::vector<bool>& in_subset,
2178 const std::vector<int>& tails,
2179 const std::vector<int>& heads,
2180 const std::vector<Literal>& literals,
2181 const std::vector<double>& literal_lp_values,
2182 int64 rhs_lower_bound,
2184 LinearConstraintManager* manager, Model*
model) {
2191 int num_optional_nodes_in = 0;
2192 int num_optional_nodes_out = 0;
2193 int optional_loop_in = -1;
2194 int optional_loop_out = -1;
2195 for (
int i = 0; i < tails.size(); ++i) {
2196 if (tails[i] != heads[i])
continue;
2197 if (in_subset[tails[i]]) {
2198 num_optional_nodes_in++;
2199 if (optional_loop_in == -1 ||
2200 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2201 optional_loop_in = i;
2204 num_optional_nodes_out++;
2205 if (optional_loop_out == -1 ||
2206 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2207 optional_loop_out = i;
2214 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2216 rhs_lower_bound = 1;
2219 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2221 double sum_outgoing = 0.0;
2224 for (
int i = 0; i < tails.size(); ++i) {
2225 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2226 sum_outgoing += literal_lp_values[i];
2227 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2232 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2234 if (num_optional_nodes_in == subset_size &&
2235 (optional_loop_in == -1 ||
2236 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2239 if (num_optional_nodes_out == num_nodes - subset_size &&
2240 (optional_loop_out == -1 ||
2241 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2246 if (num_optional_nodes_in == subset_size) {
2248 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2249 sum_outgoing += literal_lp_values[optional_loop_in];
2253 if (num_optional_nodes_out == num_nodes - subset_size) {
2254 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2256 sum_outgoing += literal_lp_values[optional_loop_out];
2260 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2261 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2274 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2275 const std::vector<Literal>& literals,
2279 if (num_nodes <= 2)
return;
2288 std::vector<Arc> relevant_arcs;
2291 std::vector<double> literal_lp_values(literals.size());
2292 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2294 for (
int i = 0; i < literals.size(); ++i) {
2296 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2298 lp_value = lp_values[direct_view];
2301 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2303 literal_lp_values[i] = lp_value;
2305 if (lp_value < 1e-6)
continue;
2306 relevant_arcs.push_back({tails[i], heads[i], lp_value});
2307 arc_by_decreasing_lp_values.push_back({lp_value, i});
2309 std::sort(arc_by_decreasing_lp_values.begin(),
2310 arc_by_decreasing_lp_values.end(),
2311 std::greater<std::pair<double, int>>());
2321 int num_components = num_nodes;
2322 std::vector<int> parent(num_nodes);
2323 std::vector<int> root(num_nodes);
2324 for (
int i = 0; i < num_nodes; ++i) {
2328 auto get_root_and_compress_path = [&root](
int node) {
2330 while (root[r] != r) r = root[r];
2331 while (root[node] != r) {
2332 const int next = root[node];
2338 for (
const auto pair : arc_by_decreasing_lp_values) {
2339 if (num_components == 2)
break;
2340 const int tail = get_root_and_compress_path(tails[pair.second]);
2341 const int head = get_root_and_compress_path(heads[pair.second]);
2345 const int new_node = parent.size();
2346 parent.push_back(new_node);
2347 parent[
head] = new_node;
2348 parent[
tail] = new_node;
2352 root.push_back(new_node);
2353 root[
head] = new_node;
2354 root[
tail] = new_node;
2365 std::vector<int> pre_order(num_nodes);
2366 std::vector<absl::Span<const int>> subsets;
2368 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2369 for (
int i = 0; i < parent.size(); ++i) {
2370 if (parent[i] != i) graph[parent[i]].push_back(i);
2372 std::vector<int> queue;
2373 std::vector<bool> seen(graph.size(),
false);
2374 std::vector<int> start_index(parent.size());
2375 for (
int i = num_nodes; i < parent.size(); ++i) {
2379 CHECK(graph[i].empty() || graph[i].size() == 2);
2380 if (parent[i] != i)
continue;
2385 while (!queue.empty()) {
2386 const int node = queue.back();
2391 const int start = start_index[node];
2392 if (new_size - start > 1) {
2393 subsets.emplace_back(&pre_order[start], new_size - start);
2398 start_index[node] = new_size;
2399 if (node < num_nodes) pre_order[new_size++] = node;
2400 for (
const int child : graph[node]) {
2401 if (!seen[child]) queue.push_back(child);
2409 int64 total_demands = 0;
2410 if (!demands.empty()) {
2415 CHECK_EQ(pre_order.size(), num_nodes);
2416 std::vector<bool> in_subset(num_nodes,
false);
2417 for (
const absl::Span<const int> subset : subsets) {
2419 CHECK_LT(subset.size(), num_nodes);
2422 bool contain_depot =
false;
2423 int64 subset_demand = 0;
2426 for (
const int n : subset) {
2427 in_subset[n] =
true;
2428 if (!demands.empty()) {
2429 if (n == 0) contain_depot =
true;
2430 subset_demand += demands[n];
2447 int64 min_outgoing_flow = 1;
2448 if (!demands.empty()) {
2470 double outgoing_flow = 0.0;
2471 for (
const auto arc : relevant_arcs) {
2472 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2473 outgoing_flow += arc.lp_value;
2478 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2479 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2480 literals, literal_lp_values,
2481 min_outgoing_flow, lp_values, manager,
2486 for (
const int n : subset) in_subset[n] =
false;
2493 std::vector<IntegerVariable> GetAssociatedVariables(
2494 const std::vector<Literal>& literals, Model*
model) {
2495 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2496 std::vector<IntegerVariable> result;
2497 for (
const Literal l : literals) {
2498 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2500 result.push_back(direct_view);
2502 result.push_back(encoder->GetLiteralView(l.Negated()));
2515 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2516 const std::vector<Literal>& literals,
Model*
model) {
2518 result.
vars = GetAssociatedVariables(literals,
model);
2520 [num_nodes, tails, heads, literals,
model](
2524 num_nodes, tails, heads, literals, lp_values,
2525 {}, 0, manager,
model);
2531 const std::vector<int>& tails,
2532 const std::vector<int>& heads,
2533 const std::vector<Literal>& literals,
2534 const std::vector<int64>& demands,
2537 result.
vars = GetAssociatedVariables(literals,
model);
2539 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2543 lp_values, demands,
capacity, manager,
2549 std::function<IntegerLiteral()>
2552 std::vector<IntegerVariable> variables;
2553 for (IntegerVariable
var : integer_variables_) {
2556 variables.push_back(
var);
2559 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2562 return [
this, variables]() {
2566 double fractional_distance_best = -1.0;
2567 for (
const IntegerVariable
var : variables) {
2572 if (lb == ub)
continue;
2576 const double fractional_distance =
2578 lp_value - std::floor(lp_value +
kEpsilon));
2579 if (fractional_distance <
kEpsilon)
continue;
2582 if (fractional_distance > fractional_distance_best) {
2583 fractional_var =
var;
2584 fractional_distance_best = fractional_distance;
2598 std::vector<IntegerVariable> variables;
2599 for (IntegerVariable
var : integer_variables_) {
2602 variables.push_back(
var);
2605 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2611 const int num_vars = variables.size();
2612 std::vector<double> cost_to_zero(num_vars, 0.0);
2613 std::vector<int> num_cost_to_zero(num_vars);
2616 return [=]()
mutable {
2621 if (num_calls == 10000) {
2622 for (
int i = 0; i < num_vars; i++) {
2623 cost_to_zero[i] /= 2;
2624 num_cost_to_zero[i] /= 2;
2630 for (
int i = 0; i < num_vars; i++) {
2631 const IntegerVariable
var = variables[i];
2636 if (lb == ub)
continue;
2640 if (std::abs(rc) <
kEpsilon)
continue;
2643 if (
value == 1.0 && rc < 0.0) {
2644 cost_to_zero[i] -= rc;
2645 num_cost_to_zero[i]++;
2650 int selected_index = -1;
2651 double best_cost = 0.0;
2652 for (
int i = 0; i < num_vars; i++) {
2653 const IntegerVariable
var = variables[i];
2658 if (num_cost_to_zero[i] > 0 &&
2659 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2660 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2665 if (selected_index >= 0) {
2673 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2674 const int num_vars = integer_variables_.size();
2675 if (sum_cost_down_.size() < num_vars) {
2676 sum_cost_down_.resize(num_vars, 0.0);
2677 num_cost_down_.resize(num_vars, 0);
2678 sum_cost_up_.resize(num_vars, 0.0);
2679 num_cost_up_.resize(num_vars, 0);
2680 rc_scores_.resize(num_vars, 0.0);
2684 num_calls_since_reduced_cost_averages_reset_++;
2685 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2686 for (
int i = 0; i < num_vars; i++) {
2687 sum_cost_up_[i] /= 2;
2688 num_cost_up_[i] /= 2;
2689 sum_cost_down_[i] /= 2;
2690 num_cost_down_[i] /= 2;
2692 num_calls_since_reduced_cost_averages_reset_ = 0;
2696 for (
int i = 0; i < num_vars; i++) {
2697 const IntegerVariable
var = integer_variables_[i];
2704 const double rc = lp_reduced_cost_[i];
2705 if (std::abs(rc) < kCpEpsilon)
continue;
2708 sum_cost_down_[i] -= rc;
2709 num_cost_down_[i]++;
2711 sum_cost_up_[i] += rc;
2718 rc_rev_int_repository_.
SetLevel(0);
2724 positions_by_decreasing_rc_score_.clear();
2725 for (
int i = 0; i < num_vars; i++) {
2730 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2731 const double a_down =
2732 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2733 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2734 rc_scores_[i] =
std::min(a_up, a_down);
2736 rc_scores_[i] = 0.5 * (a_down + a_up);
2741 if (rc_scores_[i] > 0.0) {
2742 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2745 std::sort(positions_by_decreasing_rc_score_.begin(),
2746 positions_by_decreasing_rc_score_.end());
2750 std::function<IntegerLiteral()>
2752 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2755 IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2757 int selected_index = -1;
2758 const int size = positions_by_decreasing_rc_score_.size();
2759 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2760 for (
int i = rev_rc_start_; i < size; ++i) {
2761 const int index = positions_by_decreasing_rc_score_[i].second;
2762 const IntegerVariable
var = integer_variables_[
index];
2765 selected_index =
index;
2770 if (selected_index == -1)
return IntegerLiteral();
2771 const IntegerVariable
var = integer_variables_[selected_index];
2778 const IntegerValue value_ceil(
2780 if (value_ceil >= ub) {
2787 const IntegerValue value_floor(
2789 if (value_floor <= lb) {
2796 num_cost_up_[selected_index] > 0
2797 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2799 const double a_down =
2800 num_cost_down_[selected_index] > 0
2801 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2803 if (a_down < a_up) {