25#include "absl/container/flat_hash_map.h"
26#include "absl/numeric/int128.h"
53 for (
const glop::ColIndex
col : non_zeros_) {
54 dense_vector_[
col] = IntegerValue(0);
56 dense_vector_.
resize(size, IntegerValue(0));
58 dense_vector_.
assign(size, IntegerValue(0));
60 for (
const glop::ColIndex
col : non_zeros_) {
61 is_zeros_[
col] =
true;
63 is_zeros_.
resize(size,
true);
69 const int64_t add =
CapAdd(
value.value(), dense_vector_[
col].value());
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()]);
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()]);
135std::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];
145 const int size = dense_vector_.
size();
146 for (glop::ColIndex
col(0);
col < size; ++
col) {
147 const IntegerValue
coeff = dense_vector_[
col];
157 : constraint_manager_(
model),
165 implied_bounds_processor_({}, integer_trail_,
168 expanded_lp_solution_(
174 if (parameters_.use_branching_in_lp() ||
176 compute_reduced_cost_averages_ =
true;
180 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
185 DCHECK(!lp_constraint_is_registered_);
186 constraint_manager_.
Add(
ct);
193 for (
const IntegerVariable
var :
ct.vars) {
198glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
199 IntegerVariable positive_variable) {
201 const auto it = mirror_lp_variable_.find(positive_variable);
202 if (it == mirror_lp_variable_.end()) {
203 const glop::ColIndex
col(integer_variables_.size());
205 mirror_lp_variable_[positive_variable] =
col;
206 integer_variables_.push_back(positive_variable);
207 lp_solution_.push_back(std::numeric_limits<double>::infinity());
208 lp_reduced_cost_.push_back(0.0);
209 (*dispatcher_)[positive_variable] =
this;
213 if (
index >= expanded_lp_solution_.
size()) {
222 IntegerValue
coeff) {
223 CHECK(!lp_constraint_is_registered_);
224 objective_is_defined_ =
true;
229 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
230 integer_objective_.push_back({
col,
coeff});
231 objective_infinity_norm_ =
248bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
251 infinity_norms_.
clear();
252 const auto& all_constraints = constraint_manager_.
AllConstraints();
256 integer_lp_.
push_back(LinearConstraintInternal());
257 LinearConstraintInternal& new_ct = integer_lp_.
back();
260 const int size =
ct.vars.size();
261 IntegerValue infinity_norm(0);
263 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
272 for (
int i = 0; i < size; ++i) {
274 IntegerVariable
var =
ct.vars[i];
275 IntegerValue
coeff =
ct.coeffs[i];
281 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var),
coeff});
283 infinity_norms_.
push_back(infinity_norm);
286 std::sort(new_ct.terms.begin(), new_ct.terms.end());
291 for (
int i = 0; i < integer_variables_.size(); ++i) {
298 objective_infinity_norm_ = 0;
299 for (
const auto entry : integer_objective_) {
300 const IntegerVariable
var = integer_variables_[entry.first.value()];
302 integer_objective_offset_ +=
306 objective_infinity_norm_ =
308 integer_objective_[new_size++] = entry;
311 objective_infinity_norm_ =
313 integer_objective_.resize(new_size);
316 for (
const LinearConstraintInternal&
ct : integer_lp_) {
319 for (
const auto& term :
ct.terms) {
331 const int num_vars = integer_variables_.size();
332 for (
int i = 0; i < num_vars; i++) {
333 const IntegerVariable cp_var = integer_variables_[i];
341 glop::GlopParameters params;
343 scaler_.
Scale(params, &lp_data_);
344 UpdateBoundsOfLpVariables();
351 for (
int i = 0; i < num_vars; ++i) {
352 const IntegerVariable cp_var = integer_variables_[i];
355 if (lb != 0 || ub != 1)
continue;
365 <<
" Managed constraints.";
369LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
371 glop::BasisState basis_state = simplex_.
GetState();
373 const glop::Status
status = simplex_.
Solve(lp_data_, time_limit_);
377 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
386 info.new_obj_bound = IntegerValue(
387 static_cast<int64_t
>(std::ceil(info.lp_objective - kCpEpsilon)));
392void LinearProgrammingConstraint::FillReducedCostReasonIn(
394 std::vector<IntegerLiteral>* integer_reason) {
395 integer_reason->clear();
396 const int num_vars = integer_variables_.size();
397 for (
int i = 0; i < num_vars; i++) {
398 const double rc = reduced_costs[glop::ColIndex(i)];
399 if (rc > kLpEpsilon) {
400 integer_reason->push_back(
402 }
else if (rc < -kLpEpsilon) {
403 integer_reason->push_back(
411bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
413 DCHECK(lp_solution_is_set_);
415 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
418 integer_reason_.clear();
420 bool deductions_were_made =
false;
422 UpdateBoundsOfLpVariables();
424 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
428 const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
432 if (current_value < current_lb || current_value > current_ub) {
437 const double new_ub = std::floor(current_value);
440 LPSolveInfo lower_branch_info = SolveLpForBranching();
450 positive_var, IntegerValue(std::ceil(current_value)));
451 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
454 deductions_were_made =
true;
455 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
460 const double new_lb = std::ceil(current_value);
463 LPSolveInfo upper_branch_info = SolveLpForBranching();
467 return deductions_were_made;
474 positive_var, IntegerValue(std::floor(current_value)));
475 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
476 return deductions_were_made;
478 deductions_were_made =
true;
480 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
481 return deductions_were_made;
490 approximate_obj_lb = upper_branch_info.new_obj_bound;
492 approximate_obj_lb = lower_branch_info.new_obj_bound;
494 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
495 upper_branch_info.new_obj_bound);
500 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
503 const IntegerLiteral deduction =
505 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
506 return deductions_were_made;
513 DCHECK(!lp_constraint_is_registered_);
514 lp_constraint_is_registered_ =
true;
519 std::sort(integer_objective_.begin(), integer_objective_.end());
525 if (!CreateLpFromConstraintManager()) {
531 const int watcher_id = watcher->
Register(
this);
532 const int num_vars = integer_variables_.size();
533 for (
int i = 0; i < num_vars; i++) {
536 if (objective_is_defined_) {
549 optimal_constraints_.resize(rev_optimal_constraints_size_);
550 if (lp_solution_is_set_ && level < lp_solution_level_) {
551 lp_solution_is_set_ =
false;
559 if (level == 0 && !level_zero_lp_solution_.empty()) {
560 lp_solution_is_set_ =
true;
561 lp_solution_ = level_zero_lp_solution_;
562 lp_solution_level_ = 0;
563 for (
int i = 0; i < lp_solution_.size(); i++) {
564 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
565 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
572 for (
const IntegerVariable
var : generator.
vars) {
575 cut_generators_.push_back(std::move(generator));
579 const std::vector<int>& watch_indices) {
580 if (!lp_solution_is_set_)
return Propagate();
590 for (
const int index : watch_indices) {
596 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
611 glop::ColIndex
var) {
616 IntegerVariable variable)
const {
617 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
621 IntegerVariable variable)
const {
622 return lp_reduced_cost_[
gtl::FindOrDie(mirror_lp_variable_, variable)
626void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
627 const int num_vars = integer_variables_.size();
628 for (
int i = 0; i < num_vars; i++) {
629 const IntegerVariable cp_var = integer_variables_[i];
637bool LinearProgrammingConstraint::SolveLp() {
639 lp_at_level_zero_is_final_ =
false;
642 const auto status = simplex_.
Solve(lp_data_, time_limit_);
645 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
649 average_degeneracy_.
AddData(CalculateDegeneracy());
651 VLOG(2) <<
"High average degeneracy: "
656 if (status_as_int >= num_solves_by_status_.size()) {
657 num_solves_by_status_.resize(status_as_int + 1);
660 num_solves_by_status_[status_as_int]++;
667 lp_solution_is_set_ =
true;
669 const int num_vars = integer_variables_.size();
670 for (
int i = 0; i < num_vars; i++) {
672 GetVariableValueAtCpScale(glop::ColIndex(i));
673 lp_solution_[i] =
value;
674 expanded_lp_solution_[integer_variables_[i]] =
value;
678 if (lp_solution_level_ == 0) {
679 level_zero_lp_solution_ = lp_solution_;
685bool LinearProgrammingConstraint::AddCutFromConstraints(
686 const std::string&
name,
687 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
698 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
700 VLOG(1) <<
"Issue, overflow!";
719 if (std::abs(activity -
ToDouble(cut_.
ub)) / norm > 1e-4) {
720 VLOG(1) <<
"Cut not tight " << activity <<
" <= " <<
ToDouble(cut_.
ub);
730 const IntegerVariable first_new_var(expanded_lp_solution_.
size());
731 CHECK_EQ(first_new_var.value() % 2, 0);
733 LinearConstraint copy_in_debug;
735 copy_in_debug = cut_;
745 tmp_ib_slack_infos_.clear();
747 false, first_new_var,
748 expanded_lp_solution_, &cut_, &tmp_ib_slack_infos_);
750 cut_, tmp_ib_slack_infos_));
758 tmp_lp_values_.clear();
759 tmp_var_lbs_.clear();
760 tmp_var_ubs_.clear();
761 for (
const IntegerVariable
var : cut_.
vars) {
762 if (
var >= first_new_var) {
765 tmp_ib_slack_infos_[(
var.value() - first_new_var.value()) / 2];
766 tmp_lp_values_.push_back(info.lp_value);
767 tmp_var_lbs_.push_back(info.lb);
768 tmp_var_ubs_.push_back(info.ub);
770 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
778 const IntegerVariable first_slack(
779 first_new_var + IntegerVariable(2 * tmp_ib_slack_infos_.size()));
780 tmp_slack_rows_.clear();
781 tmp_slack_bounds_.clear();
782 for (
const auto pair : integer_multipliers) {
783 const RowIndex
row = pair.first;
784 const IntegerValue
coeff = pair.second;
788 tmp_lp_values_.push_back(0.0);
789 cut_.
vars.push_back(first_slack +
790 2 * IntegerVariable(tmp_slack_rows_.size()));
791 tmp_slack_rows_.push_back(
row);
794 const IntegerValue diff(
795 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value()));
797 tmp_slack_bounds_.push_back(integer_lp_[
row].ub);
798 tmp_var_lbs_.push_back(IntegerValue(0));
799 tmp_var_ubs_.push_back(diff);
801 tmp_slack_bounds_.push_back(integer_lp_[
row].lb);
802 tmp_var_lbs_.push_back(-diff);
803 tmp_var_ubs_.push_back(IntegerValue(0));
807 bool at_least_one_added =
false;
813 at_least_one_added |= PostprocessAndAddCut(
814 absl::StrCat(
name,
"_K"), cover_cut_helper_.
Info(), first_new_var,
815 first_slack, tmp_ib_slack_infos_, cover_cut_helper_.
mutable_cut());
821 RoundingOptions options;
823 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
824 tmp_var_lbs_, tmp_var_ubs_,
825 &implied_bounds_processor_, &cut_);
826 at_least_one_added |= PostprocessAndAddCut(
828 absl::StrCat(
"num_lifted_booleans=",
830 first_new_var, first_slack, tmp_ib_slack_infos_, &cut_);
832 return at_least_one_added;
835bool LinearProgrammingConstraint::PostprocessAndAddCut(
836 const std::string&
name,
const std::string& info,
837 IntegerVariable first_new_var, IntegerVariable first_slack,
838 const std::vector<ImpliedBoundsProcessor::SlackInfo>& ib_slack_infos,
839 LinearConstraint* cut) {
843 double activity = 0.0;
844 for (
int i = 0; i < cut->vars.size(); ++i) {
845 if (cut->vars[i] < first_new_var) {
847 ToDouble(cut->coeffs[i]) * expanded_lp_solution_[cut->vars[i]];
850 const double kMinViolation = 1e-4;
851 const double violation = activity -
ToDouble(cut->ub);
852 if (violation < kMinViolation) {
853 VLOG(3) <<
"Bad cut " << activity <<
" <= " <<
ToDouble(cut->ub);
861 IntegerValue cut_ub = cut->ub;
862 bool overflow =
false;
863 for (
int i = 0; i < cut->vars.size(); ++i) {
864 const IntegerVariable
var = cut->vars[i];
867 if (
var < first_new_var) {
868 const glop::ColIndex
col =
871 tmp_scattered_vector_.
Add(
col, cut->coeffs[i]);
873 tmp_scattered_vector_.
Add(
col, -cut->coeffs[i]);
879 if (
var < first_slack) {
880 const IntegerValue multiplier = cut->coeffs[i];
881 const int index = (
var.value() - first_new_var.value()) / 2;
885 for (
const std::pair<IntegerVariable, IntegerValue>& term :
886 ib_slack_infos[
index].terms) {
887 tmp_terms_.push_back(
906 const int slack_index = (
var.value() - first_slack.value()) / 2;
907 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
908 const IntegerValue multiplier = -cut->coeffs[i];
910 multiplier, integer_lp_[
row].terms)) {
916 if (!
AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
923 VLOG(1) <<
"Overflow in slack removal.";
927 VLOG(3) <<
" num_slack: " << num_slack;
933 const std::string extra_info =
934 absl::StrCat(info,
" num_ib_substitutions=", ib_slack_infos.size());
936 const double new_violation =
938 if (std::abs(violation - new_violation) >= 1e-4) {
939 VLOG(1) <<
"Violation discrepancy after slack removal. "
940 <<
" before = " << violation <<
" after = " << new_violation;
944 return constraint_manager_.
AddCut(*cut,
name, expanded_lp_solution_,
952void LinearProgrammingConstraint::AddCGCuts() {
954 for (RowIndex
row(0);
row < num_rows; ++
row) {
956 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
964 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
968 if (basis_col >= integer_variables_.size())
continue;
973 double magnitude = 0.0;
974 tmp_lp_multipliers_.clear();
976 if (lambda.non_zeros.empty()) {
977 for (RowIndex
row(0);
row < num_rows; ++
row) {
979 if (std::abs(
value) < kZeroTolerance)
continue;
985 VLOG(1) <<
"BASIC row not expected! " <<
value;
990 tmp_lp_multipliers_.push_back({
row,
value});
993 for (
const ColIndex
col : lambda.non_zeros) {
995 const double value = lambda.values[
col];
996 if (std::abs(
value) < kZeroTolerance)
continue;
1000 VLOG(1) <<
"BASIC row not expected! " <<
value;
1005 tmp_lp_multipliers_.push_back({
row,
value});
1008 if (tmp_lp_multipliers_.empty())
continue;
1011 for (
int i = 0; i < 2; ++i) {
1017 for (std::pair<RowIndex, double>& p : tmp_lp_multipliers_) {
1018 p.second = -p.second;
1024 tmp_integer_multipliers_ =
1025 ScaleLpMultiplier(
false,
1026 tmp_lp_multipliers_, &scaling, 52);
1027 AddCutFromConstraints(
"CG", tmp_integer_multipliers_);
1035void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
1036 ModelRandomGenerator* random,
1037 std::vector<std::pair<RowIndex, RowIndex>>* output) {
1038 if (
a.empty() ||
b.empty())
return;
1039 for (
const RowIndex
row :
a) {
1040 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
1042 output->push_back({
row, other});
1047template <
class ListOfTerms>
1048IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1049 for (
const auto& term : terms) {
1050 if (term.first ==
col)
return term.second;
1052 return IntegerValue(0);
1066void LinearProgrammingConstraint::AddObjectiveCut() {
1067 if (integer_objective_.size() <= 1)
return;
1073 const IntegerValue obj_lower_bound =
1075 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1077 tmp_lp_values_.clear();
1078 tmp_var_lbs_.clear();
1079 tmp_var_ubs_.clear();
1082 LinearConstraint objective_ct;
1084 objective_ct.ub = integer_objective_offset_ -
1086 IntegerValue obj_coeff_magnitude(0);
1087 for (
const auto& [
col,
coeff] : integer_objective_) {
1088 const IntegerVariable
var = integer_variables_[
col.value()];
1089 objective_ct.vars.push_back(
var);
1090 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1093 objective_ct.coeffs.push_back(-
coeff);
1100 if (obj_coeff_magnitude < 1e9) {
1101 const bool added = constraint_manager_.
AddCut(objective_ct,
"Objective",
1102 expanded_lp_solution_);
1108 cut_ = objective_ct;
1111 constraint_manager_.
AddCut(cut_,
"Objective_K", expanded_lp_solution_);
1117 cut_ = objective_ct;
1118 RoundingOptions options;
1120 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
1121 tmp_var_lbs_, tmp_var_ubs_,
1122 &implied_bounds_processor_, &cut_);
1125 constraint_manager_.
AddCut(cut_,
"Objective_MIR", expanded_lp_solution_);
1129void LinearProgrammingConstraint::AddMirCuts() {
1145 integer_variables_.size(), IntegerValue(0));
1146 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1151 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1153 for (RowIndex
row(0);
row < num_rows; ++
row) {
1160 base_rows.push_back({
row, IntegerValue(1)});
1164 base_rows.push_back({
row, IntegerValue(-1)});
1185 std::vector<double> weights;
1187 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1188 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1198 integer_multipliers = {entry};
1199 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1204 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1205 dense_cut[
col] = IntegerValue(0);
1207 non_zeros_.SparseClearAll();
1210 const IntegerValue multiplier = entry.second;
1211 for (
const std::pair<ColIndex, IntegerValue> term :
1212 integer_lp_[entry.first].terms) {
1213 const ColIndex
col = term.first;
1214 const IntegerValue
coeff = term.second;
1215 non_zeros_.Set(
col);
1216 dense_cut[
col] +=
coeff * multiplier;
1219 used_rows.
assign(num_rows.value(),
false);
1220 used_rows[entry.first] =
true;
1225 const int kMaxAggregation = 5;
1226 for (
int i = 0; i < kMaxAggregation; ++i) {
1229 IntegerValue max_magnitude(0);
1231 std::vector<ColIndex> col_candidates;
1232 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1233 if (dense_cut[
col] == 0)
continue;
1236 const int col_degree =
1238 if (col_degree <= 1)
continue;
1243 const IntegerVariable
var = integer_variables_[
col.value()];
1244 const double lp_value = expanded_lp_solution_[
var];
1247 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1248 if (bound_distance > 1e-2) {
1249 weights.push_back(bound_distance);
1250 col_candidates.push_back(
col);
1253 if (col_candidates.empty())
break;
1255 const ColIndex var_to_eliminate =
1256 col_candidates[std::discrete_distribution<>(weights.begin(),
1257 weights.end())(*random_)];
1260 std::vector<RowIndex> possible_rows;
1263 const RowIndex
row = entry.row();
1271 if (used_rows[
row])
continue;
1272 used_rows[
row] =
true;
1280 bool add_row =
false;
1283 if (entry.coefficient() > 0.0) {
1284 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1286 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1291 if (entry.coefficient() > 0.0) {
1292 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1294 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1299 weights.push_back(row_weights[
row]);
1302 if (possible_rows.empty())
break;
1304 const RowIndex row_to_combine =
1305 possible_rows[std::discrete_distribution<>(weights.begin(),
1306 weights.end())(*random_)];
1307 const IntegerValue to_combine_coeff =
1308 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1311 IntegerValue mult1 = -to_combine_coeff;
1312 IntegerValue mult2 = dense_cut[var_to_eliminate];
1319 const IntegerValue gcd = IntegerValue(
1329 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1332 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1334 std::abs(mult2.value()))) ==
1339 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1340 entry.second *= mult1;
1342 integer_multipliers.push_back({row_to_combine, mult2});
1345 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1346 integer_multipliers)) {
1352 if (i + 1 == kMaxAggregation)
break;
1354 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1355 dense_cut[
col] *= mult1;
1357 for (
const std::pair<ColIndex, IntegerValue> term :
1358 integer_lp_[row_to_combine].terms) {
1359 const ColIndex
col = term.first;
1360 const IntegerValue
coeff = term.second;
1361 non_zeros_.Set(
col);
1368void LinearProgrammingConstraint::AddZeroHalfCuts() {
1371 tmp_lp_values_.clear();
1372 tmp_var_lbs_.clear();
1373 tmp_var_ubs_.clear();
1374 for (
const IntegerVariable
var : integer_variables_) {
1375 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1383 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1391 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1393 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1401 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1405void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1406 const int64_t min_iter,
const int64_t max_iter) {
1408 const int64_t num_degenerate_columns = CalculateDegeneracy();
1410 if (num_cols <= 0) {
1414 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1419 if (is_degenerate_) {
1420 next_simplex_iter_ /=
std::max(int64_t{1}, decrease_factor);
1422 next_simplex_iter_ *= 2;
1425 if (is_degenerate_) {
1426 next_simplex_iter_ /=
std::max(int64_t{1}, 2 * decrease_factor);
1430 next_simplex_iter_ = num_cols / 40;
1433 next_simplex_iter_ =
1438 UpdateBoundsOfLpVariables();
1443 if ( (
false) && objective_is_defined_) {
1452 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1453 100.0 * kCpEpsilon));
1462 parameters.set_max_number_of_iterations(2000);
1464 parameters.set_max_number_of_iterations(next_simplex_iter_);
1467 parameters.set_change_status_to_imprecise(
false);
1468 parameters.set_primal_feasibility_tolerance(1e-7);
1469 parameters.set_dual_feasibility_tolerance(1e-7);
1474 if (!SolveLp())
return true;
1477 const int max_cuts_rounds =
1485 cuts_round < max_cuts_rounds) {
1490 if (num_solves_ > 1) {
1493 expanded_lp_solution_);
1507 if (!cut_generators_.empty() &&
1510 for (
const CutGenerator& generator : cut_generators_) {
1511 if (!generator.generate_cuts(expanded_lp_solution_,
1512 &constraint_manager_)) {
1519 expanded_lp_solution_, &constraint_manager_);
1523 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1525 if (!CreateLpFromConstraintManager()) {
1529 if (!SolveLp())
return true;
1531 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1538 lp_at_level_zero_is_final_ =
true;
1547 if (!FillExactDualRayReason())
return true;
1556 UpdateSimplexIterationLimit(10, 1000);
1559 if (objective_is_defined_ &&
1565 if (!ExactLpReasonning())
return false;
1570 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1571 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1572 const IntegerValue propagated_lb =
1574 if (approximate_new_lb > propagated_lb) {
1575 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1577 <<
" ] approx_lb += "
1578 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1579 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1586 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1587 const double objective_cp_ub =
1590 ReducedCostStrengtheningDeductions(objective_cp_ub -
1591 relaxed_optimal_objective);
1592 if (!deductions_.empty()) {
1593 deductions_reason_ = integer_reason_;
1594 deductions_reason_.push_back(
1599 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1600 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1601 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1604 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1610 if (!deductions_.empty()) {
1611 const int trail_index_with_same_reason = integer_trail_->
Index();
1613 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1614 trail_index_with_same_reason)) {
1624 CHECK(lp_solution_is_set_);
1627 lp_solution_is_integer_ =
true;
1628 const int num_vars = integer_variables_.size();
1629 for (
int i = 0; i < num_vars; i++) {
1632 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1634 lp_solution_is_integer_ =
false;
1638 if (compute_reduced_cost_averages_) {
1639 UpdateAverageReducedCosts();
1645 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1647 compute_reduced_cost_averages_ &&
1649 count_since_last_branching_++;
1650 if (count_since_last_branching_ < branching_frequency_) {
1653 count_since_last_branching_ = 0;
1654 bool branching_successful =
false;
1657 const int max_num_branches = 3;
1658 const int num_vars = integer_variables_.size();
1659 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1660 for (
int i = 0; i < num_vars; ++i) {
1661 const IntegerVariable
var = integer_variables_[i];
1666 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1680 const double cost_i = rc_scores_[i];
1681 std::pair<double, IntegerVariable> branching_var =
1682 std::make_pair(-cost_i, positive_var);
1684 branching_vars.end(), branching_var);
1686 branching_vars.insert(iterator, branching_var);
1687 if (branching_vars.size() > max_num_branches) {
1688 branching_vars.resize(max_num_branches);
1692 for (
const std::pair<double, IntegerVariable>& branching_var :
1694 const IntegerVariable positive_var = branching_var.second;
1695 VLOG(2) <<
"Branching on: " << positive_var;
1696 if (BranchOnVar(positive_var)) {
1697 VLOG(2) <<
"Branching successful.";
1698 branching_successful =
true;
1703 if (!branching_successful) {
1704 branching_frequency_ *= 2;
1713IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1716 const int size = terms.
vars.size();
1717 for (
int i = 0; i < size; ++i) {
1718 const IntegerVariable
var = terms.
vars[i];
1728bool LinearProgrammingConstraint::PossibleOverflow(
1729 const LinearConstraint& constraint) {
1731 const int size = constraint.vars.size();
1732 for (
int i = 0; i < size; ++i) {
1733 const IntegerVariable
var = constraint.vars[i];
1734 const IntegerValue
coeff = constraint.coeffs[i];
1753absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1754 absl::int128 div128(positive_div.value());
1755 absl::int128 result = x / div128;
1756 if (result * div128 > x)
return result - 1;
1762void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1771 const int size = constraint->vars.size();
1772 for (
int i = 0; i < size; ++i) {
1773 const IntegerVariable
var = constraint->vars[i];
1782 const double max_value =
std::max({sum_max, -sum_min, sum_max - sum_min});
1784 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1785 if (divisor <= 1)
return;
1800 absl::int128 adjust = 0;
1801 for (
int i = 0; i < size; ++i) {
1802 const IntegerValue old_coeff = constraint->coeffs[i];
1803 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1806 const absl::int128 remainder =
1807 absl::int128(old_coeff.value()) -
1808 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1814 if (new_coeff == 0)
continue;
1815 constraint->vars[new_size] = constraint->vars[i];
1816 constraint->coeffs[new_size] = new_coeff;
1819 constraint->vars.resize(new_size);
1820 constraint->coeffs.resize(new_size);
1822 constraint->ub = IntegerValue(
static_cast<int64_t
>(
1823 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1828void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1829 const LinearConstraint& terms, IntegerValue slack) {
1830 integer_reason_.clear();
1831 std::vector<IntegerValue> magnitudes;
1832 const int size = terms.vars.size();
1833 for (
int i = 0; i < size; ++i) {
1834 const IntegerVariable
var = terms.vars[i];
1835 const IntegerValue
coeff = terms.coeffs[i];
1838 magnitudes.push_back(
coeff);
1841 magnitudes.push_back(-
coeff);
1852std::vector<std::pair<RowIndex, IntegerValue>>
1853LinearProgrammingConstraint::ScaleLpMultiplier(
1854 bool take_objective_into_account,
1855 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1857 double max_sum = 0.0;
1858 tmp_cp_multipliers_.clear();
1859 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1860 const RowIndex
row = p.first;
1865 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1881 tmp_cp_multipliers_.push_back({
row, cp_multi});
1882 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1887 if (take_objective_into_account) {
1888 max_sum +=
ToDouble(objective_infinity_norm_);
1892 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1893 if (max_sum == 0.0) {
1895 return integer_multipliers;
1900 const double threshold = std::ldexp(1, max_pow) / max_sum;
1901 if (threshold < 1.0) {
1904 return integer_multipliers;
1906 while (2 * *scaling <= threshold) *scaling *= 2;
1911 for (
const auto entry : tmp_cp_multipliers_) {
1912 const IntegerValue
coeff(std::round(entry.second * (*scaling)));
1913 if (
coeff != 0) integer_multipliers.push_back({entry.first,
coeff});
1915 return integer_multipliers;
1918bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1919 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1920 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1923 scattered_vector->ClearAndResize(integer_variables_.size());
1927 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1928 const RowIndex
row = term.first;
1929 const IntegerValue multiplier = term.second;
1933 if (!scattered_vector->AddLinearExpressionMultiple(
1934 multiplier, integer_lp_[
row].terms)) {
1939 const IntegerValue
bound =
1940 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1948void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1949 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1950 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1951 const IntegerValue kMaxWantedCoeff(1e18);
1952 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1953 const RowIndex
row = term.first;
1954 const IntegerValue multiplier = term.second;
1955 if (multiplier == 0)
continue;
1959 IntegerValue negative_limit = kMaxWantedCoeff;
1960 IntegerValue positive_limit = kMaxWantedCoeff;
1964 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1965 if (multiplier > 0) {
1966 negative_limit =
std::min(negative_limit, multiplier);
1968 positive_limit =
std::min(positive_limit, -multiplier);
1973 const IntegerValue row_bound =
1974 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1975 if (row_bound != 0) {
1979 const IntegerValue limit2 =
1982 positive_limit =
std::min(positive_limit, limit1);
1983 negative_limit =
std::min(negative_limit, limit2);
1985 negative_limit =
std::min(negative_limit, limit1);
1986 positive_limit =
std::min(positive_limit, limit2);
1998 double positive_diff =
ToDouble(row_bound);
1999 double negative_diff =
ToDouble(row_bound);
2004 for (
const auto entry : integer_lp_[
row].terms) {
2005 const ColIndex
col = entry.first;
2006 const IntegerValue
coeff = entry.second;
2010 const IntegerVariable
var = integer_variables_[
col.value()];
2017 const IntegerValue current = (*scattered_vector)[
col];
2019 const IntegerValue overflow_limit(
2021 positive_limit =
std::min(positive_limit, overflow_limit);
2022 negative_limit =
std::min(negative_limit, overflow_limit);
2039 const IntegerValue current_magnitude =
IntTypeAbs(current);
2040 const IntegerValue other_direction_limit =
FloorRatio(
2042 ? kMaxWantedCoeff +
std::min(current_magnitude,
2044 : current_magnitude,
2046 const IntegerValue same_direction_limit(
FloorRatio(
2047 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
2049 if ((current > 0) == (
coeff > 0)) {
2050 negative_limit =
std::min(negative_limit, other_direction_limit);
2051 positive_limit =
std::min(positive_limit, same_direction_limit);
2053 negative_limit =
std::min(negative_limit, same_direction_limit);
2054 positive_limit =
std::min(positive_limit, other_direction_limit);
2058 const IntegerValue implied = current > 0 ? lb : ub;
2069 IntegerValue to_add(0);
2070 if (positive_diff <= -1.0 && positive_limit > 0) {
2071 to_add = positive_limit;
2073 if (negative_diff >= 1.0 && negative_limit > 0) {
2076 std::abs(
ToDouble(negative_limit) * negative_diff) >
2077 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2078 to_add = -negative_limit;
2082 term.second += to_add;
2087 CHECK(scattered_vector->AddLinearExpressionMultiple(
2088 to_add, integer_lp_[
row].terms));
2107bool LinearProgrammingConstraint::ExactLpReasonning() {
2109 integer_reason_.clear();
2110 deductions_.clear();
2111 deductions_reason_.clear();
2117 tmp_lp_multipliers_.clear();
2118 for (RowIndex
row(0);
row < num_rows; ++
row) {
2120 if (std::abs(
value) < kZeroTolerance)
continue;
2121 tmp_lp_multipliers_.push_back({
row,
value});
2125 tmp_integer_multipliers_ = ScaleLpMultiplier(
2126 true, tmp_lp_multipliers_, &scaling);
2129 if (!ComputeNewLinearConstraint(tmp_integer_multipliers_,
2130 &tmp_scattered_vector_, &rc_ub)) {
2131 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2137 const IntegerValue obj_scale(std::round(scaling));
2138 if (obj_scale == 0) {
2139 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2143 integer_objective_));
2145 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2152 tmp_constraint_.
vars.push_back(objective_cp_);
2153 tmp_constraint_.
coeffs.push_back(-obj_scale);
2155 PreventOverflow(&tmp_constraint_);
2156 DCHECK(!PossibleOverflow(tmp_constraint_));
2160 if (tmp_constraint_.
vars.empty()) {
2162 return tmp_constraint_.
ub >= 0;
2165 IntegerSumLE* cp_constraint =
2166 new IntegerSumLE({}, tmp_constraint_.
vars, tmp_constraint_.
coeffs,
2167 tmp_constraint_.
ub, model_);
2171 optimal_constraints_.clear();
2173 optimal_constraints_.emplace_back(cp_constraint);
2174 rev_optimal_constraints_size_ = optimal_constraints_.size();
2175 if (!cp_constraint->PropagateAtLevelZero())
return false;
2176 return cp_constraint->Propagate();
2179bool LinearProgrammingConstraint::FillExactDualRayReason() {
2182 tmp_lp_multipliers_.clear();
2183 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2185 if (std::abs(
value) < kZeroTolerance)
continue;
2186 tmp_lp_multipliers_.push_back({
row,
value});
2188 tmp_integer_multipliers_ = ScaleLpMultiplier(
2189 false, tmp_lp_multipliers_, &scaling);
2191 IntegerValue new_constraint_ub;
2192 if (!ComputeNewLinearConstraint(tmp_integer_multipliers_,
2193 &tmp_scattered_vector_, &new_constraint_ub)) {
2194 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2198 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2199 &new_constraint_ub);
2202 integer_variables_, new_constraint_ub, &tmp_constraint_);
2204 PreventOverflow(&tmp_constraint_);
2205 DCHECK(!PossibleOverflow(tmp_constraint_));
2208 const IntegerValue implied_lb = GetImpliedLowerBound(tmp_constraint_);
2209 if (implied_lb <= tmp_constraint_.
ub) {
2210 VLOG(1) <<
"LP exact dual ray not infeasible,"
2211 <<
" implied_lb: " << implied_lb.value() / scaling
2212 <<
" ub: " << tmp_constraint_.
ub.value() / scaling;
2215 const IntegerValue slack = (implied_lb - tmp_constraint_.
ub) - 1;
2216 SetImpliedLowerBoundReason(tmp_constraint_, slack);
2220int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2222 int num_non_basic_with_zero_rc = 0;
2223 for (glop::ColIndex i(0); i < num_vars; ++i) {
2225 if (rc != 0.0)
continue;
2229 num_non_basic_with_zero_rc++;
2232 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2233 return num_non_basic_with_zero_rc;
2236void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2237 double cp_objective_delta) {
2238 deductions_.clear();
2243 const double lp_objective_delta =
2245 const int num_vars = integer_variables_.size();
2246 for (
int i = 0; i < num_vars; i++) {
2247 const IntegerVariable cp_var = integer_variables_[i];
2248 const glop::ColIndex lp_var = glop::ColIndex(i);
2252 if (rc == 0.0)
continue;
2253 const double lp_other_bound =
value + lp_objective_delta / rc;
2254 const double cp_other_bound =
2257 if (rc > kLpEpsilon) {
2259 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2264 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2267 }
else if (rc < -kLpEpsilon) {
2269 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2271 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2272 deductions_.push_back(
2287 int num_nodes,
int subset_size,
const std::vector<bool>& in_subset,
2288 const std::vector<int>& tails,
const std::vector<int>& heads,
2289 const std::vector<Literal>& literals,
2290 const std::vector<double>& literal_lp_values, int64_t rhs_lower_bound,
2292 LinearConstraintManager* manager, Model*
model) {
2299 int num_optional_nodes_in = 0;
2300 int num_optional_nodes_out = 0;
2301 int optional_loop_in = -1;
2302 int optional_loop_out = -1;
2303 for (
int i = 0; i < tails.size(); ++i) {
2304 if (tails[i] != heads[i])
continue;
2305 if (in_subset[tails[i]]) {
2306 num_optional_nodes_in++;
2307 if (optional_loop_in == -1 ||
2308 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2309 optional_loop_in = i;
2312 num_optional_nodes_out++;
2313 if (optional_loop_out == -1 ||
2314 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2315 optional_loop_out = i;
2322 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2324 rhs_lower_bound = 1;
2327 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2329 double sum_outgoing = 0.0;
2332 for (
int i = 0; i < tails.size(); ++i) {
2333 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2334 sum_outgoing += literal_lp_values[i];
2335 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2340 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2342 if (num_optional_nodes_in == subset_size &&
2343 (optional_loop_in == -1 ||
2344 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2347 if (num_optional_nodes_out == num_nodes - subset_size &&
2348 (optional_loop_out == -1 ||
2349 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2354 if (num_optional_nodes_in == subset_size) {
2356 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2357 sum_outgoing += literal_lp_values[optional_loop_in];
2361 if (num_optional_nodes_out == num_nodes - subset_size) {
2362 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2364 sum_outgoing += literal_lp_values[optional_loop_out];
2368 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2369 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2382 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2383 const std::vector<Literal>& literals,
2385 absl::Span<const int64_t> demands, int64_t
capacity,
2387 if (num_nodes <= 2)
return;
2396 std::vector<Arc> relevant_arcs;
2399 std::vector<double> literal_lp_values(literals.size());
2400 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2402 for (
int i = 0; i < literals.size(); ++i) {
2404 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2406 lp_value = lp_values[direct_view];
2409 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2411 literal_lp_values[i] = lp_value;
2413 if (lp_value < 1e-6)
continue;
2414 relevant_arcs.
push_back({tails[i], heads[i], lp_value});
2415 arc_by_decreasing_lp_values.push_back({lp_value, i});
2417 std::sort(arc_by_decreasing_lp_values.begin(),
2418 arc_by_decreasing_lp_values.end(),
2419 std::greater<std::pair<double, int>>());
2429 int num_components = num_nodes;
2430 std::vector<int> parent(num_nodes);
2431 std::vector<int> root(num_nodes);
2432 for (
int i = 0; i < num_nodes; ++i) {
2436 auto get_root_and_compress_path = [&root](
int node) {
2438 while (root[r] != r) r = root[r];
2439 while (root[node] != r) {
2440 const int next = root[node];
2446 for (
const auto pair : arc_by_decreasing_lp_values) {
2447 if (num_components == 2)
break;
2448 const int tail = get_root_and_compress_path(tails[pair.second]);
2449 const int head = get_root_and_compress_path(heads[pair.second]);
2453 const int new_node = parent.size();
2454 parent.push_back(new_node);
2455 parent[
head] = new_node;
2456 parent[
tail] = new_node;
2460 root.push_back(new_node);
2461 root[
head] = new_node;
2462 root[
tail] = new_node;
2473 std::vector<int> pre_order(num_nodes);
2474 std::vector<absl::Span<const int>> subsets;
2476 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2477 for (
int i = 0; i < parent.size(); ++i) {
2478 if (parent[i] != i) graph[parent[i]].push_back(i);
2480 std::vector<int> queue;
2481 std::vector<bool> seen(graph.size(),
false);
2482 std::vector<int> start_index(parent.size());
2483 for (
int i = num_nodes; i < parent.size(); ++i) {
2487 CHECK(graph[i].empty() || graph[i].size() == 2);
2488 if (parent[i] != i)
continue;
2493 while (!queue.empty()) {
2494 const int node = queue.back();
2499 const int start = start_index[node];
2500 if (new_size -
start > 1) {
2501 subsets.emplace_back(&pre_order[
start], new_size -
start);
2506 start_index[node] = new_size;
2507 if (node < num_nodes) pre_order[new_size++] = node;
2508 for (
const int child : graph[node]) {
2509 if (!seen[child]) queue.push_back(child);
2517 int64_t total_demands = 0;
2518 if (!demands.empty()) {
2519 for (
const int64_t
demand : demands) total_demands +=
demand;
2523 CHECK_EQ(pre_order.size(), num_nodes);
2524 std::vector<bool> in_subset(num_nodes,
false);
2525 for (
const absl::Span<const int> subset : subsets) {
2527 CHECK_LT(subset.size(), num_nodes);
2530 bool contain_depot =
false;
2531 int64_t subset_demand = 0;
2534 for (
const int n : subset) {
2535 in_subset[n] =
true;
2536 if (!demands.empty()) {
2537 if (n == 0) contain_depot =
true;
2538 subset_demand += demands[n];
2555 int64_t min_outgoing_flow = 1;
2556 if (!demands.empty()) {
2566 min_outgoing_flow =
std::max(min_outgoing_flow, int64_t{1});
2578 double outgoing_flow = 0.0;
2579 for (
const auto arc : relevant_arcs) {
2580 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2581 outgoing_flow += arc.lp_value;
2586 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2587 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2588 literals, literal_lp_values,
2589 min_outgoing_flow, lp_values, manager,
2594 for (
const int n : subset) in_subset[n] =
false;
2601std::vector<IntegerVariable> GetAssociatedVariables(
2602 const std::vector<Literal>& literals, Model*
model) {
2603 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2604 std::vector<IntegerVariable> result;
2605 for (
const Literal l : literals) {
2606 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2608 result.push_back(direct_view);
2610 result.push_back(encoder->GetLiteralView(l.Negated()));
2623 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2624 const std::vector<Literal>& literals,
Model*
model) {
2626 result.
vars = GetAssociatedVariables(literals,
model);
2628 [num_nodes, tails, heads, literals,
model](
2632 num_nodes, tails, heads, literals, lp_values,
2633 {}, 0, manager,
model);
2640 const std::vector<int>& tails,
2641 const std::vector<int>& heads,
2642 const std::vector<Literal>& literals,
2643 const std::vector<int64_t>& demands,
2646 result.
vars = GetAssociatedVariables(literals,
model);
2648 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2652 lp_values, demands,
capacity, manager,
2659std::function<IntegerLiteral()>
2662 std::vector<IntegerVariable> variables;
2663 for (IntegerVariable
var : integer_variables_) {
2666 variables.push_back(
var);
2669 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2672 return [
this, variables]() {
2676 double fractional_distance_best = -1.0;
2677 for (
const IntegerVariable
var : variables) {
2682 if (lb == ub)
continue;
2686 const double fractional_distance =
2688 lp_value - std::floor(lp_value +
kEpsilon));
2689 if (fractional_distance <
kEpsilon)
continue;
2692 if (fractional_distance > fractional_distance_best) {
2693 fractional_var =
var;
2694 fractional_distance_best = fractional_distance;
2708 std::vector<IntegerVariable> variables;
2709 for (IntegerVariable
var : integer_variables_) {
2712 variables.push_back(
var);
2715 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2721 const int num_vars = variables.size();
2722 std::vector<double> cost_to_zero(num_vars, 0.0);
2723 std::vector<int> num_cost_to_zero(num_vars);
2726 return [=]()
mutable {
2731 if (num_calls == 10000) {
2732 for (
int i = 0; i < num_vars; i++) {
2733 cost_to_zero[i] /= 2;
2734 num_cost_to_zero[i] /= 2;
2740 for (
int i = 0; i < num_vars; i++) {
2741 const IntegerVariable
var = variables[i];
2746 if (lb == ub)
continue;
2750 if (std::abs(rc) <
kEpsilon)
continue;
2753 if (
value == 1.0 && rc < 0.0) {
2754 cost_to_zero[i] -= rc;
2755 num_cost_to_zero[i]++;
2760 int selected_index = -1;
2761 double best_cost = 0.0;
2762 for (
int i = 0; i < num_vars; i++) {
2763 const IntegerVariable
var = variables[i];
2768 if (num_cost_to_zero[i] > 0 &&
2769 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2770 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2775 if (selected_index >= 0) {
2783void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2784 const int num_vars = integer_variables_.size();
2785 if (sum_cost_down_.size() < num_vars) {
2786 sum_cost_down_.resize(num_vars, 0.0);
2787 num_cost_down_.resize(num_vars, 0);
2788 sum_cost_up_.resize(num_vars, 0.0);
2789 num_cost_up_.resize(num_vars, 0);
2790 rc_scores_.resize(num_vars, 0.0);
2794 num_calls_since_reduced_cost_averages_reset_++;
2795 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2796 for (
int i = 0; i < num_vars; i++) {
2797 sum_cost_up_[i] /= 2;
2798 num_cost_up_[i] /= 2;
2799 sum_cost_down_[i] /= 2;
2800 num_cost_down_[i] /= 2;
2802 num_calls_since_reduced_cost_averages_reset_ = 0;
2806 for (
int i = 0; i < num_vars; i++) {
2807 const IntegerVariable
var = integer_variables_[i];
2814 const double rc = lp_reduced_cost_[i];
2815 if (std::abs(rc) < kCpEpsilon)
continue;
2818 sum_cost_down_[i] -= rc;
2819 num_cost_down_[i]++;
2821 sum_cost_up_[i] += rc;
2828 rc_rev_int_repository_.
SetLevel(0);
2834 positions_by_decreasing_rc_score_.clear();
2835 for (
int i = 0; i < num_vars; i++) {
2840 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2841 const double a_down =
2842 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2843 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2844 rc_scores_[i] =
std::min(a_up, a_down);
2846 rc_scores_[i] = 0.5 * (a_down + a_up);
2851 if (rc_scores_[i] > 0.0) {
2852 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2855 std::sort(positions_by_decreasing_rc_score_.begin(),
2856 positions_by_decreasing_rc_score_.end());
2860std::function<IntegerLiteral()>
2862 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2865IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2867 int selected_index = -1;
2868 const int size = positions_by_decreasing_rc_score_.size();
2869 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2870 for (
int i = rev_rc_start_; i < size; ++i) {
2871 const int index = positions_by_decreasing_rc_score_[i].second;
2872 const IntegerVariable
var = integer_variables_[
index];
2875 selected_index =
index;
2880 if (selected_index == -1)
return IntegerLiteral();
2881 const IntegerVariable
var = integer_variables_[selected_index];
2888 const IntegerValue value_ceil(
2890 if (value_ceil >= ub) {
2897 const IntegerValue value_floor(
2899 if (value_floor <= lb) {
2906 num_cost_up_[selected_index] > 0
2907 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2909 const double a_down =
2910 num_cost_down_[selected_index] > 0
2911 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2913 if (a_down < a_up) {
2921 std::string result =
"LP statistics:\n";
2922 absl::StrAppend(&result,
" final dimension: ",
DimensionString(),
"\n");
2923 absl::StrAppend(&result,
" total number of simplex iterations: ",
2924 total_num_simplex_iterations_,
"\n");
2925 absl::StrAppend(&result,
" num solves: \n");
2926 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
2927 if (num_solves_by_status_[i] == 0)
continue;
2928 absl::StrAppend(&result,
" - #",
2930 num_solves_by_status_[i],
"\n");
2932 absl::StrAppend(&result, constraint_manager_.
Statistics());
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
void assign(size_type n, const value_type &val)
void resize(size_type new_size)
void push_back(const value_type &x)
static int64_t GCD64(int64_t x, int64_t y)
void SetLevel(int level) final
void SaveState(T *object)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
static constexpr CostScalingAlgorithm MEAN_COST_SCALING
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
void SetObjectiveOffset(Fractional objective_offset)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
ColIndex CreateNewVariable()
void NotifyThatColumnsAreClean()
void SetObjectiveCoefficient(ColIndex col, Fractional value)
RowIndex CreateNewConstraint()
std::string GetDimensionString() const
Fractional objective_scaling_factor() const
const SparseColumn & GetSparseColumn(ColIndex col) const
RowIndex num_constraints() const
void Scale(LinearProgram *lp)
Fractional VariableScalingFactor(ColIndex col) const
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Fractional UnscaleReducedCost(ColIndex col, Fractional value) const
Fractional UnscaleDualValue(RowIndex row, Fractional value) const
const GlopParameters & GetParameters() const
const DenseRow & GetDualRayRowCombination() const
Fractional GetVariableValue(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
VariableStatus GetVariableStatus(ColIndex col) const
Fractional GetReducedCost(ColIndex col) const
const DenseColumn & GetDualRay() const
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
Fractional GetObjectiveValue() const
Fractional GetDualValue(RowIndex row) const
void ClearIntegralityScales()
void NotifyThatMatrixIsUnchangedForNextSolve()
ConstraintStatus GetConstraintStatus(RowIndex row) const
ColIndex GetProblemNumCols() const
void LoadStateForNextSolve(const BasisState &state)
RowIndex GetProblemNumRows() const
void ClearStateForNextSolve()
int64_t GetNumberOfIterations() const
const BasisState & GetState() const
ColIndex GetBasis(RowIndex row) const
void SetParameters(const GlopParameters ¶meters)
EntryIndex num_entries() const
LinearConstraint * mutable_cut()
bool TrySimpleKnapsack(const LinearConstraint base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
void AlwaysCallAtLevelZero(int id)
void RegisterReversibleInt(int id, int *rev)
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
void AddLpVariable(IntegerVariable var)
void ProcessUpperBoundedConstraintWithSlackCreation(bool substitute_only_inner_variables, IntegerVariable first_slack, const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraint *cut, std::vector< SlackInfo > *slack_infos)
bool DebugSlack(IntegerVariable first_slack, const LinearConstraint &initial_cut, const LinearConstraint &cut, const std::vector< SlackInfo > &info)
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
void AddData(double new_record)
double CurrentAverage() const
const IntegerVariable GetLiteralView(Literal lit) const
int NumLiftedBooleans() const
void ComputeCut(RoundingOptions options, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds, ImpliedBoundsProcessor *ib_processor, LinearConstraint *cut)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
bool IsCurrentlyIgnored(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
bool IsFixedAtLevelZero(IntegerVariable var) const
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
void RegisterReversibleClass(ReversibleInterface *rev)
bool ChangeLp(const absl::StrongVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
bool DebugCheckConstraint(const LinearConstraint &cut)
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
const std::vector< ConstraintIndex > & LpConstraints() const
std::string Statistics() const
void AddAllConstraintsToLp()
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
const absl::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
bool Propagate() override
std::string DimensionString() const
double GetSolutionValue(IntegerVariable variable) const
void RegisterWith(Model *model)
glop::RowIndex ConstraintIndex
std::function< IntegerLiteral()> HeuristicLpReducedCostAverageBranching()
LinearProgrammingConstraint(Model *model)
std::string Statistics() const
std::function< IntegerLiteral()> HeuristicLpReducedCostBinary(Model *model)
void AddLinearConstraint(const LinearConstraint &ct)
bool IncrementalPropagate(const std::vector< int > &watch_indices) override
void SetLevel(int level) override
std::function< IntegerLiteral()> HeuristicLpMostInfeasibleBinary(Model *model)
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
double GetSolutionReducedCost(IntegerVariable variable) const
void AddCutGenerator(CutGenerator generator)
Class that owns everything related to a particular optimization model.
int32_t linearization_level() const
bool polish_lp_solution() const
bool add_mir_cuts() const
int32_t cut_level() const
bool add_objective_cut() const
bool use_branching_in_lp() const
static constexpr SearchBranching LP_SEARCH
bool add_zero_half_cuts() const
bool add_lp_constraints_lazily() const
bool use_exact_lp_reason() const
int32_t max_cut_rounds_at_level_zero() const
int32_t max_integer_rounding_scaling() const
bool only_add_cuts_at_level_zero() const
void ConvertToLinearConstraint(const std::vector< IntegerVariable > &integer_variables, IntegerValue upper_bound, LinearConstraint *result)
bool Add(glop::ColIndex col, IntegerValue value)
bool AddLinearExpressionMultiple(IntegerValue multiplier, const std::vector< std::pair< glop::ColIndex, IntegerValue > > &terms)
void ClearAndResize(int size)
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
std::vector< Literal > * MutableConflict()
int CurrentDecisionLevel() const
void ProcessVariables(const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
void AddOneConstraint(glop::RowIndex, const std::vector< std::pair< glop::ColIndex, IntegerValue > > &terms, IntegerValue lb, IntegerValue ub)
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
StrictITIVector< ColIndex, Fractional > DenseRow
std::string GetProblemStatusString(ProblemStatus problem_status)
ColIndex RowToColIndex(RowIndex row)
RowIndex ColToRowIndex(ColIndex col)
StrictITIVector< RowIndex, Fractional > DenseColumn
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
CutGenerator CreateCVRPCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const std::vector< int64_t > &demands, int64_t capacity, Model *model)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
const IntegerVariable kNoIntegerVariable(-1)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
IntegerVariable PositiveVariable(IntegerVariable i)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
void SeparateSubtourInequalities(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const absl::StrongVector< IntegerVariable, double > &lp_values, absl::Span< const int64_t > demands, int64_t capacity, LinearConstraintManager *manager, Model *model)
bool VariableIsPositive(IntegerVariable i)
void DivideByGCD(LinearConstraint *constraint)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
std::pair< int64_t, int64_t > Arc
int64_t CapProd(int64_t x, int64_t y)
std::vector< IntegerVariable > vars
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
#define VLOG_IS_ON(verboselevel)