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);
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];
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 (sat_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;
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_ =
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 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;
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;
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_,
952void 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);
1037void 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});
1049template <
class ListOfTerms>
1050IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1051 for (
const auto& term : terms) {
1052 if (term.first ==
col)
return term.second;
1054 return IntegerValue(0);
1059void LinearProgrammingConstraint::AddMirCuts() {
1075 integer_variables_.size(), IntegerValue(0));
1076 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1081 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1083 for (RowIndex
row(0);
row < num_rows; ++
row) {
1090 base_rows.push_back({
row, IntegerValue(1)});
1094 base_rows.push_back({
row, IntegerValue(-1)});
1115 std::vector<double> weights;
1117 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1118 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1128 integer_multipliers = {entry};
1129 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1134 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1135 dense_cut[
col] = IntegerValue(0);
1137 non_zeros_.SparseClearAll();
1140 const IntegerValue multiplier = entry.second;
1141 for (
const std::pair<ColIndex, IntegerValue> term :
1142 integer_lp_[entry.first].terms) {
1143 const ColIndex
col = term.first;
1144 const IntegerValue coeff = term.second;
1145 non_zeros_.Set(
col);
1146 dense_cut[
col] += coeff * multiplier;
1149 used_rows.
assign(num_rows.value(),
false);
1150 used_rows[entry.first] =
true;
1155 const int kMaxAggregation = 5;
1156 for (
int i = 0; i < kMaxAggregation; ++i) {
1159 IntegerValue max_magnitude(0);
1161 std::vector<ColIndex> col_candidates;
1162 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1163 if (dense_cut[
col] == 0)
continue;
1166 const int col_degree =
1168 if (col_degree <= 1)
continue;
1173 const IntegerVariable
var = integer_variables_[
col.value()];
1174 const double lp_value = expanded_lp_solution_[
var];
1177 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1178 if (bound_distance > 1e-2) {
1179 weights.push_back(bound_distance);
1180 col_candidates.push_back(
col);
1183 if (col_candidates.empty())
break;
1185 const ColIndex var_to_eliminate =
1186 col_candidates[std::discrete_distribution<>(weights.begin(),
1187 weights.end())(*random_)];
1190 std::vector<RowIndex> possible_rows;
1193 const RowIndex
row = entry.row();
1201 if (used_rows[
row])
continue;
1202 used_rows[
row] =
true;
1210 bool add_row =
false;
1213 if (entry.coefficient() > 0.0) {
1214 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1216 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1221 if (entry.coefficient() > 0.0) {
1222 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1224 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1229 weights.push_back(row_weights[
row]);
1232 if (possible_rows.empty())
break;
1234 const RowIndex row_to_combine =
1235 possible_rows[std::discrete_distribution<>(weights.begin(),
1236 weights.end())(*random_)];
1237 const IntegerValue to_combine_coeff =
1238 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1241 IntegerValue mult1 = -to_combine_coeff;
1242 IntegerValue mult2 = dense_cut[var_to_eliminate];
1249 const IntegerValue gcd = IntegerValue(
1259 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1262 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1264 std::abs(mult2.value()))) ==
1269 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1270 entry.second *= mult1;
1272 integer_multipliers.push_back({row_to_combine, mult2});
1275 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1276 integer_multipliers)) {
1282 if (i + 1 == kMaxAggregation)
break;
1284 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1285 dense_cut[
col] *= mult1;
1287 for (
const std::pair<ColIndex, IntegerValue> term :
1288 integer_lp_[row_to_combine].terms) {
1289 const ColIndex
col = term.first;
1290 const IntegerValue coeff = term.second;
1291 non_zeros_.Set(
col);
1292 dense_cut[
col] += coeff * mult2;
1298void LinearProgrammingConstraint::AddZeroHalfCuts() {
1301 tmp_lp_values_.clear();
1302 tmp_var_lbs_.clear();
1303 tmp_var_ubs_.clear();
1304 for (
const IntegerVariable
var : integer_variables_) {
1305 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1313 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1321 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1323 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1331 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1335void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1336 const int64_t min_iter,
const int64_t max_iter) {
1338 const int64_t num_degenerate_columns = CalculateDegeneracy();
1340 if (num_cols <= 0) {
1344 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1349 if (is_degenerate_) {
1350 next_simplex_iter_ /=
std::max(int64_t{1}, decrease_factor);
1352 next_simplex_iter_ *= 2;
1355 if (is_degenerate_) {
1356 next_simplex_iter_ /=
std::max(int64_t{1}, 2 * decrease_factor);
1360 next_simplex_iter_ = num_cols / 40;
1363 next_simplex_iter_ =
1368 UpdateBoundsOfLpVariables();
1373 if ( (
false) && objective_is_defined_) {
1382 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1383 100.0 * kCpEpsilon));
1392 parameters.set_max_number_of_iterations(2000);
1394 parameters.set_max_number_of_iterations(next_simplex_iter_);
1397 parameters.set_change_status_to_imprecise(
false);
1398 parameters.set_primal_feasibility_tolerance(1e-7);
1399 parameters.set_dual_feasibility_tolerance(1e-7);
1404 if (!SolveLp())
return true;
1407 const int max_cuts_rounds =
1413 cuts_round < max_cuts_rounds) {
1418 if (num_solves_ > 1) {
1421 expanded_lp_solution_);
1434 if (!cut_generators_.empty() &&
1437 for (
const CutGenerator& generator : cut_generators_) {
1438 if (!generator.generate_cuts(expanded_lp_solution_,
1439 &constraint_manager_)) {
1446 expanded_lp_solution_, &constraint_manager_);
1450 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1452 if (!CreateLpFromConstraintManager()) {
1456 if (!SolveLp())
return true;
1458 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1465 lp_at_level_zero_is_final_ =
true;
1474 if (!FillExactDualRayReason())
return true;
1483 UpdateSimplexIterationLimit(10, 1000);
1486 if (objective_is_defined_ &&
1492 if (!ExactLpReasonning())
return false;
1497 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1498 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1499 const IntegerValue propagated_lb =
1501 if (approximate_new_lb > propagated_lb) {
1502 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1504 <<
" ] approx_lb += "
1505 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1506 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1513 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1514 const double objective_cp_ub =
1517 ReducedCostStrengtheningDeductions(objective_cp_ub -
1518 relaxed_optimal_objective);
1519 if (!deductions_.empty()) {
1520 deductions_reason_ = integer_reason_;
1521 deductions_reason_.push_back(
1526 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1527 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1528 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1531 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1537 if (!deductions_.empty()) {
1538 const int trail_index_with_same_reason = integer_trail_->
Index();
1540 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1541 trail_index_with_same_reason)) {
1551 CHECK(lp_solution_is_set_);
1554 lp_solution_is_integer_ =
true;
1555 const int num_vars = integer_variables_.size();
1556 for (
int i = 0; i < num_vars; i++) {
1559 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1561 lp_solution_is_integer_ =
false;
1565 if (compute_reduced_cost_averages_) {
1566 UpdateAverageReducedCosts();
1572 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1574 compute_reduced_cost_averages_ &&
1576 count_since_last_branching_++;
1577 if (count_since_last_branching_ < branching_frequency_) {
1580 count_since_last_branching_ = 0;
1581 bool branching_successful =
false;
1584 const int max_num_branches = 3;
1585 const int num_vars = integer_variables_.size();
1586 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1587 for (
int i = 0; i < num_vars; ++i) {
1588 const IntegerVariable
var = integer_variables_[i];
1593 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1607 const double cost_i = rc_scores_[i];
1608 std::pair<double, IntegerVariable> branching_var =
1609 std::make_pair(-cost_i, positive_var);
1611 branching_vars.end(), branching_var);
1613 branching_vars.insert(iterator, branching_var);
1614 if (branching_vars.size() > max_num_branches) {
1615 branching_vars.resize(max_num_branches);
1619 for (
const std::pair<double, IntegerVariable>& branching_var :
1621 const IntegerVariable positive_var = branching_var.second;
1622 VLOG(2) <<
"Branching on: " << positive_var;
1623 if (BranchOnVar(positive_var)) {
1624 VLOG(2) <<
"Branching successful.";
1625 branching_successful =
true;
1630 if (!branching_successful) {
1631 branching_frequency_ *= 2;
1640IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1643 const int size = terms.
vars.size();
1644 for (
int i = 0; i < size; ++i) {
1645 const IntegerVariable
var = terms.
vars[i];
1646 const IntegerValue coeff = terms.
coeffs[i];
1655bool LinearProgrammingConstraint::PossibleOverflow(
1656 const LinearConstraint& constraint) {
1658 const int size = constraint.vars.size();
1659 for (
int i = 0; i < size; ++i) {
1660 const IntegerVariable
var = constraint.vars[i];
1661 const IntegerValue coeff = constraint.coeffs[i];
1663 const IntegerValue
bound = coeff > 0
1680absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1681 absl::int128 div128(positive_div.value());
1682 absl::int128 result = x / div128;
1683 if (result * div128 > x)
return result - 1;
1689void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1698 const int size = constraint->vars.size();
1699 for (
int i = 0; i < size; ++i) {
1700 const IntegerVariable
var = constraint->vars[i];
1701 const double coeff =
ToDouble(constraint->coeffs[i]);
1709 const double max_value =
std::max({sum_max, -sum_min, sum_max - sum_min});
1711 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1712 if (divisor <= 1)
return;
1727 absl::int128 adjust = 0;
1728 for (
int i = 0; i < size; ++i) {
1729 const IntegerValue old_coeff = constraint->coeffs[i];
1730 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1733 const absl::int128 remainder =
1734 absl::int128(old_coeff.value()) -
1735 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1741 if (new_coeff == 0)
continue;
1742 constraint->vars[new_size] = constraint->vars[i];
1743 constraint->coeffs[new_size] = new_coeff;
1746 constraint->vars.resize(new_size);
1747 constraint->coeffs.resize(new_size);
1749 constraint->ub = IntegerValue(
static_cast<int64_t
>(
1750 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1755void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1756 const LinearConstraint& terms, IntegerValue slack) {
1757 integer_reason_.clear();
1758 std::vector<IntegerValue> magnitudes;
1759 const int size = terms.vars.size();
1760 for (
int i = 0; i < size; ++i) {
1761 const IntegerVariable
var = terms.vars[i];
1762 const IntegerValue coeff = terms.coeffs[i];
1765 magnitudes.push_back(coeff);
1768 magnitudes.push_back(-coeff);
1779std::vector<std::pair<RowIndex, IntegerValue>>
1780LinearProgrammingConstraint::ScaleLpMultiplier(
1781 bool take_objective_into_account,
1782 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1784 double max_sum = 0.0;
1785 tmp_cp_multipliers_.clear();
1786 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1787 const RowIndex
row = p.first;
1792 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1808 tmp_cp_multipliers_.push_back({
row, cp_multi});
1809 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1814 if (take_objective_into_account) {
1815 max_sum +=
ToDouble(objective_infinity_norm_);
1819 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1820 if (max_sum == 0.0) {
1822 return integer_multipliers;
1827 const double threshold = std::ldexp(1, max_pow) / max_sum;
1828 if (threshold < 1.0) {
1831 return integer_multipliers;
1833 while (2 * *scaling <= threshold) *scaling *= 2;
1838 for (
const auto entry : tmp_cp_multipliers_) {
1839 const IntegerValue coeff(std::round(entry.second * (*scaling)));
1840 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1842 return integer_multipliers;
1845bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1846 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1847 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1850 scattered_vector->ClearAndResize(integer_variables_.size());
1854 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1855 const RowIndex
row = term.first;
1856 const IntegerValue multiplier = term.second;
1860 if (!scattered_vector->AddLinearExpressionMultiple(
1861 multiplier, integer_lp_[
row].terms)) {
1866 const IntegerValue
bound =
1867 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1875void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1876 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1877 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1878 const IntegerValue kMaxWantedCoeff(1e18);
1879 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1880 const RowIndex
row = term.first;
1881 const IntegerValue multiplier = term.second;
1882 if (multiplier == 0)
continue;
1886 IntegerValue negative_limit = kMaxWantedCoeff;
1887 IntegerValue positive_limit = kMaxWantedCoeff;
1891 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1892 if (multiplier > 0) {
1893 negative_limit =
std::min(negative_limit, multiplier);
1895 positive_limit =
std::min(positive_limit, -multiplier);
1900 const IntegerValue row_bound =
1901 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1902 if (row_bound != 0) {
1906 const IntegerValue limit2 =
1909 positive_limit =
std::min(positive_limit, limit1);
1910 negative_limit =
std::min(negative_limit, limit2);
1912 negative_limit =
std::min(negative_limit, limit1);
1913 positive_limit =
std::min(positive_limit, limit2);
1925 double positive_diff =
ToDouble(row_bound);
1926 double negative_diff =
ToDouble(row_bound);
1931 for (
const auto entry : integer_lp_[
row].terms) {
1932 const ColIndex
col = entry.first;
1933 const IntegerValue coeff = entry.second;
1934 const IntegerValue abs_coef =
IntTypeAbs(coeff);
1937 const IntegerVariable
var = integer_variables_[
col.value()];
1944 const IntegerValue current = (*scattered_vector)[
col];
1946 const IntegerValue overflow_limit(
1948 positive_limit =
std::min(positive_limit, overflow_limit);
1949 negative_limit =
std::min(negative_limit, overflow_limit);
1966 const IntegerValue current_magnitude =
IntTypeAbs(current);
1967 const IntegerValue other_direction_limit =
FloorRatio(
1969 ? kMaxWantedCoeff +
std::min(current_magnitude,
1971 : current_magnitude,
1973 const IntegerValue same_direction_limit(
FloorRatio(
1974 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
1976 if ((current > 0) == (coeff > 0)) {
1977 negative_limit =
std::min(negative_limit, other_direction_limit);
1978 positive_limit =
std::min(positive_limit, same_direction_limit);
1980 negative_limit =
std::min(negative_limit, same_direction_limit);
1981 positive_limit =
std::min(positive_limit, other_direction_limit);
1985 const IntegerValue implied = current > 0 ? lb : ub;
1996 IntegerValue to_add(0);
1997 if (positive_diff <= -1.0 && positive_limit > 0) {
1998 to_add = positive_limit;
2000 if (negative_diff >= 1.0 && negative_limit > 0) {
2003 std::abs(
ToDouble(negative_limit) * negative_diff) >
2004 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2005 to_add = -negative_limit;
2009 term.second += to_add;
2014 CHECK(scattered_vector->AddLinearExpressionMultiple(
2015 to_add, integer_lp_[
row].terms));
2034bool LinearProgrammingConstraint::ExactLpReasonning() {
2036 integer_reason_.clear();
2037 deductions_.clear();
2038 deductions_reason_.clear();
2044 std::vector<std::pair<RowIndex, double>> lp_multipliers;
2045 for (RowIndex
row(0);
row < num_rows; ++
row) {
2047 if (std::abs(
value) < kZeroTolerance)
continue;
2048 lp_multipliers.push_back({
row,
value});
2052 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2053 ScaleLpMultiplier(
true, lp_multipliers,
2057 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2059 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2065 const IntegerValue obj_scale(std::round(scaling));
2066 if (obj_scale == 0) {
2067 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2071 integer_objective_));
2073 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2078 LinearConstraint new_constraint;
2081 new_constraint.vars.push_back(objective_cp_);
2082 new_constraint.coeffs.push_back(-obj_scale);
2084 PreventOverflow(&new_constraint);
2085 DCHECK(!PossibleOverflow(new_constraint));
2089 if (new_constraint.vars.empty()) {
2091 return new_constraint.ub >= 0;
2094 IntegerSumLE* cp_constraint =
2095 new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
2096 new_constraint.ub, model_);
2100 optimal_constraints_.clear();
2102 optimal_constraints_.emplace_back(cp_constraint);
2103 rev_optimal_constraints_size_ = optimal_constraints_.size();
2104 if (!cp_constraint->PropagateAtLevelZero())
return false;
2105 return cp_constraint->Propagate();
2108bool LinearProgrammingConstraint::FillExactDualRayReason() {
2111 std::vector<std::pair<RowIndex, double>> lp_multipliers;
2112 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2114 if (std::abs(
value) < kZeroTolerance)
continue;
2115 lp_multipliers.push_back({
row,
value});
2117 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2118 ScaleLpMultiplier(
false, lp_multipliers,
2121 IntegerValue new_constraint_ub;
2122 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2123 &new_constraint_ub)) {
2124 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2128 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2129 &new_constraint_ub);
2131 LinearConstraint new_constraint;
2133 integer_variables_, new_constraint_ub, &new_constraint);
2135 PreventOverflow(&new_constraint);
2136 DCHECK(!PossibleOverflow(new_constraint));
2139 const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
2140 if (implied_lb <= new_constraint.ub) {
2141 VLOG(1) <<
"LP exact dual ray not infeasible,"
2142 <<
" implied_lb: " << implied_lb.value() / scaling
2143 <<
" ub: " << new_constraint.ub.value() / scaling;
2146 const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
2147 SetImpliedLowerBoundReason(new_constraint, slack);
2151int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2153 int num_non_basic_with_zero_rc = 0;
2154 for (glop::ColIndex i(0); i < num_vars; ++i) {
2156 if (rc != 0.0)
continue;
2160 num_non_basic_with_zero_rc++;
2163 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2164 return num_non_basic_with_zero_rc;
2167void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2168 double cp_objective_delta) {
2169 deductions_.clear();
2174 const double lp_objective_delta =
2176 const int num_vars = integer_variables_.size();
2177 for (
int i = 0; i < num_vars; i++) {
2178 const IntegerVariable cp_var = integer_variables_[i];
2179 const glop::ColIndex lp_var = glop::ColIndex(i);
2183 if (rc == 0.0)
continue;
2184 const double lp_other_bound =
value + lp_objective_delta / rc;
2185 const double cp_other_bound =
2188 if (rc > kLpEpsilon) {
2190 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2195 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2198 }
else if (rc < -kLpEpsilon) {
2200 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2202 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2203 deductions_.push_back(
2218 int num_nodes,
int subset_size,
const std::vector<bool>& in_subset,
2219 const std::vector<int>& tails,
const std::vector<int>& heads,
2220 const std::vector<Literal>& literals,
2221 const std::vector<double>& literal_lp_values, int64_t rhs_lower_bound,
2223 LinearConstraintManager* manager, Model*
model) {
2230 int num_optional_nodes_in = 0;
2231 int num_optional_nodes_out = 0;
2232 int optional_loop_in = -1;
2233 int optional_loop_out = -1;
2234 for (
int i = 0; i < tails.size(); ++i) {
2235 if (tails[i] != heads[i])
continue;
2236 if (in_subset[tails[i]]) {
2237 num_optional_nodes_in++;
2238 if (optional_loop_in == -1 ||
2239 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2240 optional_loop_in = i;
2243 num_optional_nodes_out++;
2244 if (optional_loop_out == -1 ||
2245 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2246 optional_loop_out = i;
2253 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2255 rhs_lower_bound = 1;
2258 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2260 double sum_outgoing = 0.0;
2263 for (
int i = 0; i < tails.size(); ++i) {
2264 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2265 sum_outgoing += literal_lp_values[i];
2266 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2271 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2273 if (num_optional_nodes_in == subset_size &&
2274 (optional_loop_in == -1 ||
2275 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2278 if (num_optional_nodes_out == num_nodes - subset_size &&
2279 (optional_loop_out == -1 ||
2280 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2285 if (num_optional_nodes_in == subset_size) {
2287 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2288 sum_outgoing += literal_lp_values[optional_loop_in];
2292 if (num_optional_nodes_out == num_nodes - subset_size) {
2293 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2295 sum_outgoing += literal_lp_values[optional_loop_out];
2299 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2300 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2313 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2314 const std::vector<Literal>& literals,
2316 absl::Span<const int64_t> demands, int64_t
capacity,
2318 if (num_nodes <= 2)
return;
2327 std::vector<Arc> relevant_arcs;
2330 std::vector<double> literal_lp_values(literals.size());
2331 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2333 for (
int i = 0; i < literals.size(); ++i) {
2335 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2337 lp_value = lp_values[direct_view];
2340 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2342 literal_lp_values[i] = lp_value;
2344 if (lp_value < 1e-6)
continue;
2345 relevant_arcs.
push_back({tails[i], heads[i], lp_value});
2346 arc_by_decreasing_lp_values.push_back({lp_value, i});
2348 std::sort(arc_by_decreasing_lp_values.begin(),
2349 arc_by_decreasing_lp_values.end(),
2350 std::greater<std::pair<double, int>>());
2360 int num_components = num_nodes;
2361 std::vector<int> parent(num_nodes);
2362 std::vector<int> root(num_nodes);
2363 for (
int i = 0; i < num_nodes; ++i) {
2367 auto get_root_and_compress_path = [&root](
int node) {
2369 while (root[r] != r) r = root[r];
2370 while (root[node] != r) {
2371 const int next = root[node];
2377 for (
const auto pair : arc_by_decreasing_lp_values) {
2378 if (num_components == 2)
break;
2379 const int tail = get_root_and_compress_path(tails[pair.second]);
2380 const int head = get_root_and_compress_path(heads[pair.second]);
2384 const int new_node = parent.size();
2385 parent.push_back(new_node);
2386 parent[
head] = new_node;
2387 parent[
tail] = new_node;
2391 root.push_back(new_node);
2392 root[
head] = new_node;
2393 root[
tail] = new_node;
2404 std::vector<int> pre_order(num_nodes);
2405 std::vector<absl::Span<const int>> subsets;
2407 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2408 for (
int i = 0; i < parent.size(); ++i) {
2409 if (parent[i] != i) graph[parent[i]].push_back(i);
2411 std::vector<int> queue;
2412 std::vector<bool> seen(graph.size(),
false);
2413 std::vector<int> start_index(parent.size());
2414 for (
int i = num_nodes; i < parent.size(); ++i) {
2418 CHECK(graph[i].empty() || graph[i].size() == 2);
2419 if (parent[i] != i)
continue;
2424 while (!queue.empty()) {
2425 const int node = queue.back();
2430 const int start = start_index[node];
2431 if (new_size - start > 1) {
2432 subsets.emplace_back(&pre_order[start], new_size - start);
2437 start_index[node] = new_size;
2438 if (node < num_nodes) pre_order[new_size++] = node;
2439 for (
const int child : graph[node]) {
2440 if (!seen[child]) queue.push_back(child);
2448 int64_t total_demands = 0;
2449 if (!demands.empty()) {
2450 for (
const int64_t
demand : demands) total_demands +=
demand;
2454 CHECK_EQ(pre_order.size(), num_nodes);
2455 std::vector<bool> in_subset(num_nodes,
false);
2456 for (
const absl::Span<const int> subset : subsets) {
2458 CHECK_LT(subset.size(), num_nodes);
2461 bool contain_depot =
false;
2462 int64_t subset_demand = 0;
2465 for (
const int n : subset) {
2466 in_subset[n] =
true;
2467 if (!demands.empty()) {
2468 if (n == 0) contain_depot =
true;
2469 subset_demand += demands[n];
2486 int64_t min_outgoing_flow = 1;
2487 if (!demands.empty()) {
2497 min_outgoing_flow =
std::max(min_outgoing_flow, int64_t{1});
2509 double outgoing_flow = 0.0;
2510 for (
const auto arc : relevant_arcs) {
2511 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2512 outgoing_flow += arc.lp_value;
2517 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2518 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2519 literals, literal_lp_values,
2520 min_outgoing_flow, lp_values, manager,
2525 for (
const int n : subset) in_subset[n] =
false;
2532std::vector<IntegerVariable> GetAssociatedVariables(
2533 const std::vector<Literal>& literals, Model*
model) {
2534 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2535 std::vector<IntegerVariable> result;
2536 for (
const Literal l : literals) {
2537 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2539 result.push_back(direct_view);
2541 result.push_back(encoder->GetLiteralView(l.Negated()));
2554 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2555 const std::vector<Literal>& literals,
Model*
model) {
2557 result.
vars = GetAssociatedVariables(literals,
model);
2559 [num_nodes, tails, heads, literals,
model](
2563 num_nodes, tails, heads, literals, lp_values,
2564 {}, 0, manager,
model);
2571 const std::vector<int>& tails,
2572 const std::vector<int>& heads,
2573 const std::vector<Literal>& literals,
2574 const std::vector<int64_t>& demands,
2577 result.
vars = GetAssociatedVariables(literals,
model);
2579 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2583 lp_values, demands,
capacity, manager,
2590std::function<IntegerLiteral()>
2593 std::vector<IntegerVariable> variables;
2594 for (IntegerVariable
var : integer_variables_) {
2597 variables.push_back(
var);
2600 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2603 return [
this, variables]() {
2607 double fractional_distance_best = -1.0;
2608 for (
const IntegerVariable
var : variables) {
2613 if (lb == ub)
continue;
2617 const double fractional_distance =
2619 lp_value - std::floor(lp_value +
kEpsilon));
2620 if (fractional_distance <
kEpsilon)
continue;
2623 if (fractional_distance > fractional_distance_best) {
2624 fractional_var =
var;
2625 fractional_distance_best = fractional_distance;
2639 std::vector<IntegerVariable> variables;
2640 for (IntegerVariable
var : integer_variables_) {
2643 variables.push_back(
var);
2646 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2652 const int num_vars = variables.size();
2653 std::vector<double> cost_to_zero(num_vars, 0.0);
2654 std::vector<int> num_cost_to_zero(num_vars);
2657 return [=]()
mutable {
2662 if (num_calls == 10000) {
2663 for (
int i = 0; i < num_vars; i++) {
2664 cost_to_zero[i] /= 2;
2665 num_cost_to_zero[i] /= 2;
2671 for (
int i = 0; i < num_vars; i++) {
2672 const IntegerVariable
var = variables[i];
2677 if (lb == ub)
continue;
2681 if (std::abs(rc) <
kEpsilon)
continue;
2684 if (
value == 1.0 && rc < 0.0) {
2685 cost_to_zero[i] -= rc;
2686 num_cost_to_zero[i]++;
2691 int selected_index = -1;
2692 double best_cost = 0.0;
2693 for (
int i = 0; i < num_vars; i++) {
2694 const IntegerVariable
var = variables[i];
2699 if (num_cost_to_zero[i] > 0 &&
2700 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2701 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2706 if (selected_index >= 0) {
2714void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2715 const int num_vars = integer_variables_.size();
2716 if (sum_cost_down_.size() < num_vars) {
2717 sum_cost_down_.resize(num_vars, 0.0);
2718 num_cost_down_.resize(num_vars, 0);
2719 sum_cost_up_.resize(num_vars, 0.0);
2720 num_cost_up_.resize(num_vars, 0);
2721 rc_scores_.resize(num_vars, 0.0);
2725 num_calls_since_reduced_cost_averages_reset_++;
2726 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2727 for (
int i = 0; i < num_vars; i++) {
2728 sum_cost_up_[i] /= 2;
2729 num_cost_up_[i] /= 2;
2730 sum_cost_down_[i] /= 2;
2731 num_cost_down_[i] /= 2;
2733 num_calls_since_reduced_cost_averages_reset_ = 0;
2737 for (
int i = 0; i < num_vars; i++) {
2738 const IntegerVariable
var = integer_variables_[i];
2745 const double rc = lp_reduced_cost_[i];
2746 if (std::abs(rc) < kCpEpsilon)
continue;
2749 sum_cost_down_[i] -= rc;
2750 num_cost_down_[i]++;
2752 sum_cost_up_[i] += rc;
2759 rc_rev_int_repository_.
SetLevel(0);
2765 positions_by_decreasing_rc_score_.clear();
2766 for (
int i = 0; i < num_vars; i++) {
2771 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2772 const double a_down =
2773 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2774 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2775 rc_scores_[i] =
std::min(a_up, a_down);
2777 rc_scores_[i] = 0.5 * (a_down + a_up);
2782 if (rc_scores_[i] > 0.0) {
2783 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2786 std::sort(positions_by_decreasing_rc_score_.begin(),
2787 positions_by_decreasing_rc_score_.end());
2791std::function<IntegerLiteral()>
2793 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2796IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2798 int selected_index = -1;
2799 const int size = positions_by_decreasing_rc_score_.size();
2800 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2801 for (
int i = rev_rc_start_; i < size; ++i) {
2802 const int index = positions_by_decreasing_rc_score_[i].second;
2803 const IntegerVariable
var = integer_variables_[
index];
2806 selected_index =
index;
2811 if (selected_index == -1)
return IntegerLiteral();
2812 const IntegerVariable
var = integer_variables_[selected_index];
2819 const IntegerValue value_ceil(
2821 if (value_ceil >= ub) {
2828 const IntegerValue value_floor(
2830 if (value_floor <= lb) {
2837 num_cost_up_[selected_index] > 0
2838 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2840 const double a_down =
2841 num_cost_down_[selected_index] > 0
2842 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2844 if (a_down < a_up) {
2852 std::string result =
"LP statistics:\n";
2853 absl::StrAppend(&result,
" final dimension: ",
DimensionString(),
"\n");
2854 absl::StrAppend(&result,
" total number of simplex iterations: ",
2855 total_num_simplex_iterations_,
"\n");
2856 absl::StrAppend(&result,
" num solves: \n");
2857 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
2858 if (num_solves_by_status_[i] == 0)
continue;
2859 absl::StrAppend(&result,
" - #",
2861 num_solves_by_status_[i],
"\n");
2863 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.
bool polish_lp_solution() const
bool add_mir_cuts() const
::PROTOBUF_NAMESPACE_ID::int32 max_cut_rounds_at_level_zero() const
::PROTOBUF_NAMESPACE_ID::int32 linearization_level() const
::PROTOBUF_NAMESPACE_ID::int32 max_integer_rounding_scaling() 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
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)