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()]);
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);
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),
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) {
198 glop::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;
226 if (ivar != pos_var) coeff = -coeff;
229 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
230 integer_objective_.push_back({
col, coeff});
231 objective_infinity_norm_ =
248 bool 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.";
369 LPSolveInfo 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)));
392 void 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(
411 bool 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)
626 void 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];
637 bool 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_;
685 bool 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 std::vector<ImpliedBoundsProcessor::SlackInfo> ib_slack_infos;
747 false, first_new_var,
748 expanded_lp_solution_, &cut_, &ib_slack_infos);
750 cut_, 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 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(first_new_var +
779 IntegerVariable(2 * 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);
792 cut_.
coeffs.push_back(coeff);
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, 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, ib_slack_infos, &cut_);
832 return at_least_one_added;
835 bool 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;
884 std::vector<std::pair<ColIndex, IntegerValue>> terms;
885 for (
const std::pair<IntegerVariable, IntegerValue>& term :
886 ib_slack_infos[
index].terms) {
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_,
952 void LinearProgrammingConstraint::AddCGCuts() {
954 std::vector<std::pair<RowIndex, double>> lp_multipliers;
955 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
956 for (RowIndex
row(0);
row < num_rows; ++
row) {
958 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
966 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
970 if (basis_col >= integer_variables_.size())
continue;
975 double magnitude = 0.0;
976 lp_multipliers.clear();
978 if (lambda.non_zeros.empty()) {
979 for (RowIndex
row(0);
row < num_rows; ++
row) {
981 if (std::abs(
value) < kZeroTolerance)
continue;
987 VLOG(1) <<
"BASIC row not expected! " <<
value;
992 lp_multipliers.push_back({
row,
value});
995 for (
const ColIndex
col : lambda.non_zeros) {
997 const double value = lambda.values[
col];
998 if (std::abs(
value) < kZeroTolerance)
continue;
1002 VLOG(1) <<
"BASIC row not expected! " <<
value;
1007 lp_multipliers.push_back({
row,
value});
1010 if (lp_multipliers.empty())
continue;
1013 for (
int i = 0; i < 2; ++i) {
1019 for (std::pair<RowIndex, double>& p : lp_multipliers) {
1020 p.second = -p.second;
1026 integer_multipliers =
1027 ScaleLpMultiplier(
false,
1028 lp_multipliers, &scaling, 52);
1029 AddCutFromConstraints(
"CG", integer_multipliers);
1037 void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
1038 ModelRandomGenerator* random,
1039 std::vector<std::pair<RowIndex, RowIndex>>* output) {
1040 if (
a.empty() ||
b.empty())
return;
1041 for (
const RowIndex
row :
a) {
1042 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
1044 output->push_back({
row, other});
1049 template <
class ListOfTerms>
1050 IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1051 for (
const auto& term : terms) {
1052 if (term.first ==
col)
return term.second;
1054 return IntegerValue(0);
1068 void LinearProgrammingConstraint::AddObjectiveCut() {
1069 if (integer_objective_.size() <= 1)
return;
1072 tmp_lp_values_.clear();
1073 tmp_var_lbs_.clear();
1074 tmp_var_ubs_.clear();
1079 cut_.
ub = integer_objective_offset_ -
1081 for (
const auto& [
col, coeff] : integer_objective_) {
1082 const IntegerVariable
var = integer_variables_[
col.value()];
1084 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1087 cut_.
coeffs.push_back(-coeff);
1092 RoundingOptions options;
1094 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_, tmp_var_lbs_,
1096 &implied_bounds_processor_, &cut_);
1099 constraint_manager_.
AddCut(cut_,
"Objective", expanded_lp_solution_);
1102 void LinearProgrammingConstraint::AddMirCuts() {
1118 integer_variables_.size(), IntegerValue(0));
1119 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1124 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1126 for (RowIndex
row(0);
row < num_rows; ++
row) {
1133 base_rows.push_back({
row, IntegerValue(1)});
1137 base_rows.push_back({
row, IntegerValue(-1)});
1158 std::vector<double> weights;
1160 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1161 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1171 integer_multipliers = {entry};
1172 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1177 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1178 dense_cut[
col] = IntegerValue(0);
1180 non_zeros_.SparseClearAll();
1183 const IntegerValue multiplier = entry.second;
1184 for (
const std::pair<ColIndex, IntegerValue> term :
1185 integer_lp_[entry.first].terms) {
1186 const ColIndex
col = term.first;
1187 const IntegerValue coeff = term.second;
1188 non_zeros_.Set(
col);
1189 dense_cut[
col] += coeff * multiplier;
1192 used_rows.
assign(num_rows.value(),
false);
1193 used_rows[entry.first] =
true;
1198 const int kMaxAggregation = 5;
1199 for (
int i = 0; i < kMaxAggregation; ++i) {
1202 IntegerValue max_magnitude(0);
1204 std::vector<ColIndex> col_candidates;
1205 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1206 if (dense_cut[
col] == 0)
continue;
1209 const int col_degree =
1211 if (col_degree <= 1)
continue;
1216 const IntegerVariable
var = integer_variables_[
col.value()];
1217 const double lp_value = expanded_lp_solution_[
var];
1220 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1221 if (bound_distance > 1e-2) {
1222 weights.push_back(bound_distance);
1223 col_candidates.push_back(
col);
1226 if (col_candidates.empty())
break;
1228 const ColIndex var_to_eliminate =
1229 col_candidates[std::discrete_distribution<>(weights.begin(),
1230 weights.end())(*random_)];
1233 std::vector<RowIndex> possible_rows;
1236 const RowIndex
row = entry.row();
1244 if (used_rows[
row])
continue;
1245 used_rows[
row] =
true;
1253 bool add_row =
false;
1256 if (entry.coefficient() > 0.0) {
1257 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1259 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1264 if (entry.coefficient() > 0.0) {
1265 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1267 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1271 possible_rows.push_back(
row);
1272 weights.push_back(row_weights[
row]);
1275 if (possible_rows.empty())
break;
1277 const RowIndex row_to_combine =
1278 possible_rows[std::discrete_distribution<>(weights.begin(),
1279 weights.end())(*random_)];
1280 const IntegerValue to_combine_coeff =
1281 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1284 IntegerValue mult1 = -to_combine_coeff;
1285 IntegerValue mult2 = dense_cut[var_to_eliminate];
1292 const IntegerValue gcd = IntegerValue(
1302 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1305 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1307 std::abs(mult2.value()))) ==
1312 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1313 entry.second *= mult1;
1315 integer_multipliers.push_back({row_to_combine, mult2});
1318 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1319 integer_multipliers)) {
1325 if (i + 1 == kMaxAggregation)
break;
1327 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1328 dense_cut[
col] *= mult1;
1330 for (
const std::pair<ColIndex, IntegerValue> term :
1331 integer_lp_[row_to_combine].terms) {
1332 const ColIndex
col = term.first;
1333 const IntegerValue coeff = term.second;
1334 non_zeros_.Set(
col);
1335 dense_cut[
col] += coeff * mult2;
1341 void LinearProgrammingConstraint::AddZeroHalfCuts() {
1344 tmp_lp_values_.clear();
1345 tmp_var_lbs_.clear();
1346 tmp_var_ubs_.clear();
1347 for (
const IntegerVariable
var : integer_variables_) {
1348 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1356 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1364 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1366 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1374 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1378 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1379 const int64_t min_iter,
const int64_t max_iter) {
1381 const int64_t num_degenerate_columns = CalculateDegeneracy();
1383 if (num_cols <= 0) {
1387 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1392 if (is_degenerate_) {
1393 next_simplex_iter_ /=
std::max(int64_t{1}, decrease_factor);
1395 next_simplex_iter_ *= 2;
1398 if (is_degenerate_) {
1399 next_simplex_iter_ /=
std::max(int64_t{1}, 2 * decrease_factor);
1403 next_simplex_iter_ = num_cols / 40;
1406 next_simplex_iter_ =
1411 UpdateBoundsOfLpVariables();
1416 if ( (
false) && objective_is_defined_) {
1425 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1426 100.0 * kCpEpsilon));
1435 parameters.set_max_number_of_iterations(2000);
1437 parameters.set_max_number_of_iterations(next_simplex_iter_);
1440 parameters.set_change_status_to_imprecise(
false);
1441 parameters.set_primal_feasibility_tolerance(1e-7);
1442 parameters.set_dual_feasibility_tolerance(1e-7);
1447 if (!SolveLp())
return true;
1450 const int max_cuts_rounds =
1458 cuts_round < max_cuts_rounds) {
1463 if (num_solves_ > 1) {
1466 expanded_lp_solution_);
1480 if (!cut_generators_.empty() &&
1483 for (
const CutGenerator& generator : cut_generators_) {
1484 if (!generator.generate_cuts(expanded_lp_solution_,
1485 &constraint_manager_)) {
1492 expanded_lp_solution_, &constraint_manager_);
1496 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1498 if (!CreateLpFromConstraintManager()) {
1502 if (!SolveLp())
return true;
1504 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> " 1511 lp_at_level_zero_is_final_ =
true;
1520 if (!FillExactDualRayReason())
return true;
1529 UpdateSimplexIterationLimit(10, 1000);
1532 if (objective_is_defined_ &&
1538 if (!ExactLpReasonning())
return false;
1543 const IntegerValue approximate_new_lb(static_cast<int64_t>(
1544 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1545 const IntegerValue propagated_lb =
1547 if (approximate_new_lb > propagated_lb) {
1548 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", " 1550 <<
" ] approx_lb += " 1551 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: " 1552 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1559 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1560 const double objective_cp_ub =
1563 ReducedCostStrengtheningDeductions(objective_cp_ub -
1564 relaxed_optimal_objective);
1565 if (!deductions_.empty()) {
1566 deductions_reason_ = integer_reason_;
1567 deductions_reason_.push_back(
1572 const IntegerValue approximate_new_lb(static_cast<int64_t>(
1573 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1574 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1577 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1583 if (!deductions_.empty()) {
1584 const int trail_index_with_same_reason = integer_trail_->
Index();
1586 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1587 trail_index_with_same_reason)) {
1597 CHECK(lp_solution_is_set_);
1600 lp_solution_is_integer_ =
true;
1601 const int num_vars = integer_variables_.size();
1602 for (
int i = 0; i < num_vars; i++) {
1605 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1607 lp_solution_is_integer_ =
false;
1611 if (compute_reduced_cost_averages_) {
1612 UpdateAverageReducedCosts();
1618 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1620 compute_reduced_cost_averages_ &&
1622 count_since_last_branching_++;
1623 if (count_since_last_branching_ < branching_frequency_) {
1626 count_since_last_branching_ = 0;
1627 bool branching_successful =
false;
1630 const int max_num_branches = 3;
1631 const int num_vars = integer_variables_.size();
1632 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1633 for (
int i = 0; i < num_vars; ++i) {
1634 const IntegerVariable
var = integer_variables_[i];
1639 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1653 const double cost_i = rc_scores_[i];
1654 std::pair<double, IntegerVariable> branching_var =
1655 std::make_pair(-cost_i, positive_var);
1657 branching_vars.end(), branching_var);
1659 branching_vars.insert(iterator, branching_var);
1660 if (branching_vars.size() > max_num_branches) {
1661 branching_vars.resize(max_num_branches);
1665 for (
const std::pair<double, IntegerVariable>& branching_var :
1667 const IntegerVariable positive_var = branching_var.second;
1668 VLOG(2) <<
"Branching on: " << positive_var;
1669 if (BranchOnVar(positive_var)) {
1670 VLOG(2) <<
"Branching successful.";
1671 branching_successful =
true;
1676 if (!branching_successful) {
1677 branching_frequency_ *= 2;
1686 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1689 const int size = terms.
vars.size();
1690 for (
int i = 0; i < size; ++i) {
1691 const IntegerVariable
var = terms.
vars[i];
1692 const IntegerValue coeff = terms.
coeffs[i];
1701 bool LinearProgrammingConstraint::PossibleOverflow(
1702 const LinearConstraint& constraint) {
1704 const int size = constraint.vars.size();
1705 for (
int i = 0; i < size; ++i) {
1706 const IntegerVariable
var = constraint.vars[i];
1707 const IntegerValue coeff = constraint.coeffs[i];
1709 const IntegerValue
bound = coeff > 0
1726 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1727 absl::int128 div128(positive_div.value());
1728 absl::int128 result = x / div128;
1729 if (result * div128 > x)
return result - 1;
1735 void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1744 const int size = constraint->vars.size();
1745 for (
int i = 0; i < size; ++i) {
1746 const IntegerVariable
var = constraint->vars[i];
1747 const double coeff =
ToDouble(constraint->coeffs[i]);
1755 const double max_value =
std::max({sum_max, -sum_min, sum_max - sum_min});
1757 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1758 if (divisor <= 1)
return;
1773 absl::int128 adjust = 0;
1774 for (
int i = 0; i < size; ++i) {
1775 const IntegerValue old_coeff = constraint->coeffs[i];
1776 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1779 const absl::int128 remainder =
1780 absl::int128(old_coeff.value()) -
1781 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1787 if (new_coeff == 0)
continue;
1788 constraint->vars[new_size] = constraint->vars[i];
1789 constraint->coeffs[new_size] = new_coeff;
1792 constraint->vars.resize(new_size);
1793 constraint->coeffs.resize(new_size);
1795 constraint->ub = IntegerValue(static_cast<int64_t>(
1796 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1801 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1802 const LinearConstraint& terms, IntegerValue slack) {
1803 integer_reason_.clear();
1804 std::vector<IntegerValue> magnitudes;
1805 const int size = terms.vars.size();
1806 for (
int i = 0; i < size; ++i) {
1807 const IntegerVariable
var = terms.vars[i];
1808 const IntegerValue coeff = terms.coeffs[i];
1811 magnitudes.push_back(coeff);
1814 magnitudes.push_back(-coeff);
1825 std::vector<std::pair<RowIndex, IntegerValue>>
1826 LinearProgrammingConstraint::ScaleLpMultiplier(
1827 bool take_objective_into_account,
1828 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1830 double max_sum = 0.0;
1831 tmp_cp_multipliers_.clear();
1832 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1833 const RowIndex
row = p.first;
1838 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1854 tmp_cp_multipliers_.push_back({
row, cp_multi});
1855 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1860 if (take_objective_into_account) {
1861 max_sum +=
ToDouble(objective_infinity_norm_);
1865 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1866 if (max_sum == 0.0) {
1868 return integer_multipliers;
1873 const double threshold = std::ldexp(1, max_pow) / max_sum;
1874 if (threshold < 1.0) {
1877 return integer_multipliers;
1879 while (2 * *scaling <= threshold) *scaling *= 2;
1884 for (
const auto entry : tmp_cp_multipliers_) {
1885 const IntegerValue coeff(std::round(entry.second * (*scaling)));
1886 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1888 return integer_multipliers;
1891 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1892 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1893 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1896 scattered_vector->ClearAndResize(integer_variables_.size());
1900 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1901 const RowIndex
row = term.first;
1902 const IntegerValue multiplier = term.second;
1906 if (!scattered_vector->AddLinearExpressionMultiple(
1907 multiplier, integer_lp_[
row].terms)) {
1912 const IntegerValue
bound =
1913 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1921 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1922 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1923 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1924 const IntegerValue kMaxWantedCoeff(1e18);
1925 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1926 const RowIndex
row = term.first;
1927 const IntegerValue multiplier = term.second;
1928 if (multiplier == 0)
continue;
1932 IntegerValue negative_limit = kMaxWantedCoeff;
1933 IntegerValue positive_limit = kMaxWantedCoeff;
1937 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1938 if (multiplier > 0) {
1939 negative_limit =
std::min(negative_limit, multiplier);
1941 positive_limit =
std::min(positive_limit, -multiplier);
1946 const IntegerValue row_bound =
1947 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1948 if (row_bound != 0) {
1952 const IntegerValue limit2 =
1955 positive_limit =
std::min(positive_limit, limit1);
1956 negative_limit =
std::min(negative_limit, limit2);
1958 negative_limit =
std::min(negative_limit, limit1);
1959 positive_limit =
std::min(positive_limit, limit2);
1971 double positive_diff =
ToDouble(row_bound);
1972 double negative_diff =
ToDouble(row_bound);
1977 for (
const auto entry : integer_lp_[
row].terms) {
1978 const ColIndex
col = entry.first;
1979 const IntegerValue coeff = entry.second;
1980 const IntegerValue abs_coef =
IntTypeAbs(coeff);
1983 const IntegerVariable
var = integer_variables_[
col.value()];
1990 const IntegerValue current = (*scattered_vector)[
col];
1992 const IntegerValue overflow_limit(
1994 positive_limit =
std::min(positive_limit, overflow_limit);
1995 negative_limit =
std::min(negative_limit, overflow_limit);
2012 const IntegerValue current_magnitude =
IntTypeAbs(current);
2013 const IntegerValue other_direction_limit =
FloorRatio(
2015 ? kMaxWantedCoeff +
std::min(current_magnitude,
2017 : current_magnitude,
2019 const IntegerValue same_direction_limit(
FloorRatio(
2020 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
2022 if ((current > 0) == (coeff > 0)) {
2023 negative_limit =
std::min(negative_limit, other_direction_limit);
2024 positive_limit =
std::min(positive_limit, same_direction_limit);
2026 negative_limit =
std::min(negative_limit, same_direction_limit);
2027 positive_limit =
std::min(positive_limit, other_direction_limit);
2031 const IntegerValue implied = current > 0 ? lb : ub;
2042 IntegerValue to_add(0);
2043 if (positive_diff <= -1.0 && positive_limit > 0) {
2044 to_add = positive_limit;
2046 if (negative_diff >= 1.0 && negative_limit > 0) {
2049 std::abs(
ToDouble(negative_limit) * negative_diff) >
2050 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2051 to_add = -negative_limit;
2055 term.second += to_add;
2060 CHECK(scattered_vector->AddLinearExpressionMultiple(
2061 to_add, integer_lp_[
row].terms));
2080 bool LinearProgrammingConstraint::ExactLpReasonning() {
2082 integer_reason_.clear();
2083 deductions_.clear();
2084 deductions_reason_.clear();
2090 std::vector<std::pair<RowIndex, double>> lp_multipliers;
2091 for (RowIndex
row(0);
row < num_rows; ++
row) {
2093 if (std::abs(
value) < kZeroTolerance)
continue;
2094 lp_multipliers.push_back({
row,
value});
2098 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2099 ScaleLpMultiplier(
true, lp_multipliers,
2103 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2105 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2111 const IntegerValue obj_scale(std::round(scaling));
2112 if (obj_scale == 0) {
2113 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2117 integer_objective_));
2119 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2124 LinearConstraint new_constraint;
2127 new_constraint.vars.push_back(objective_cp_);
2128 new_constraint.coeffs.push_back(-obj_scale);
2130 PreventOverflow(&new_constraint);
2131 DCHECK(!PossibleOverflow(new_constraint));
2135 if (new_constraint.vars.empty()) {
2137 return new_constraint.ub >= 0;
2140 IntegerSumLE* cp_constraint =
2141 new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
2142 new_constraint.ub, model_);
2146 optimal_constraints_.clear();
2148 optimal_constraints_.emplace_back(cp_constraint);
2149 rev_optimal_constraints_size_ = optimal_constraints_.size();
2150 if (!cp_constraint->PropagateAtLevelZero())
return false;
2151 return cp_constraint->Propagate();
2154 bool LinearProgrammingConstraint::FillExactDualRayReason() {
2157 std::vector<std::pair<RowIndex, double>> lp_multipliers;
2158 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2160 if (std::abs(
value) < kZeroTolerance)
continue;
2161 lp_multipliers.push_back({
row,
value});
2163 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2164 ScaleLpMultiplier(
false, lp_multipliers,
2167 IntegerValue new_constraint_ub;
2168 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2169 &new_constraint_ub)) {
2170 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2174 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2175 &new_constraint_ub);
2177 LinearConstraint new_constraint;
2179 integer_variables_, new_constraint_ub, &new_constraint);
2181 PreventOverflow(&new_constraint);
2182 DCHECK(!PossibleOverflow(new_constraint));
2185 const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
2186 if (implied_lb <= new_constraint.ub) {
2187 VLOG(1) <<
"LP exact dual ray not infeasible," 2188 <<
" implied_lb: " << implied_lb.value() / scaling
2189 <<
" ub: " << new_constraint.ub.value() / scaling;
2192 const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
2193 SetImpliedLowerBoundReason(new_constraint, slack);
2197 int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2199 int num_non_basic_with_zero_rc = 0;
2200 for (glop::ColIndex i(0); i < num_vars; ++i) {
2202 if (rc != 0.0)
continue;
2206 num_non_basic_with_zero_rc++;
2209 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2210 return num_non_basic_with_zero_rc;
2213 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2214 double cp_objective_delta) {
2215 deductions_.clear();
2220 const double lp_objective_delta =
2222 const int num_vars = integer_variables_.size();
2223 for (
int i = 0; i < num_vars; i++) {
2224 const IntegerVariable cp_var = integer_variables_[i];
2225 const glop::ColIndex lp_var = glop::ColIndex(i);
2229 if (rc == 0.0)
continue;
2230 const double lp_other_bound =
value + lp_objective_delta / rc;
2231 const double cp_other_bound =
2234 if (rc > kLpEpsilon) {
2236 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2241 const IntegerValue new_ub_int(static_cast<IntegerValue>(new_ub));
2244 }
else if (rc < -kLpEpsilon) {
2246 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2248 const IntegerValue new_lb_int(static_cast<IntegerValue>(new_lb));
2249 deductions_.push_back(
2263 void AddOutgoingCut(
2264 int num_nodes,
int subset_size,
const std::vector<bool>& in_subset,
2265 const std::vector<int>& tails,
const std::vector<int>& heads,
2266 const std::vector<Literal>& literals,
2267 const std::vector<double>& literal_lp_values, int64_t rhs_lower_bound,
2269 LinearConstraintManager* manager, Model*
model) {
2276 int num_optional_nodes_in = 0;
2277 int num_optional_nodes_out = 0;
2278 int optional_loop_in = -1;
2279 int optional_loop_out = -1;
2280 for (
int i = 0; i < tails.size(); ++i) {
2281 if (tails[i] != heads[i])
continue;
2282 if (in_subset[tails[i]]) {
2283 num_optional_nodes_in++;
2284 if (optional_loop_in == -1 ||
2285 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2286 optional_loop_in = i;
2289 num_optional_nodes_out++;
2290 if (optional_loop_out == -1 ||
2291 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2292 optional_loop_out = i;
2299 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2301 rhs_lower_bound = 1;
2304 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2306 double sum_outgoing = 0.0;
2309 for (
int i = 0; i < tails.size(); ++i) {
2310 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2311 sum_outgoing += literal_lp_values[i];
2312 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2317 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2319 if (num_optional_nodes_in == subset_size &&
2320 (optional_loop_in == -1 ||
2321 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2324 if (num_optional_nodes_out == num_nodes - subset_size &&
2325 (optional_loop_out == -1 ||
2326 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2331 if (num_optional_nodes_in == subset_size) {
2333 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2334 sum_outgoing += literal_lp_values[optional_loop_in];
2338 if (num_optional_nodes_out == num_nodes - subset_size) {
2339 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2341 sum_outgoing += literal_lp_values[optional_loop_out];
2345 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2346 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2359 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2360 const std::vector<Literal>& literals,
2362 absl::Span<const int64_t> demands, int64_t
capacity,
2364 if (num_nodes <= 2)
return;
2373 std::vector<Arc> relevant_arcs;
2376 std::vector<double> literal_lp_values(literals.size());
2377 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2379 for (
int i = 0; i < literals.size(); ++i) {
2381 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2383 lp_value = lp_values[direct_view];
2386 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2388 literal_lp_values[i] = lp_value;
2390 if (lp_value < 1e-6)
continue;
2391 relevant_arcs.push_back({tails[i], heads[i], lp_value});
2392 arc_by_decreasing_lp_values.push_back({lp_value, i});
2394 std::sort(arc_by_decreasing_lp_values.begin(),
2395 arc_by_decreasing_lp_values.end(),
2396 std::greater<std::pair<double, int>>());
2406 int num_components = num_nodes;
2407 std::vector<int> parent(num_nodes);
2408 std::vector<int> root(num_nodes);
2409 for (
int i = 0; i < num_nodes; ++i) {
2413 auto get_root_and_compress_path = [&root](
int node) {
2415 while (root[r] != r) r = root[r];
2416 while (root[node] != r) {
2417 const int next = root[node];
2423 for (
const auto pair : arc_by_decreasing_lp_values) {
2424 if (num_components == 2)
break;
2425 const int tail = get_root_and_compress_path(tails[pair.second]);
2426 const int head = get_root_and_compress_path(heads[pair.second]);
2430 const int new_node = parent.size();
2431 parent.push_back(new_node);
2432 parent[
head] = new_node;
2433 parent[
tail] = new_node;
2437 root.push_back(new_node);
2438 root[
head] = new_node;
2439 root[
tail] = new_node;
2450 std::vector<int> pre_order(num_nodes);
2451 std::vector<absl::Span<const int>> subsets;
2453 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2454 for (
int i = 0; i < parent.size(); ++i) {
2455 if (parent[i] != i) graph[parent[i]].push_back(i);
2457 std::vector<int> queue;
2458 std::vector<bool> seen(graph.size(),
false);
2459 std::vector<int> start_index(parent.size());
2460 for (
int i = num_nodes; i < parent.size(); ++i) {
2464 CHECK(graph[i].empty() || graph[i].size() == 2);
2465 if (parent[i] != i)
continue;
2470 while (!queue.empty()) {
2471 const int node = queue.back();
2476 const int start = start_index[node];
2477 if (new_size - start > 1) {
2478 subsets.emplace_back(&pre_order[start], new_size - start);
2483 start_index[node] = new_size;
2484 if (node < num_nodes) pre_order[new_size++] = node;
2485 for (
const int child : graph[node]) {
2486 if (!seen[child]) queue.push_back(child);
2494 int64_t total_demands = 0;
2495 if (!demands.empty()) {
2496 for (
const int64_t
demand : demands) total_demands +=
demand;
2500 CHECK_EQ(pre_order.size(), num_nodes);
2501 std::vector<bool> in_subset(num_nodes,
false);
2502 for (
const absl::Span<const int> subset : subsets) {
2504 CHECK_LT(subset.size(), num_nodes);
2507 bool contain_depot =
false;
2508 int64_t subset_demand = 0;
2511 for (
const int n : subset) {
2512 in_subset[n] =
true;
2513 if (!demands.empty()) {
2514 if (n == 0) contain_depot =
true;
2515 subset_demand += demands[n];
2532 int64_t min_outgoing_flow = 1;
2533 if (!demands.empty()) {
2543 min_outgoing_flow =
std::max(min_outgoing_flow, int64_t{1});
2555 double outgoing_flow = 0.0;
2556 for (
const auto arc : relevant_arcs) {
2557 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2558 outgoing_flow += arc.lp_value;
2563 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2564 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2565 literals, literal_lp_values,
2566 min_outgoing_flow, lp_values, manager,
2571 for (
const int n : subset) in_subset[n] =
false;
2578 std::vector<IntegerVariable> GetAssociatedVariables(
2579 const std::vector<Literal>& literals, Model*
model) {
2580 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2581 std::vector<IntegerVariable> result;
2582 for (
const Literal l : literals) {
2583 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2585 result.push_back(direct_view);
2587 result.push_back(encoder->GetLiteralView(l.Negated()));
2600 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2601 const std::vector<Literal>& literals,
Model*
model) {
2603 result.
vars = GetAssociatedVariables(literals,
model);
2605 [num_nodes, tails, heads, literals,
model](
2609 num_nodes, tails, heads, literals, lp_values,
2610 {}, 0, manager,
model);
2617 const std::vector<int>& tails,
2618 const std::vector<int>& heads,
2619 const std::vector<Literal>& literals,
2620 const std::vector<int64_t>& demands,
2623 result.
vars = GetAssociatedVariables(literals,
model);
2625 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2629 lp_values, demands,
capacity, manager,
2636 std::function<IntegerLiteral()>
2639 std::vector<IntegerVariable> variables;
2640 for (IntegerVariable
var : integer_variables_) {
2643 variables.push_back(
var);
2646 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2649 return [
this, variables]() {
2653 double fractional_distance_best = -1.0;
2654 for (
const IntegerVariable
var : variables) {
2659 if (lb == ub)
continue;
2663 const double fractional_distance =
2665 lp_value - std::floor(lp_value +
kEpsilon));
2666 if (fractional_distance <
kEpsilon)
continue;
2669 if (fractional_distance > fractional_distance_best) {
2670 fractional_var =
var;
2671 fractional_distance_best = fractional_distance;
2685 std::vector<IntegerVariable> variables;
2686 for (IntegerVariable
var : integer_variables_) {
2689 variables.push_back(
var);
2692 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2698 const int num_vars = variables.size();
2699 std::vector<double> cost_to_zero(num_vars, 0.0);
2700 std::vector<int> num_cost_to_zero(num_vars);
2703 return [=]()
mutable {
2708 if (num_calls == 10000) {
2709 for (
int i = 0; i < num_vars; i++) {
2710 cost_to_zero[i] /= 2;
2711 num_cost_to_zero[i] /= 2;
2717 for (
int i = 0; i < num_vars; i++) {
2718 const IntegerVariable
var = variables[i];
2723 if (lb == ub)
continue;
2727 if (std::abs(rc) <
kEpsilon)
continue;
2730 if (
value == 1.0 && rc < 0.0) {
2731 cost_to_zero[i] -= rc;
2732 num_cost_to_zero[i]++;
2737 int selected_index = -1;
2738 double best_cost = 0.0;
2739 for (
int i = 0; i < num_vars; i++) {
2740 const IntegerVariable
var = variables[i];
2745 if (num_cost_to_zero[i] > 0 &&
2746 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2747 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2752 if (selected_index >= 0) {
2760 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2761 const int num_vars = integer_variables_.size();
2762 if (sum_cost_down_.size() < num_vars) {
2763 sum_cost_down_.resize(num_vars, 0.0);
2764 num_cost_down_.resize(num_vars, 0);
2765 sum_cost_up_.resize(num_vars, 0.0);
2766 num_cost_up_.resize(num_vars, 0);
2767 rc_scores_.resize(num_vars, 0.0);
2771 num_calls_since_reduced_cost_averages_reset_++;
2772 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2773 for (
int i = 0; i < num_vars; i++) {
2774 sum_cost_up_[i] /= 2;
2775 num_cost_up_[i] /= 2;
2776 sum_cost_down_[i] /= 2;
2777 num_cost_down_[i] /= 2;
2779 num_calls_since_reduced_cost_averages_reset_ = 0;
2783 for (
int i = 0; i < num_vars; i++) {
2784 const IntegerVariable
var = integer_variables_[i];
2791 const double rc = lp_reduced_cost_[i];
2792 if (std::abs(rc) < kCpEpsilon)
continue;
2795 sum_cost_down_[i] -= rc;
2796 num_cost_down_[i]++;
2798 sum_cost_up_[i] += rc;
2805 rc_rev_int_repository_.
SetLevel(0);
2811 positions_by_decreasing_rc_score_.clear();
2812 for (
int i = 0; i < num_vars; i++) {
2817 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2818 const double a_down =
2819 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2820 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2821 rc_scores_[i] =
std::min(a_up, a_down);
2823 rc_scores_[i] = 0.5 * (a_down + a_up);
2828 if (rc_scores_[i] > 0.0) {
2829 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2832 std::sort(positions_by_decreasing_rc_score_.begin(),
2833 positions_by_decreasing_rc_score_.end());
2837 std::function<IntegerLiteral()>
2839 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2842 IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2844 int selected_index = -1;
2845 const int size = positions_by_decreasing_rc_score_.size();
2846 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2847 for (
int i = rev_rc_start_; i < size; ++i) {
2848 const int index = positions_by_decreasing_rc_score_[i].second;
2849 const IntegerVariable
var = integer_variables_[
index];
2852 selected_index =
index;
2857 if (selected_index == -1)
return IntegerLiteral();
2858 const IntegerVariable
var = integer_variables_[selected_index];
2865 const IntegerValue value_ceil(
2867 if (value_ceil >= ub) {
2874 const IntegerValue value_floor(
2876 if (value_floor <= lb) {
2883 num_cost_up_[selected_index] > 0
2884 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2886 const double a_down =
2887 num_cost_down_[selected_index] > 0
2888 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2890 if (a_down < a_up) {
2898 std::string result =
"LP statistics:\n";
2899 absl::StrAppend(&result,
" final dimension: ",
DimensionString(),
"\n");
2900 absl::StrAppend(&result,
" total number of simplex iterations: ",
2901 total_num_simplex_iterations_,
"\n");
2902 absl::StrAppend(&result,
" num solves: \n");
2903 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
2904 if (num_solves_by_status_[i] == 0)
continue;
2905 absl::StrAppend(&result,
" - #",
2907 num_solves_by_status_[i],
"\n");
2909 absl::StrAppend(&result, constraint_manager_.
Statistics());
std::string DimensionString() const
ColIndex RowToColIndex(RowIndex row)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
int64_t CapSub(int64_t x, int64_t y)
void NotifyThatMatrixIsUnchangedForNextSolve()
static constexpr SearchBranching LP_SEARCH
void SetObjectiveCoefficient(ColIndex col, Fractional value)
#define CHECK_GE(val1, val2)
Class that owns everything related to a particular optimization model.
RowIndex num_constraints() const
void LoadStateForNextSolve(const BasisState &state)
std::vector< IntegerValue > coeffs
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
int NumLiftedBooleans() const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
bool Propagate() override
#define CHECK_GT(val1, val2)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< Literal > * MutableConflict()
ProblemStatus GetProblemStatus() const
#define VLOG(verboselevel)
Fractional GetVariableValue(ColIndex col) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Fractional UnscaleDualValue(RowIndex row, Fractional value) const
std::vector< IntegerVariable > vars
IntegerValue LowerBound(IntegerVariable i) const
const std::vector< ConstraintIndex > & LpConstraints() const
std::function< IntegerLiteral()> HeuristicLpMostInfeasibleBinary(Model *model)
void SetLevel(int level) final
void RegisterWith(Model *model)
void ClearStateForNextSolve()
std::string GetProblemStatusString(ProblemStatus problem_status)
const absl::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
int64_t CapProd(int64_t x, int64_t y)
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
#define DCHECK_GT(val1, val2)
void SetObjectiveOffset(Fractional objective_offset)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
int32_t linearization_level() 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)
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
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)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool use_branching_in_lp() const
void AddData(double new_record)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
const IntegerVariable GetLiteralView(Literal lit) const
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
void NotifyThatColumnsAreClean()
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
std::vector< IntegerVariable > vars
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
#define CHECK_LT(val1, val2)
bool only_add_cuts_at_level_zero() const
Fractional GetReducedCost(ColIndex col) const
int32_t max_integer_rounding_scaling() const
double ToDouble(IntegerValue value)
IntegerVariable PositiveVariable(IntegerVariable i)
static constexpr CostScalingAlgorithm MEAN_COST_SCALING
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
void DivideByGCD(LinearConstraint *constraint)
EntryIndex num_entries() const
void ClearAndResize(int size)
Fractional GetObjectiveValue() const
Fractional objective_scaling_factor() const
void resize(size_type new_size)
std::string GetDimensionString() const
bool add_lp_constraints_lazily() const
StrictITIVector< RowIndex, Fractional > DenseColumn
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)
double GetSolutionReducedCost(IntegerVariable variable) const
Fractional VariableScalingFactor(ColIndex col) const
int64_t CapAdd(int64_t x, int64_t y)
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
#define DCHECK_NE(val1, val2)
std::string Statistics() const
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
ColIndex GetProblemNumCols() const
void AddOneConstraint(glop::RowIndex, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms, IntegerValue lb, IntegerValue ub)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
void SetParameters(const GlopParameters ¶meters)
VariableStatus GetVariableStatus(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
std::string Statistics() const
ColIndex CreateNewVariable()
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
static int64_t GCD64(int64_t x, int64_t y)
std::function< IntegerLiteral()> HeuristicLpReducedCostAverageBranching()
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
int32_t cut_level() const
bool DebugSlack(IntegerVariable first_slack, const LinearConstraint &initial_cut, const LinearConstraint &cut, const std::vector< SlackInfo > &info)
void SetPropagatorPriority(int id, int priority)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
const BasisState & GetState() const
void ProcessVariables(const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
std::function< IntegerLiteral()> HeuristicLpReducedCostBinary(Model *model)
void AlwaysCallAtLevelZero(int id)
void push_back(const value_type &x)
bool IsFixedAtLevelZero(IntegerVariable var) const
RowIndex CreateNewConstraint()
bool polish_lp_solution() const
bool ChangeLp(const absl::StrongVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
bool VariableIsPositive(IntegerVariable i)
RowIndex ColToRowIndex(ColIndex col)
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
bool add_zero_half_cuts() const
const DenseColumn & GetDualRay() const
void ProcessUpperBoundedConstraintWithSlackCreation(bool substitute_only_inner_variables, IntegerVariable first_slack, const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraint *cut, std::vector< SlackInfo > *slack_infos)
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
#define CHECK_EQ(val1, val2)
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
LinearConstraint * mutable_cut()
const DenseRow & GetReducedCosts() const
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
bool IncrementalPropagate(const std::vector< int > &watch_indices) override
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
#define DCHECK(condition)
const GlopParameters & GetParameters() const
bool use_exact_lp_reason() const
int64_t GetNumberOfIterations() const
ConstraintStatus GetConstraintStatus(RowIndex row) const
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
void RegisterReversibleClass(ReversibleInterface *rev)
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
bool IsCurrentlyIgnored(IntegerVariable i) const
const DenseRow & GetDualRayRowCombination() const
int32_t max_cut_rounds_at_level_zero() const
RowIndex GetProblemNumRows() const
std::pair< int64_t, int64_t > Arc
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
glop::RowIndex ConstraintIndex
int Register(PropagatorInterface *propagator)
void AddLinearConstraint(const LinearConstraint &ct)
IntegerValue UpperBound(IntegerVariable i) const
void AddLpVariable(IntegerVariable var)
Collection of objects used to extend the Constraint Solver library.
const IntegerVariable kNoIntegerVariable(-1)
void SetLevel(int level) override
void ClearIntegralityScales()
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
void assign(size_type n, const value_type &val)
Fractional UnscaleReducedCost(ColIndex col, Fractional value) const
Fractional GetDualValue(RowIndex row) const
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void SaveState(T *object)
bool add_objective_cut() const
bool Add(glop::ColIndex col, IntegerValue value)
void AddAllConstraintsToLp()
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
double CurrentAverage() const
bool add_mir_cuts() const
LinearProgrammingConstraint(Model *model)
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
#define VLOG_IS_ON(verboselevel)
bool DebugCheckConstraint(const LinearConstraint &cut)
void ConvertToLinearConstraint(const std::vector< IntegerVariable > &integer_variables, IntegerValue upper_bound, LinearConstraint *result)
IntType IntTypeAbs(IntType t)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
StrictITIVector< ColIndex, Fractional > DenseRow
bool AddLinearExpressionMultiple(IntegerValue multiplier, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms)
void AddCutGenerator(CutGenerator generator)
int CurrentDecisionLevel() const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
bool TrySimpleKnapsack(const LinearConstraint base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
#define CHECK_NE(val1, val2)
bool IsFixed(IntegerVariable i) const
ColIndex GetBasis(RowIndex row) const
const SparseColumn & GetSparseColumn(ColIndex col) const
void RegisterReversibleInt(int id, int *rev)
double GetSolutionValue(IntegerVariable variable) const
void Scale(LinearProgram *lp)