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];
145 const int size = dense_vector_.
size();
146 for (glop::ColIndex
col(0);
col < size; ++
col) {
147 const IntegerValue coeff = dense_vector_[
col];
157 : constraint_manager_(
model),
158 sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
165 implied_bounds_processor_({}, integer_trail_,
168 expanded_lp_solution_(
174 if (sat_parameters_.use_branching_in_lp() ||
175 sat_parameters_.search_branching() == SatParameters::LP_SEARCH) {
176 compute_reduced_cost_averages_ =
true;
180 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
184 VLOG(1) <<
"Total number of simplex iterations: "
185 << total_num_simplex_iterations_;
186 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
187 if (num_solves_by_status_[i] == 0)
continue;
189 << num_solves_by_status_[i];
195 DCHECK(!lp_constraint_is_registered_);
196 constraint_manager_.
Add(
ct);
203 for (
const IntegerVariable
var :
ct.vars) {
208 glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
209 IntegerVariable positive_variable) {
211 const auto it = mirror_lp_variable_.find(positive_variable);
212 if (it == mirror_lp_variable_.end()) {
213 const glop::ColIndex
col(integer_variables_.size());
215 mirror_lp_variable_[positive_variable] =
col;
216 integer_variables_.push_back(positive_variable);
217 lp_solution_.push_back(std::numeric_limits<double>::infinity());
218 lp_reduced_cost_.push_back(0.0);
219 (*dispatcher_)[positive_variable] =
this;
223 if (
index >= expanded_lp_solution_.
size()) {
232 IntegerValue coeff) {
233 CHECK(!lp_constraint_is_registered_);
234 objective_is_defined_ =
true;
236 if (ivar != pos_var) coeff = -coeff;
239 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
240 integer_objective_.push_back({
col, coeff});
241 objective_infinity_norm_ =
258 bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
261 infinity_norms_.
clear();
262 const auto& all_constraints = constraint_manager_.
AllConstraints();
266 integer_lp_.
push_back(LinearConstraintInternal());
267 LinearConstraintInternal& new_ct = integer_lp_.
back();
270 const int size =
ct.vars.size();
271 IntegerValue infinity_norm(0);
273 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
282 for (
int i = 0; i < size; ++i) {
284 IntegerVariable
var =
ct.vars[i];
285 IntegerValue coeff =
ct.coeffs[i];
291 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var), coeff});
293 infinity_norms_.
push_back(infinity_norm);
296 std::sort(new_ct.terms.begin(), new_ct.terms.end());
301 for (
int i = 0; i < integer_variables_.size(); ++i) {
308 objective_infinity_norm_ = 0;
309 for (
const auto entry : integer_objective_) {
310 const IntegerVariable
var = integer_variables_[entry.first.value()];
312 integer_objective_offset_ +=
316 objective_infinity_norm_ =
318 integer_objective_[new_size++] = entry;
321 objective_infinity_norm_ =
323 integer_objective_.resize(new_size);
326 for (
const LinearConstraintInternal&
ct : integer_lp_) {
329 for (
const auto& term :
ct.terms) {
341 const int num_vars = integer_variables_.size();
342 for (
int i = 0; i < num_vars; i++) {
343 const IntegerVariable cp_var = integer_variables_[i];
351 glop::GlopParameters params;
352 params.set_cost_scaling(glop::GlopParameters::MEAN_COST_SCALING);
353 scaler_.
Scale(params, &lp_data_);
354 UpdateBoundsOfLpVariables();
359 if (sat_parameters_.polish_lp_solution()) {
361 for (
int i = 0; i < num_vars; ++i) {
362 const IntegerVariable cp_var = integer_variables_[i];
365 if (lb != 0 || ub != 1)
continue;
376 <<
" Managed constraints.";
380 LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
382 glop::BasisState basis_state = simplex_.
GetState();
384 const glop::Status status = simplex_.
Solve(lp_data_, time_limit_);
388 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
397 info.new_obj_bound = IntegerValue(
398 static_cast<int64_t
>(std::ceil(info.lp_objective - kCpEpsilon)));
403 void LinearProgrammingConstraint::FillReducedCostReasonIn(
405 std::vector<IntegerLiteral>* integer_reason) {
406 integer_reason->clear();
407 const int num_vars = integer_variables_.size();
408 for (
int i = 0; i < num_vars; i++) {
409 const double rc = reduced_costs[glop::ColIndex(i)];
410 if (rc > kLpEpsilon) {
411 integer_reason->push_back(
413 }
else if (rc < -kLpEpsilon) {
414 integer_reason->push_back(
422 bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
424 DCHECK(lp_solution_is_set_);
426 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
429 integer_reason_.clear();
431 bool deductions_were_made =
false;
433 UpdateBoundsOfLpVariables();
435 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
439 const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
443 if (current_value < current_lb || current_value > current_ub) {
448 const double new_ub = std::floor(current_value);
451 LPSolveInfo lower_branch_info = SolveLpForBranching();
461 positive_var, IntegerValue(std::ceil(current_value)));
462 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
465 deductions_were_made =
true;
466 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
471 const double new_lb = std::ceil(current_value);
474 LPSolveInfo upper_branch_info = SolveLpForBranching();
478 return deductions_were_made;
485 positive_var, IntegerValue(std::floor(current_value)));
486 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
487 return deductions_were_made;
489 deductions_were_made =
true;
491 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
492 return deductions_were_made;
501 approximate_obj_lb = upper_branch_info.new_obj_bound;
503 approximate_obj_lb = lower_branch_info.new_obj_bound;
505 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
506 upper_branch_info.new_obj_bound);
511 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
514 const IntegerLiteral deduction =
516 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
517 return deductions_were_made;
524 DCHECK(!lp_constraint_is_registered_);
525 lp_constraint_is_registered_ =
true;
530 std::sort(integer_objective_.begin(), integer_objective_.end());
533 if (!sat_parameters_.add_lp_constraints_lazily()) {
536 if (!CreateLpFromConstraintManager()) {
542 const int watcher_id = watcher->
Register(
this);
543 const int num_vars = integer_variables_.size();
544 for (
int i = 0; i < num_vars; i++) {
547 if (objective_is_defined_) {
560 optimal_constraints_.resize(rev_optimal_constraints_size_);
561 if (lp_solution_is_set_ && level < lp_solution_level_) {
562 lp_solution_is_set_ =
false;
570 if (level == 0 && !level_zero_lp_solution_.empty()) {
571 lp_solution_is_set_ =
true;
572 lp_solution_ = level_zero_lp_solution_;
573 lp_solution_level_ = 0;
574 for (
int i = 0; i < lp_solution_.size(); i++) {
575 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
576 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
583 for (
const IntegerVariable
var : generator.
vars) {
586 cut_generators_.push_back(std::move(generator));
590 const std::vector<int>& watch_indices) {
591 if (!lp_solution_is_set_)
return Propagate();
601 for (
const int index : watch_indices) {
607 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
622 glop::ColIndex
var) {
627 IntegerVariable variable)
const {
628 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
632 IntegerVariable variable)
const {
633 return lp_reduced_cost_[
gtl::FindOrDie(mirror_lp_variable_, variable)
637 void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
638 const int num_vars = integer_variables_.size();
639 for (
int i = 0; i < num_vars; i++) {
640 const IntegerVariable cp_var = integer_variables_[i];
648 bool LinearProgrammingConstraint::SolveLp() {
650 lp_at_level_zero_is_final_ =
false;
653 const auto status = simplex_.
Solve(lp_data_, time_limit_);
656 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
660 average_degeneracy_.
AddData(CalculateDegeneracy());
662 VLOG(2) <<
"High average degeneracy: "
667 if (status_as_int >= num_solves_by_status_.size()) {
668 num_solves_by_status_.resize(status_as_int + 1);
670 num_solves_by_status_[status_as_int]++;
677 lp_solution_is_set_ =
true;
679 const int num_vars = integer_variables_.size();
680 for (
int i = 0; i < num_vars; i++) {
682 GetVariableValueAtCpScale(glop::ColIndex(i));
683 lp_solution_[i] =
value;
684 expanded_lp_solution_[integer_variables_[i]] =
value;
688 if (lp_solution_level_ == 0) {
689 level_zero_lp_solution_ = lp_solution_;
695 bool LinearProgrammingConstraint::AddCutFromConstraints(
696 const std::string&
name,
697 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
708 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
710 VLOG(1) <<
"Issue, overflow!";
729 if (std::abs(activity -
ToDouble(cut_.
ub)) / norm > 1e-4) {
730 VLOG(1) <<
"Cut not tight " << activity <<
" <= " <<
ToDouble(cut_.
ub);
740 const IntegerVariable first_new_var(expanded_lp_solution_.
size());
741 CHECK_EQ(first_new_var.value() % 2, 0);
743 LinearConstraint copy_in_debug;
745 copy_in_debug = cut_;
755 std::vector<ImpliedBoundsProcessor::SlackInfo> ib_slack_infos;
757 false, first_new_var,
758 expanded_lp_solution_, &cut_, &ib_slack_infos);
760 cut_, ib_slack_infos));
768 tmp_lp_values_.clear();
769 tmp_var_lbs_.clear();
770 tmp_var_ubs_.clear();
771 for (
const IntegerVariable
var : cut_.
vars) {
772 if (
var >= first_new_var) {
775 ib_slack_infos[(
var.value() - first_new_var.value()) / 2];
776 tmp_lp_values_.push_back(info.lp_value);
777 tmp_var_lbs_.push_back(info.lb);
778 tmp_var_ubs_.push_back(info.ub);
780 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
788 const IntegerVariable first_slack(first_new_var +
789 IntegerVariable(2 * ib_slack_infos.size()));
790 tmp_slack_rows_.clear();
791 tmp_slack_bounds_.clear();
792 for (
const auto pair : integer_multipliers) {
793 const RowIndex
row = pair.first;
794 const IntegerValue coeff = pair.second;
798 tmp_lp_values_.push_back(0.0);
799 cut_.
vars.push_back(first_slack +
800 2 * IntegerVariable(tmp_slack_rows_.size()));
801 tmp_slack_rows_.push_back(
row);
802 cut_.
coeffs.push_back(coeff);
804 const IntegerValue diff(
805 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value()));
807 tmp_slack_bounds_.push_back(integer_lp_[
row].ub);
808 tmp_var_lbs_.push_back(IntegerValue(0));
809 tmp_var_ubs_.push_back(diff);
811 tmp_slack_bounds_.push_back(integer_lp_[
row].lb);
812 tmp_var_lbs_.push_back(-diff);
813 tmp_var_ubs_.push_back(IntegerValue(0));
817 bool at_least_one_added =
false;
823 at_least_one_added |= PostprocessAndAddCut(
824 absl::StrCat(
name,
"_K"), cover_cut_helper_.
Info(), first_new_var,
825 first_slack, ib_slack_infos, cover_cut_helper_.
mutable_cut());
831 RoundingOptions options;
832 options.max_scaling = sat_parameters_.max_integer_rounding_scaling();
833 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
834 tmp_var_lbs_, tmp_var_ubs_,
835 &implied_bounds_processor_, &cut_);
836 at_least_one_added |= PostprocessAndAddCut(
838 absl::StrCat(
"num_lifted_booleans=",
840 first_new_var, first_slack, ib_slack_infos, &cut_);
842 return at_least_one_added;
845 bool LinearProgrammingConstraint::PostprocessAndAddCut(
846 const std::string&
name,
const std::string& info,
847 IntegerVariable first_new_var, IntegerVariable first_slack,
848 const std::vector<ImpliedBoundsProcessor::SlackInfo>& ib_slack_infos,
849 LinearConstraint* cut) {
853 double activity = 0.0;
854 for (
int i = 0; i < cut->vars.size(); ++i) {
855 if (cut->vars[i] < first_new_var) {
857 ToDouble(cut->coeffs[i]) * expanded_lp_solution_[cut->vars[i]];
860 const double kMinViolation = 1e-4;
861 const double violation = activity -
ToDouble(cut->ub);
862 if (violation < kMinViolation) {
863 VLOG(3) <<
"Bad cut " << activity <<
" <= " <<
ToDouble(cut->ub);
871 IntegerValue cut_ub = cut->ub;
872 bool overflow =
false;
873 for (
int i = 0; i < cut->vars.size(); ++i) {
874 const IntegerVariable
var = cut->vars[i];
877 if (
var < first_new_var) {
878 const glop::ColIndex
col =
881 tmp_scattered_vector_.
Add(
col, cut->coeffs[i]);
883 tmp_scattered_vector_.
Add(
col, -cut->coeffs[i]);
889 if (
var < first_slack) {
890 const IntegerValue multiplier = cut->coeffs[i];
891 const int index = (
var.value() - first_new_var.value()) / 2;
894 std::vector<std::pair<ColIndex, IntegerValue>> terms;
895 for (
const std::pair<IntegerVariable, IntegerValue>& term :
896 ib_slack_infos[
index].terms) {
916 const int slack_index = (
var.value() - first_slack.value()) / 2;
917 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
918 const IntegerValue multiplier = -cut->coeffs[i];
920 multiplier, integer_lp_[
row].terms)) {
926 if (!
AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
933 VLOG(1) <<
"Overflow in slack removal.";
937 VLOG(3) <<
" num_slack: " << num_slack;
943 const std::string extra_info =
944 absl::StrCat(info,
" num_ib_substitutions=", ib_slack_infos.size());
946 const double new_violation =
948 if (std::abs(violation - new_violation) >= 1e-4) {
949 VLOG(1) <<
"Violation discrepancy after slack removal. "
950 <<
" before = " << violation <<
" after = " << new_violation;
954 return constraint_manager_.
AddCut(*cut,
name, expanded_lp_solution_,
962 void LinearProgrammingConstraint::AddCGCuts() {
964 std::vector<std::pair<RowIndex, double>> lp_multipliers;
965 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
966 for (RowIndex
row(0);
row < num_rows; ++
row) {
968 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
976 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
980 if (basis_col >= integer_variables_.size())
continue;
985 double magnitude = 0.0;
986 lp_multipliers.clear();
988 if (lambda.non_zeros.empty()) {
989 for (RowIndex
row(0);
row < num_rows; ++
row) {
991 if (std::abs(
value) < kZeroTolerance)
continue;
997 VLOG(1) <<
"BASIC row not expected! " <<
value;
1002 lp_multipliers.push_back({
row,
value});
1005 for (
const ColIndex
col : lambda.non_zeros) {
1007 const double value = lambda.values[
col];
1008 if (std::abs(
value) < kZeroTolerance)
continue;
1012 VLOG(1) <<
"BASIC row not expected! " <<
value;
1017 lp_multipliers.push_back({
row,
value});
1020 if (lp_multipliers.empty())
continue;
1023 for (
int i = 0; i < 2; ++i) {
1029 for (std::pair<RowIndex, double>& p : lp_multipliers) {
1030 p.second = -p.second;
1036 integer_multipliers =
1037 ScaleLpMultiplier(
false,
1038 lp_multipliers, &scaling, 52);
1039 AddCutFromConstraints(
"CG", integer_multipliers);
1047 void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
1048 ModelRandomGenerator* random,
1049 std::vector<std::pair<RowIndex, RowIndex>>* output) {
1050 if (
a.empty() ||
b.empty())
return;
1051 for (
const RowIndex
row :
a) {
1052 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
1054 output->push_back({
row, other});
1059 template <
class ListOfTerms>
1060 IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1061 for (
const auto& term : terms) {
1062 if (term.first ==
col)
return term.second;
1064 return IntegerValue(0);
1069 void LinearProgrammingConstraint::AddMirCuts() {
1085 integer_variables_.size(), IntegerValue(0));
1086 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1091 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1093 for (RowIndex
row(0);
row < num_rows; ++
row) {
1100 base_rows.push_back({
row, IntegerValue(1)});
1104 base_rows.push_back({
row, IntegerValue(-1)});
1125 std::vector<double> weights;
1127 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1128 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1138 integer_multipliers = {entry};
1139 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1144 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1145 dense_cut[
col] = IntegerValue(0);
1147 non_zeros_.SparseClearAll();
1150 const IntegerValue multiplier = entry.second;
1151 for (
const std::pair<ColIndex, IntegerValue> term :
1152 integer_lp_[entry.first].terms) {
1153 const ColIndex
col = term.first;
1154 const IntegerValue coeff = term.second;
1155 non_zeros_.Set(
col);
1156 dense_cut[
col] += coeff * multiplier;
1159 used_rows.
assign(num_rows.value(),
false);
1160 used_rows[entry.first] =
true;
1165 const int kMaxAggregation = 5;
1166 for (
int i = 0; i < kMaxAggregation; ++i) {
1169 IntegerValue max_magnitude(0);
1171 std::vector<ColIndex> col_candidates;
1172 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1173 if (dense_cut[
col] == 0)
continue;
1176 const int col_degree =
1178 if (col_degree <= 1)
continue;
1183 const IntegerVariable
var = integer_variables_[
col.value()];
1184 const double lp_value = expanded_lp_solution_[
var];
1187 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1188 if (bound_distance > 1e-2) {
1189 weights.push_back(bound_distance);
1190 col_candidates.push_back(
col);
1193 if (col_candidates.empty())
break;
1195 const ColIndex var_to_eliminate =
1196 col_candidates[std::discrete_distribution<>(weights.begin(),
1197 weights.end())(*random_)];
1200 std::vector<RowIndex> possible_rows;
1203 const RowIndex
row = entry.row();
1211 if (used_rows[
row])
continue;
1212 used_rows[
row] =
true;
1220 bool add_row =
false;
1223 if (entry.coefficient() > 0.0) {
1224 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1226 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1231 if (entry.coefficient() > 0.0) {
1232 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1234 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1239 weights.push_back(row_weights[
row]);
1242 if (possible_rows.empty())
break;
1244 const RowIndex row_to_combine =
1245 possible_rows[std::discrete_distribution<>(weights.begin(),
1246 weights.end())(*random_)];
1247 const IntegerValue to_combine_coeff =
1248 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1251 IntegerValue mult1 = -to_combine_coeff;
1252 IntegerValue mult2 = dense_cut[var_to_eliminate];
1259 const IntegerValue gcd = IntegerValue(
1269 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1270 max_magnitude =
std::max(max_magnitude, entry.second);
1272 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1274 std::abs(mult2.value()))) ==
1279 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1280 entry.second *= mult1;
1282 integer_multipliers.push_back({row_to_combine, mult2});
1285 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1286 integer_multipliers)) {
1292 if (i + 1 == kMaxAggregation)
break;
1294 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1295 dense_cut[
col] *= mult1;
1297 for (
const std::pair<ColIndex, IntegerValue> term :
1298 integer_lp_[row_to_combine].terms) {
1299 const ColIndex
col = term.first;
1300 const IntegerValue coeff = term.second;
1301 non_zeros_.Set(
col);
1302 dense_cut[
col] += coeff * mult2;
1308 void LinearProgrammingConstraint::AddZeroHalfCuts() {
1311 tmp_lp_values_.clear();
1312 tmp_var_lbs_.clear();
1313 tmp_var_ubs_.clear();
1314 for (
const IntegerVariable
var : integer_variables_) {
1315 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1323 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1331 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1333 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1341 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1345 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1346 const int64_t min_iter,
const int64_t max_iter) {
1347 if (sat_parameters_.linearization_level() < 2)
return;
1348 const int64_t num_degenerate_columns = CalculateDegeneracy();
1350 if (num_cols <= 0) {
1354 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1359 if (is_degenerate_) {
1360 next_simplex_iter_ /=
std::max(int64_t{1}, decrease_factor);
1362 next_simplex_iter_ *= 2;
1365 if (is_degenerate_) {
1366 next_simplex_iter_ /=
std::max(int64_t{1}, 2 * decrease_factor);
1370 next_simplex_iter_ = num_cols / 40;
1373 next_simplex_iter_ =
1378 UpdateBoundsOfLpVariables();
1383 if ( (
false) && objective_is_defined_) {
1392 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1393 100.0 * kCpEpsilon));
1402 parameters.set_max_number_of_iterations(2000);
1404 parameters.set_max_number_of_iterations(next_simplex_iter_);
1406 if (sat_parameters_.use_exact_lp_reason()) {
1407 parameters.set_change_status_to_imprecise(
false);
1408 parameters.set_primal_feasibility_tolerance(1e-7);
1409 parameters.set_dual_feasibility_tolerance(1e-7);
1414 if (!SolveLp())
return true;
1417 const int max_cuts_rounds =
1419 ? sat_parameters_.max_cut_rounds_at_level_zero()
1423 cuts_round < max_cuts_rounds) {
1427 if (!integer_lp_.empty()) {
1430 expanded_lp_solution_);
1437 if (sat_parameters_.add_mir_cuts()) AddMirCuts();
1438 if (sat_parameters_.add_cg_cuts()) AddCGCuts();
1439 if (sat_parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1443 if (!cut_generators_.empty() &&
1445 !sat_parameters_.only_add_cuts_at_level_zero())) {
1446 for (
const CutGenerator& generator : cut_generators_) {
1447 generator.generate_cuts(expanded_lp_solution_, &constraint_manager_);
1452 expanded_lp_solution_, &constraint_manager_);
1456 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1458 if (!CreateLpFromConstraintManager()) {
1462 if (!SolveLp())
return true;
1464 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1471 lp_at_level_zero_is_final_ =
true;
1479 if (sat_parameters_.use_exact_lp_reason()) {
1480 if (!FillExactDualRayReason())
return true;
1489 UpdateSimplexIterationLimit(10, 1000);
1492 if (objective_is_defined_ &&
1499 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1500 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1504 if (sat_parameters_.use_exact_lp_reason()) {
1505 if (!ExactLpReasonning())
return false;
1508 const IntegerValue propagated_lb =
1510 if (approximate_new_lb > propagated_lb) {
1511 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1513 <<
" ] approx_lb += "
1514 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1515 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1518 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1519 const double objective_cp_ub =
1521 ReducedCostStrengtheningDeductions(objective_cp_ub -
1522 relaxed_optimal_objective);
1523 if (!deductions_.empty()) {
1524 deductions_reason_ = integer_reason_;
1525 deductions_reason_.push_back(
1530 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1533 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1539 if (!deductions_.empty()) {
1540 const int trail_index_with_same_reason = integer_trail_->
Index();
1542 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1543 trail_index_with_same_reason)) {
1553 CHECK(lp_solution_is_set_);
1556 lp_solution_is_integer_ =
true;
1557 const int num_vars = integer_variables_.size();
1558 for (
int i = 0; i < num_vars; i++) {
1561 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1563 lp_solution_is_integer_ =
false;
1567 if (compute_reduced_cost_averages_) {
1568 UpdateAverageReducedCosts();
1572 if (sat_parameters_.use_branching_in_lp() && objective_is_defined_ &&
1574 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1575 sat_parameters_.linearization_level() >= 2 &&
1576 compute_reduced_cost_averages_ &&
1578 count_since_last_branching_++;
1579 if (count_since_last_branching_ < branching_frequency_) {
1582 count_since_last_branching_ = 0;
1583 bool branching_successful =
false;
1586 const int max_num_branches = 3;
1587 const int num_vars = integer_variables_.size();
1588 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1589 for (
int i = 0; i < num_vars; ++i) {
1590 const IntegerVariable
var = integer_variables_[i];
1595 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1609 const double cost_i = rc_scores_[i];
1610 std::pair<double, IntegerVariable> branching_var =
1611 std::make_pair(-cost_i, positive_var);
1613 branching_vars.end(), branching_var);
1615 branching_vars.insert(iterator, branching_var);
1616 if (branching_vars.size() > max_num_branches) {
1617 branching_vars.resize(max_num_branches);
1621 for (
const std::pair<double, IntegerVariable>& branching_var :
1623 const IntegerVariable positive_var = branching_var.second;
1624 VLOG(2) <<
"Branching on: " << positive_var;
1625 if (BranchOnVar(positive_var)) {
1626 VLOG(2) <<
"Branching successful.";
1627 branching_successful =
true;
1632 if (!branching_successful) {
1633 branching_frequency_ *= 2;
1642 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1645 const int size = terms.
vars.size();
1646 for (
int i = 0; i < size; ++i) {
1647 const IntegerVariable
var = terms.
vars[i];
1648 const IntegerValue coeff = terms.
coeffs[i];
1657 bool LinearProgrammingConstraint::PossibleOverflow(
1658 const LinearConstraint& constraint) {
1660 const int size = constraint.vars.size();
1661 for (
int i = 0; i < size; ++i) {
1662 const IntegerVariable
var = constraint.vars[i];
1663 const IntegerValue coeff = constraint.coeffs[i];
1665 const IntegerValue
bound = coeff > 0
1682 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1683 absl::int128 div128(positive_div.value());
1684 absl::int128 result = x / div128;
1685 if (result * div128 > x)
return result - 1;
1691 void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1697 const int size = constraint->vars.size();
1698 for (
int i = 0; i < size; ++i) {
1699 const IntegerVariable
var = constraint->vars[i];
1700 const double coeff =
ToDouble(constraint->coeffs[i]);
1701 const double prod1 =
1703 const double prod2 =
1708 const double max_value =
std::max({sum_max, -sum_min, sum_max - sum_min});
1710 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1711 if (divisor <= 1)
return;
1726 absl::int128 adjust = 0;
1727 for (
int i = 0; i < size; ++i) {
1728 const IntegerValue old_coeff = constraint->coeffs[i];
1729 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1732 const absl::int128 remainder =
1733 absl::int128(old_coeff.value()) -
1734 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1740 if (new_coeff == 0)
continue;
1741 constraint->vars[new_size] = constraint->vars[i];
1742 constraint->coeffs[new_size] = new_coeff;
1745 constraint->vars.resize(new_size);
1746 constraint->coeffs.resize(new_size);
1748 constraint->ub = IntegerValue(
static_cast<int64_t
>(
1749 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1754 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1755 const LinearConstraint& terms, IntegerValue slack) {
1756 integer_reason_.clear();
1757 std::vector<IntegerValue> magnitudes;
1758 const int size = terms.vars.size();
1759 for (
int i = 0; i < size; ++i) {
1760 const IntegerVariable
var = terms.vars[i];
1761 const IntegerValue coeff = terms.coeffs[i];
1764 magnitudes.push_back(coeff);
1767 magnitudes.push_back(-coeff);
1778 std::vector<std::pair<RowIndex, IntegerValue>>
1779 LinearProgrammingConstraint::ScaleLpMultiplier(
1780 bool take_objective_into_account,
1781 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1783 double max_sum = 0.0;
1784 tmp_cp_multipliers_.clear();
1785 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1786 const RowIndex
row = p.first;
1791 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1807 tmp_cp_multipliers_.push_back({
row, cp_multi});
1808 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1813 if (take_objective_into_account) {
1814 max_sum +=
ToDouble(objective_infinity_norm_);
1818 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1819 if (max_sum == 0.0) {
1821 return integer_multipliers;
1826 const double threshold = std::ldexp(1, max_pow) / max_sum;
1827 if (threshold < 1.0) {
1830 return integer_multipliers;
1832 while (2 * *scaling <= threshold) *scaling *= 2;
1837 for (
const auto entry : tmp_cp_multipliers_) {
1838 const IntegerValue coeff(std::round(entry.second * (*scaling)));
1839 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1841 return integer_multipliers;
1844 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1845 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1846 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1849 scattered_vector->ClearAndResize(integer_variables_.size());
1853 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1854 const RowIndex
row = term.first;
1855 const IntegerValue multiplier = term.second;
1859 if (!scattered_vector->AddLinearExpressionMultiple(
1860 multiplier, integer_lp_[
row].terms)) {
1865 const IntegerValue
bound =
1866 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1874 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1875 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1876 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1877 const IntegerValue kMaxWantedCoeff(1e18);
1878 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1879 const RowIndex
row = term.first;
1880 const IntegerValue multiplier = term.second;
1881 if (multiplier == 0)
continue;
1885 IntegerValue negative_limit = kMaxWantedCoeff;
1886 IntegerValue positive_limit = kMaxWantedCoeff;
1890 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1891 if (multiplier > 0) {
1892 negative_limit =
std::min(negative_limit, multiplier);
1894 positive_limit =
std::min(positive_limit, -multiplier);
1899 const IntegerValue row_bound =
1900 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1901 if (row_bound != 0) {
1905 const IntegerValue limit2 =
1908 positive_limit =
std::min(positive_limit, limit1);
1909 negative_limit =
std::min(negative_limit, limit2);
1911 negative_limit =
std::min(negative_limit, limit1);
1912 positive_limit =
std::min(positive_limit, limit2);
1924 double positive_diff =
ToDouble(row_bound);
1925 double negative_diff =
ToDouble(row_bound);
1930 for (
const auto entry : integer_lp_[
row].terms) {
1931 const ColIndex
col = entry.first;
1932 const IntegerValue coeff = entry.second;
1933 const IntegerValue abs_coef =
IntTypeAbs(coeff);
1936 const IntegerVariable
var = integer_variables_[
col.value()];
1943 const IntegerValue current = (*scattered_vector)[
col];
1945 const IntegerValue overflow_limit(
1947 positive_limit =
std::min(positive_limit, overflow_limit);
1948 negative_limit =
std::min(negative_limit, overflow_limit);
1965 const IntegerValue current_magnitude =
IntTypeAbs(current);
1966 const IntegerValue other_direction_limit =
FloorRatio(
1968 ? kMaxWantedCoeff +
std::min(current_magnitude,
1970 : current_magnitude,
1972 const IntegerValue same_direction_limit(
FloorRatio(
1973 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
1975 if ((current > 0) == (coeff > 0)) {
1976 negative_limit =
std::min(negative_limit, other_direction_limit);
1977 positive_limit =
std::min(positive_limit, same_direction_limit);
1979 negative_limit =
std::min(negative_limit, same_direction_limit);
1980 positive_limit =
std::min(positive_limit, other_direction_limit);
1984 const IntegerValue implied = current > 0 ? lb : ub;
1995 IntegerValue to_add(0);
1996 if (positive_diff <= -1.0 && positive_limit > 0) {
1997 to_add = positive_limit;
1999 if (negative_diff >= 1.0 && negative_limit > 0) {
2002 std::abs(
ToDouble(negative_limit) * negative_diff) >
2003 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2004 to_add = -negative_limit;
2008 term.second += to_add;
2013 CHECK(scattered_vector->AddLinearExpressionMultiple(
2014 to_add, integer_lp_[
row].terms));
2033 bool LinearProgrammingConstraint::ExactLpReasonning() {
2035 integer_reason_.clear();
2036 deductions_.clear();
2037 deductions_reason_.clear();
2043 std::vector<std::pair<RowIndex, double>> lp_multipliers;
2044 for (RowIndex
row(0);
row < num_rows; ++
row) {
2046 if (std::abs(
value) < kZeroTolerance)
continue;
2047 lp_multipliers.push_back({
row,
value});
2051 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2052 ScaleLpMultiplier(
true, lp_multipliers,
2056 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2058 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2064 const IntegerValue obj_scale(std::round(scaling));
2065 if (obj_scale == 0) {
2066 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2070 integer_objective_));
2072 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2077 LinearConstraint new_constraint;
2080 new_constraint.vars.push_back(objective_cp_);
2081 new_constraint.coeffs.push_back(-obj_scale);
2083 PreventOverflow(&new_constraint);
2084 DCHECK(!PossibleOverflow(new_constraint));
2087 IntegerSumLE* cp_constraint =
2088 new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
2089 new_constraint.ub, model_);
2093 optimal_constraints_.clear();
2095 optimal_constraints_.emplace_back(cp_constraint);
2096 rev_optimal_constraints_size_ = optimal_constraints_.size();
2097 if (!cp_constraint->PropagateAtLevelZero())
return false;
2098 return cp_constraint->Propagate();
2101 bool LinearProgrammingConstraint::FillExactDualRayReason() {
2104 std::vector<std::pair<RowIndex, double>> lp_multipliers;
2105 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2107 if (std::abs(
value) < kZeroTolerance)
continue;
2108 lp_multipliers.push_back({
row,
value});
2110 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2111 ScaleLpMultiplier(
false, lp_multipliers,
2114 IntegerValue new_constraint_ub;
2115 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2116 &new_constraint_ub)) {
2117 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2121 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2122 &new_constraint_ub);
2124 LinearConstraint new_constraint;
2126 integer_variables_, new_constraint_ub, &new_constraint);
2128 PreventOverflow(&new_constraint);
2129 DCHECK(!PossibleOverflow(new_constraint));
2132 const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
2133 if (implied_lb <= new_constraint.ub) {
2134 VLOG(1) <<
"LP exact dual ray not infeasible,"
2135 <<
" implied_lb: " << implied_lb.value() / scaling
2136 <<
" ub: " << new_constraint.ub.value() / scaling;
2139 const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
2140 SetImpliedLowerBoundReason(new_constraint, slack);
2144 int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2146 int num_non_basic_with_zero_rc = 0;
2147 for (glop::ColIndex i(0); i < num_vars; ++i) {
2149 if (rc != 0.0)
continue;
2153 num_non_basic_with_zero_rc++;
2156 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2157 return num_non_basic_with_zero_rc;
2160 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2161 double cp_objective_delta) {
2162 deductions_.clear();
2167 const double lp_objective_delta =
2169 const int num_vars = integer_variables_.size();
2170 for (
int i = 0; i < num_vars; i++) {
2171 const IntegerVariable cp_var = integer_variables_[i];
2172 const glop::ColIndex lp_var = glop::ColIndex(i);
2176 if (rc == 0.0)
continue;
2177 const double lp_other_bound =
value + lp_objective_delta / rc;
2178 const double cp_other_bound =
2181 if (rc > kLpEpsilon) {
2183 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2188 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2191 }
else if (rc < -kLpEpsilon) {
2193 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2195 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2196 deductions_.push_back(
2210 void AddOutgoingCut(
2211 int num_nodes,
int subset_size,
const std::vector<bool>& in_subset,
2212 const std::vector<int>& tails,
const std::vector<int>& heads,
2213 const std::vector<Literal>& literals,
2214 const std::vector<double>& literal_lp_values, int64_t rhs_lower_bound,
2216 LinearConstraintManager* manager, Model*
model) {
2223 int num_optional_nodes_in = 0;
2224 int num_optional_nodes_out = 0;
2225 int optional_loop_in = -1;
2226 int optional_loop_out = -1;
2227 for (
int i = 0; i < tails.size(); ++i) {
2228 if (tails[i] != heads[i])
continue;
2229 if (in_subset[tails[i]]) {
2230 num_optional_nodes_in++;
2231 if (optional_loop_in == -1 ||
2232 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2233 optional_loop_in = i;
2236 num_optional_nodes_out++;
2237 if (optional_loop_out == -1 ||
2238 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2239 optional_loop_out = i;
2246 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2248 rhs_lower_bound = 1;
2251 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2253 double sum_outgoing = 0.0;
2256 for (
int i = 0; i < tails.size(); ++i) {
2257 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2258 sum_outgoing += literal_lp_values[i];
2259 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2264 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2266 if (num_optional_nodes_in == subset_size &&
2267 (optional_loop_in == -1 ||
2268 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2271 if (num_optional_nodes_out == num_nodes - subset_size &&
2272 (optional_loop_out == -1 ||
2273 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2278 if (num_optional_nodes_in == subset_size) {
2280 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2281 sum_outgoing += literal_lp_values[optional_loop_in];
2285 if (num_optional_nodes_out == num_nodes - subset_size) {
2286 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2288 sum_outgoing += literal_lp_values[optional_loop_out];
2292 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2293 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2306 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2307 const std::vector<Literal>& literals,
2309 absl::Span<const int64_t> demands, int64_t
capacity,
2311 if (num_nodes <= 2)
return;
2320 std::vector<Arc> relevant_arcs;
2323 std::vector<double> literal_lp_values(literals.size());
2324 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2326 for (
int i = 0; i < literals.size(); ++i) {
2328 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2330 lp_value = lp_values[direct_view];
2333 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2335 literal_lp_values[i] = lp_value;
2337 if (lp_value < 1e-6)
continue;
2338 relevant_arcs.
push_back({tails[i], heads[i], lp_value});
2339 arc_by_decreasing_lp_values.push_back({lp_value, i});
2341 std::sort(arc_by_decreasing_lp_values.begin(),
2342 arc_by_decreasing_lp_values.end(),
2343 std::greater<std::pair<double, int>>());
2353 int num_components = num_nodes;
2354 std::vector<int> parent(num_nodes);
2355 std::vector<int> root(num_nodes);
2356 for (
int i = 0; i < num_nodes; ++i) {
2360 auto get_root_and_compress_path = [&root](
int node) {
2362 while (root[r] != r) r = root[r];
2363 while (root[node] != r) {
2364 const int next = root[node];
2370 for (
const auto pair : arc_by_decreasing_lp_values) {
2371 if (num_components == 2)
break;
2372 const int tail = get_root_and_compress_path(tails[pair.second]);
2373 const int head = get_root_and_compress_path(heads[pair.second]);
2377 const int new_node = parent.size();
2378 parent.push_back(new_node);
2379 parent[
head] = new_node;
2380 parent[
tail] = new_node;
2384 root.push_back(new_node);
2385 root[
head] = new_node;
2386 root[
tail] = new_node;
2397 std::vector<int> pre_order(num_nodes);
2398 std::vector<absl::Span<const int>> subsets;
2400 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2401 for (
int i = 0; i < parent.size(); ++i) {
2402 if (parent[i] != i) graph[parent[i]].push_back(i);
2404 std::vector<int> queue;
2405 std::vector<bool> seen(graph.size(),
false);
2406 std::vector<int> start_index(parent.size());
2407 for (
int i = num_nodes; i < parent.size(); ++i) {
2411 CHECK(graph[i].empty() || graph[i].size() == 2);
2412 if (parent[i] != i)
continue;
2417 while (!queue.empty()) {
2418 const int node = queue.back();
2423 const int start = start_index[node];
2424 if (new_size - start > 1) {
2425 subsets.emplace_back(&pre_order[start], new_size - start);
2430 start_index[node] = new_size;
2431 if (node < num_nodes) pre_order[new_size++] = node;
2432 for (
const int child : graph[node]) {
2433 if (!seen[child]) queue.push_back(child);
2441 int64_t total_demands = 0;
2442 if (!demands.empty()) {
2443 for (
const int64_t
demand : demands) total_demands +=
demand;
2447 CHECK_EQ(pre_order.size(), num_nodes);
2448 std::vector<bool> in_subset(num_nodes,
false);
2449 for (
const absl::Span<const int> subset : subsets) {
2451 CHECK_LT(subset.size(), num_nodes);
2454 bool contain_depot =
false;
2455 int64_t subset_demand = 0;
2458 for (
const int n : subset) {
2459 in_subset[n] =
true;
2460 if (!demands.empty()) {
2461 if (n == 0) contain_depot =
true;
2462 subset_demand += demands[n];
2479 int64_t min_outgoing_flow = 1;
2480 if (!demands.empty()) {
2490 min_outgoing_flow =
std::max(min_outgoing_flow, int64_t{1});
2502 double outgoing_flow = 0.0;
2503 for (
const auto arc : relevant_arcs) {
2504 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2505 outgoing_flow += arc.lp_value;
2510 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2511 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2512 literals, literal_lp_values,
2513 min_outgoing_flow, lp_values, manager,
2518 for (
const int n : subset) in_subset[n] =
false;
2525 std::vector<IntegerVariable> GetAssociatedVariables(
2526 const std::vector<Literal>& literals, Model*
model) {
2527 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2528 std::vector<IntegerVariable> result;
2529 for (
const Literal l : literals) {
2530 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2532 result.push_back(direct_view);
2534 result.push_back(encoder->GetLiteralView(l.Negated()));
2547 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2548 const std::vector<Literal>& literals,
Model*
model) {
2550 result.
vars = GetAssociatedVariables(literals,
model);
2552 [num_nodes, tails, heads, literals,
model](
2556 num_nodes, tails, heads, literals, lp_values,
2557 {}, 0, manager,
model);
2563 const std::vector<int>& tails,
2564 const std::vector<int>& heads,
2565 const std::vector<Literal>& literals,
2566 const std::vector<int64_t>& demands,
2569 result.
vars = GetAssociatedVariables(literals,
model);
2571 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2575 lp_values, demands,
capacity, manager,
2581 std::function<IntegerLiteral()>
2584 std::vector<IntegerVariable> variables;
2585 for (IntegerVariable
var : integer_variables_) {
2588 variables.push_back(
var);
2591 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2594 return [
this, variables]() {
2598 double fractional_distance_best = -1.0;
2599 for (
const IntegerVariable
var : variables) {
2604 if (lb == ub)
continue;
2608 const double fractional_distance =
2610 lp_value - std::floor(lp_value +
kEpsilon));
2611 if (fractional_distance <
kEpsilon)
continue;
2614 if (fractional_distance > fractional_distance_best) {
2615 fractional_var =
var;
2616 fractional_distance_best = fractional_distance;
2630 std::vector<IntegerVariable> variables;
2631 for (IntegerVariable
var : integer_variables_) {
2634 variables.push_back(
var);
2637 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2643 const int num_vars = variables.size();
2644 std::vector<double> cost_to_zero(num_vars, 0.0);
2645 std::vector<int> num_cost_to_zero(num_vars);
2648 return [=]()
mutable {
2653 if (num_calls == 10000) {
2654 for (
int i = 0; i < num_vars; i++) {
2655 cost_to_zero[i] /= 2;
2656 num_cost_to_zero[i] /= 2;
2662 for (
int i = 0; i < num_vars; i++) {
2663 const IntegerVariable
var = variables[i];
2668 if (lb == ub)
continue;
2672 if (std::abs(rc) <
kEpsilon)
continue;
2675 if (
value == 1.0 && rc < 0.0) {
2676 cost_to_zero[i] -= rc;
2677 num_cost_to_zero[i]++;
2682 int selected_index = -1;
2683 double best_cost = 0.0;
2684 for (
int i = 0; i < num_vars; i++) {
2685 const IntegerVariable
var = variables[i];
2690 if (num_cost_to_zero[i] > 0 &&
2691 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2692 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2697 if (selected_index >= 0) {
2705 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2706 const int num_vars = integer_variables_.size();
2707 if (sum_cost_down_.size() < num_vars) {
2708 sum_cost_down_.resize(num_vars, 0.0);
2709 num_cost_down_.resize(num_vars, 0);
2710 sum_cost_up_.resize(num_vars, 0.0);
2711 num_cost_up_.resize(num_vars, 0);
2712 rc_scores_.resize(num_vars, 0.0);
2716 num_calls_since_reduced_cost_averages_reset_++;
2717 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2718 for (
int i = 0; i < num_vars; i++) {
2719 sum_cost_up_[i] /= 2;
2720 num_cost_up_[i] /= 2;
2721 sum_cost_down_[i] /= 2;
2722 num_cost_down_[i] /= 2;
2724 num_calls_since_reduced_cost_averages_reset_ = 0;
2728 for (
int i = 0; i < num_vars; i++) {
2729 const IntegerVariable
var = integer_variables_[i];
2736 const double rc = lp_reduced_cost_[i];
2737 if (std::abs(rc) < kCpEpsilon)
continue;
2740 sum_cost_down_[i] -= rc;
2741 num_cost_down_[i]++;
2743 sum_cost_up_[i] += rc;
2750 rc_rev_int_repository_.
SetLevel(0);
2756 positions_by_decreasing_rc_score_.clear();
2757 for (
int i = 0; i < num_vars; i++) {
2762 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2763 const double a_down =
2764 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2765 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2766 rc_scores_[i] =
std::min(a_up, a_down);
2768 rc_scores_[i] = 0.5 * (a_down + a_up);
2773 if (rc_scores_[i] > 0.0) {
2774 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2777 std::sort(positions_by_decreasing_rc_score_.begin(),
2778 positions_by_decreasing_rc_score_.end());
2782 std::function<IntegerLiteral()>
2784 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2787 IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2789 int selected_index = -1;
2790 const int size = positions_by_decreasing_rc_score_.size();
2791 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2792 for (
int i = rev_rc_start_; i < size; ++i) {
2793 const int index = positions_by_decreasing_rc_score_[i].second;
2794 const IntegerVariable
var = integer_variables_[
index];
2797 selected_index =
index;
2802 if (selected_index == -1)
return IntegerLiteral();
2803 const IntegerVariable
var = integer_variables_[selected_index];
2810 const IntegerValue value_ceil(
2812 if (value_ceil >= ub) {
2819 const IntegerValue value_floor(
2821 if (value_floor <= lb) {
2828 num_cost_up_[selected_index] > 0
2829 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2831 const double a_down =
2832 num_cost_down_[selected_index] > 0
2833 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2835 if (a_down < a_up) {
#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...
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 AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
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
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)
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
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 SeparateSomeImpliedBoundCuts(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 absl::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
const std::vector< ConstraintIndex > & LpConstraints() const
void AddAllConstraintsToLp()
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
bool Propagate() override
double GetSolutionValue(IntegerVariable variable) const
void RegisterWith(Model *model)
glop::RowIndex ConstraintIndex
std::function< IntegerLiteral()> HeuristicLpReducedCostAverageBranching()
LinearProgrammingConstraint(Model *model)
std::function< IntegerLiteral()> HeuristicLpReducedCostBinary(Model *model)
~LinearProgrammingConstraint() override
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.
void ConvertToLinearConstraint(const std::vector< IntegerVariable > &integer_variables, IntegerValue upper_bound, LinearConstraint *result)
bool Add(glop::ColIndex col, IntegerValue value)
void ClearAndResize(int size)
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
bool AddLinearExpressionMultiple(IntegerValue multiplier, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
int CurrentDecisionLevel() const
void ProcessVariables(const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
void AddOneConstraint(glop::RowIndex, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms, IntegerValue lb, IntegerValue ub)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
StrictITIVector< ColIndex, Fractional > DenseRow
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)
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< void(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