24 #include "absl/container/flat_hash_map.h"
25 #include "absl/numeric/int128.h"
50 const double LinearProgrammingConstraint::kCpEpsilon = 1e-4;
51 const double LinearProgrammingConstraint::kLpEpsilon = 1e-6;
55 for (
const glop::ColIndex
col : non_zeros_) {
56 dense_vector_[
col] = IntegerValue(0);
58 dense_vector_.
resize(size, IntegerValue(0));
60 dense_vector_.
assign(size, IntegerValue(0));
62 for (
const glop::ColIndex
col : non_zeros_) {
63 is_zeros_[
col] =
true;
65 is_zeros_.
resize(size,
true);
73 dense_vector_[
col] = IntegerValue(add);
74 if (is_sparse_ && is_zeros_[
col]) {
75 is_zeros_[
col] =
false;
76 non_zeros_.push_back(
col);
82 IntegerValue multiplier,
83 const std::vector<std::pair<glop::ColIndex, IntegerValue>>& terms) {
84 const double threshold = 0.1 *
static_cast<double>(dense_vector_.
size());
85 if (is_sparse_ &&
static_cast<double>(terms.size()) < threshold) {
86 for (
const std::pair<glop::ColIndex, IntegerValue> term : terms) {
87 if (is_zeros_[term.first]) {
88 is_zeros_[term.first] =
false;
89 non_zeros_.push_back(term.first);
91 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
95 if (
static_cast<double>(non_zeros_.size()) < threshold) {
100 for (
const std::pair<glop::ColIndex, IntegerValue> term : terms) {
101 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
110 const std::vector<IntegerVariable>& integer_variables,
112 result->
vars.clear();
115 std::sort(non_zeros_.begin(), non_zeros_.end());
116 for (
const glop::ColIndex
col : non_zeros_) {
117 const IntegerValue coeff = dense_vector_[
col];
118 if (coeff == 0)
continue;
119 result->
vars.push_back(integer_variables[
col.value()]);
120 result->
coeffs.push_back(coeff);
123 const int size = dense_vector_.
size();
124 for (glop::ColIndex
col(0);
col < size; ++
col) {
125 const IntegerValue coeff = dense_vector_[
col];
126 if (coeff == 0)
continue;
127 result->
vars.push_back(integer_variables[
col.value()]);
128 result->
coeffs.push_back(coeff);
132 result->
ub = upper_bound;
135 std::vector<std::pair<glop::ColIndex, IntegerValue>>
137 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
139 std::sort(non_zeros_.begin(), non_zeros_.end());
140 for (
const glop::ColIndex
col : non_zeros_) {
141 const IntegerValue coeff = dense_vector_[
col];
142 if (coeff != 0) result.push_back({
col, coeff});
145 const int size = dense_vector_.
size();
146 for (glop::ColIndex
col(0);
col < size; ++
col) {
147 const IntegerValue coeff = dense_vector_[
col];
148 if (coeff != 0) result.push_back({
col, coeff});
157 : constraint_manager_(
model),
158 sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
166 implied_bounds_processor_({}, integer_trail_,
169 expanded_lp_solution_(
175 if (sat_parameters_.use_branching_in_lp() ||
176 sat_parameters_.search_branching() == SatParameters::LP_SEARCH) {
177 compute_reduced_cost_averages_ =
true;
181 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
185 VLOG(1) <<
"Total number of simplex iterations: "
186 << total_num_simplex_iterations_;
191 DCHECK(!lp_constraint_is_registered_);
192 constraint_manager_.
Add(
ct);
199 for (
const IntegerVariable
var :
ct.vars) {
204 glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
205 IntegerVariable positive_variable) {
207 const auto it = mirror_lp_variable_.find(positive_variable);
208 if (it == mirror_lp_variable_.end()) {
209 const glop::ColIndex
col(integer_variables_.size());
211 mirror_lp_variable_[positive_variable] =
col;
212 integer_variables_.push_back(positive_variable);
213 lp_solution_.push_back(std::numeric_limits<double>::infinity());
214 lp_reduced_cost_.push_back(0.0);
215 (*dispatcher_)[positive_variable] =
this;
219 if (
index >= expanded_lp_solution_.
size()) {
228 IntegerValue coeff) {
229 CHECK(!lp_constraint_is_registered_);
230 objective_is_defined_ =
true;
232 if (ivar != pos_var) coeff = -coeff;
235 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
236 integer_objective_.push_back({
col, coeff});
237 objective_infinity_norm_ =
254 bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
257 infinity_norms_.
clear();
258 const auto& all_constraints = constraint_manager_.
AllConstraints();
262 integer_lp_.
push_back(LinearConstraintInternal());
263 LinearConstraintInternal& new_ct = integer_lp_.
back();
266 const int size =
ct.vars.size();
267 IntegerValue infinity_norm(0);
269 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
278 for (
int i = 0; i < size; ++i) {
280 IntegerVariable
var =
ct.vars[i];
281 IntegerValue coeff =
ct.coeffs[i];
287 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var), coeff});
289 infinity_norms_.
push_back(infinity_norm);
292 std::sort(new_ct.terms.begin(), new_ct.terms.end());
297 for (
int i = 0; i < integer_variables_.size(); ++i) {
300 for (
const auto entry : integer_objective_) {
303 for (
const LinearConstraintInternal&
ct : integer_lp_) {
306 for (
const auto& term :
ct.terms) {
318 const int num_vars = integer_variables_.size();
319 for (
int i = 0; i < num_vars; i++) {
320 const IntegerVariable cp_var = integer_variables_[i];
326 scaler_.
Scale(&lp_data_);
327 UpdateBoundsOfLpVariables();
333 <<
" Managed constraints.";
337 LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
339 glop::BasisState basis_state = simplex_.
GetState();
341 const glop::Status status = simplex_.
Solve(lp_data_, time_limit_);
345 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
351 info.status == glop::ProblemStatus::DUAL_FEASIBLE) {
354 info.new_obj_bound = IntegerValue(
355 static_cast<int64>(std::ceil(info.lp_objective - kCpEpsilon)));
360 void LinearProgrammingConstraint::FillReducedCostReasonIn(
362 std::vector<IntegerLiteral>* integer_reason) {
363 integer_reason->clear();
364 const int num_vars = integer_variables_.size();
365 for (
int i = 0; i < num_vars; i++) {
366 const double rc = reduced_costs[glop::ColIndex(i)];
367 if (rc > kLpEpsilon) {
368 integer_reason->push_back(
370 }
else if (rc < -kLpEpsilon) {
371 integer_reason->push_back(
379 bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
381 DCHECK(lp_solution_is_set_);
383 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
386 integer_reason_.clear();
388 bool deductions_were_made =
false;
390 UpdateBoundsOfLpVariables();
392 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
396 const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
400 if (current_value < current_lb || current_value > current_ub) {
405 const double new_ub = std::floor(current_value);
408 LPSolveInfo lower_branch_info = SolveLpForBranching();
410 lower_branch_info.status != glop::ProblemStatus::DUAL_FEASIBLE &&
411 lower_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
415 if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
418 positive_var, IntegerValue(std::ceil(current_value)));
419 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
422 deductions_were_made =
true;
423 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
428 const double new_lb = std::ceil(current_value);
431 LPSolveInfo upper_branch_info = SolveLpForBranching();
433 upper_branch_info.status != glop::ProblemStatus::DUAL_FEASIBLE &&
434 upper_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
435 return deductions_were_made;
438 if (upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
440 if (lower_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
442 positive_var, IntegerValue(std::floor(current_value)));
443 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
444 return deductions_were_made;
446 deductions_were_made =
true;
448 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
449 return deductions_were_made;
454 if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED &&
455 upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
457 }
else if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
458 approximate_obj_lb = upper_branch_info.new_obj_bound;
459 }
else if (upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
460 approximate_obj_lb = lower_branch_info.new_obj_bound;
462 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
463 upper_branch_info.new_obj_bound);
468 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
471 const IntegerLiteral deduction =
473 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
474 return deductions_were_made;
481 DCHECK(!lp_constraint_is_registered_);
482 lp_constraint_is_registered_ =
true;
487 std::sort(integer_objective_.begin(), integer_objective_.end());
490 if (!sat_parameters_.add_lp_constraints_lazily()) {
493 if (!CreateLpFromConstraintManager()) {
499 const int watcher_id = watcher->
Register(
this);
500 const int num_vars = integer_variables_.size();
501 for (
int i = 0; i < num_vars; i++) {
504 if (objective_is_defined_) {
510 if (integer_variables_.size() >= 20) {
523 optimal_constraints_.resize(rev_optimal_constraints_size_);
524 if (lp_solution_is_set_ && level < lp_solution_level_) {
525 lp_solution_is_set_ =
false;
533 if (level == 0 && !level_zero_lp_solution_.empty()) {
534 lp_solution_is_set_ =
true;
535 lp_solution_ = level_zero_lp_solution_;
536 lp_solution_level_ = 0;
537 for (
int i = 0; i < lp_solution_.size(); i++) {
538 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
539 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
546 for (
const IntegerVariable
var : generator.
vars) {
549 cut_generators_.push_back(std::move(generator));
553 const std::vector<int>& watch_indices) {
554 if (!lp_solution_is_set_)
return Propagate();
564 for (
const int index : watch_indices) {
570 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
585 glop::ColIndex
var) {
590 IntegerVariable variable)
const {
591 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
595 IntegerVariable variable)
const {
596 return lp_reduced_cost_[
gtl::FindOrDie(mirror_lp_variable_, variable)
600 void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
601 const int num_vars = integer_variables_.size();
602 for (
int i = 0; i < num_vars; i++) {
603 const IntegerVariable cp_var = integer_variables_[i];
611 bool LinearProgrammingConstraint::SolveLp() {
613 lp_at_level_zero_is_final_ =
false;
616 const auto status = simplex_.
Solve(lp_data_, time_limit_);
619 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
623 average_degeneracy_.
AddData(CalculateDegeneracy());
625 VLOG(2) <<
"High average degeneracy: "
630 lp_solution_is_set_ =
true;
632 const int num_vars = integer_variables_.size();
633 for (
int i = 0; i < num_vars; i++) {
635 GetVariableValueAtCpScale(glop::ColIndex(i));
636 lp_solution_[i] =
value;
637 expanded_lp_solution_[integer_variables_[i]] =
value;
641 if (lp_solution_level_ == 0) {
642 level_zero_lp_solution_ = lp_solution_;
648 bool LinearProgrammingConstraint::AddCutFromConstraints(
649 const std::string&
name,
650 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
661 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
663 VLOG(1) <<
"Issue, overflow!";
682 if (std::abs(activity -
ToDouble(cut_.
ub)) / norm > 1e-4) {
683 VLOG(1) <<
"Cut not tight " << activity <<
" <= " <<
ToDouble(cut_.
ub);
693 const IntegerVariable first_new_var(expanded_lp_solution_.
size());
694 CHECK_EQ(first_new_var.value() % 2, 0);
696 LinearConstraint copy_in_debug;
698 copy_in_debug = cut_;
708 std::vector<ImpliedBoundsProcessor::SlackInfo> ib_slack_infos;
710 false, first_new_var,
711 expanded_lp_solution_, &cut_, &ib_slack_infos);
712 DCHECK(implied_bounds_processor_.
DebugSlack(first_new_var, copy_in_debug,
713 cut_, ib_slack_infos));
721 tmp_lp_values_.clear();
722 tmp_var_lbs_.clear();
723 tmp_var_ubs_.clear();
724 for (
const IntegerVariable
var : cut_.
vars) {
725 if (
var >= first_new_var) {
728 ib_slack_infos[(
var.value() - first_new_var.value()) / 2];
729 tmp_lp_values_.push_back(info.lp_value);
730 tmp_var_lbs_.push_back(info.lb);
731 tmp_var_ubs_.push_back(info.ub);
733 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
741 const IntegerVariable first_slack(first_new_var +
742 IntegerVariable(2 * ib_slack_infos.size()));
743 tmp_slack_rows_.clear();
744 tmp_slack_bounds_.clear();
745 for (
const auto pair : integer_multipliers) {
746 const RowIndex
row = pair.first;
747 const IntegerValue coeff = pair.second;
751 tmp_lp_values_.push_back(0.0);
752 cut_.
vars.push_back(first_slack +
753 2 * IntegerVariable(tmp_slack_rows_.size()));
754 tmp_slack_rows_.push_back(
row);
755 cut_.
coeffs.push_back(coeff);
757 const IntegerValue diff(
758 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value()));
760 tmp_slack_bounds_.push_back(integer_lp_[
row].ub);
761 tmp_var_lbs_.push_back(IntegerValue(0));
762 tmp_var_ubs_.push_back(diff);
764 tmp_slack_bounds_.push_back(integer_lp_[
row].lb);
765 tmp_var_lbs_.push_back(-diff);
766 tmp_var_ubs_.push_back(IntegerValue(0));
770 bool at_least_one_added =
false;
776 at_least_one_added |= PostprocessAndAddCut(
777 absl::StrCat(
name,
"_K"), cover_cut_helper_.
Info(), first_new_var,
778 first_slack, ib_slack_infos, cover_cut_helper_.
mutable_cut());
784 RoundingOptions options;
785 options.max_scaling = sat_parameters_.max_integer_rounding_scaling();
786 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
787 tmp_var_lbs_, tmp_var_ubs_,
788 &implied_bounds_processor_, &cut_);
789 at_least_one_added |= PostprocessAndAddCut(
791 absl::StrCat(
"num_lifted_booleans=",
793 first_new_var, first_slack, ib_slack_infos, &cut_);
795 return at_least_one_added;
798 bool LinearProgrammingConstraint::PostprocessAndAddCut(
799 const std::string&
name,
const std::string& info,
800 IntegerVariable first_new_var, IntegerVariable first_slack,
801 const std::vector<ImpliedBoundsProcessor::SlackInfo>& ib_slack_infos,
802 LinearConstraint* cut) {
806 double activity = 0.0;
807 for (
int i = 0; i < cut->vars.size(); ++i) {
808 if (cut->vars[i] < first_new_var) {
810 ToDouble(cut->coeffs[i]) * expanded_lp_solution_[cut->vars[i]];
813 const double kMinViolation = 1e-4;
814 const double violation = activity -
ToDouble(cut->ub);
815 if (violation < kMinViolation) {
816 VLOG(3) <<
"Bad cut " << activity <<
" <= " <<
ToDouble(cut->ub);
824 IntegerValue cut_ub = cut->ub;
825 bool overflow =
false;
826 for (
int i = 0; i < cut->vars.size(); ++i) {
827 const IntegerVariable
var = cut->vars[i];
830 if (
var < first_new_var) {
831 const glop::ColIndex
col =
834 tmp_scattered_vector_.
Add(
col, cut->coeffs[i]);
836 tmp_scattered_vector_.
Add(
col, -cut->coeffs[i]);
842 if (
var < first_slack) {
843 const IntegerValue multiplier = cut->coeffs[i];
844 const int index = (
var.value() - first_new_var.value()) / 2;
845 CHECK_LT(
index, ib_slack_infos.size());
847 std::vector<std::pair<ColIndex, IntegerValue>> terms;
848 for (
const std::pair<IntegerVariable, IntegerValue>& term :
849 ib_slack_infos[
index].terms) {
869 const int slack_index = (
var.value() - first_slack.value()) / 2;
870 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
871 const IntegerValue multiplier = -cut->coeffs[i];
873 multiplier, integer_lp_[
row].terms)) {
879 if (!
AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
886 VLOG(1) <<
"Overflow in slack removal.";
890 VLOG(3) <<
" num_slack: " << num_slack;
896 const std::string extra_info =
897 absl::StrCat(info,
" num_ib_substitutions=", ib_slack_infos.size());
899 const double new_violation =
901 if (std::abs(violation - new_violation) >= 1e-4) {
902 VLOG(1) <<
"Violation discrepancy after slack removal. "
903 <<
" before = " << violation <<
" after = " << new_violation;
907 return constraint_manager_.
AddCut(*cut,
name, expanded_lp_solution_,
911 void LinearProgrammingConstraint::AddCGCuts() {
914 for (RowIndex
row(0);
row < num_rows; ++
row) {
916 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
921 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
925 if (basis_col >= integer_variables_.size())
continue;
929 double magnitude = 0.0;
930 int num_non_zeros = 0;
931 for (RowIndex
row(0);
row < num_rows; ++
row) {
933 if (std::abs(lp_multipliers[
row]) < 1e-12) {
934 lp_multipliers[
row] = 0.0;
942 VLOG(1) <<
"BASIC row not expected! " << lp_multipliers[
row];
943 lp_multipliers[
row] = 0.0;
946 magnitude =
std::max(magnitude, std::abs(lp_multipliers[
row]));
947 if (lp_multipliers[
row] != 0.0) ++num_non_zeros;
949 if (num_non_zeros == 0)
continue;
952 for (
int i = 0; i < 2; ++i) {
958 for (RowIndex
row(0);
row < num_rows; ++
row) {
959 lp_multipliers[
row] = -lp_multipliers[
row];
965 const std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
966 ScaleLpMultiplier(
false,
967 lp_multipliers, &scaling, 52);
968 AddCutFromConstraints(
"CG", integer_multipliers);
976 void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
977 ModelRandomGenerator* random,
978 std::vector<std::pair<RowIndex, RowIndex>>* output) {
979 if (
a.empty() ||
b.empty())
return;
980 for (
const RowIndex
row :
a) {
981 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
983 output->push_back({
row, other});
988 template <
class ListOfTerms>
989 IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
990 for (
const auto& term : terms) {
991 if (term.first ==
col)
return term.second;
993 return IntegerValue(0);
998 void LinearProgrammingConstraint::AddMirCuts() {
1017 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1022 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1024 for (RowIndex
row(0);
row < num_rows; ++
row) {
1031 base_rows.push_back({
row, IntegerValue(1)});
1035 base_rows.push_back({
row, IntegerValue(-1)});
1056 std::vector<double> weights;
1058 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1059 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1067 integer_multipliers = {entry};
1068 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1073 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1074 dense_cut[
col] = IntegerValue(0);
1076 non_zeros_.SparseClearAll();
1079 const IntegerValue multiplier = entry.second;
1080 for (
const std::pair<ColIndex, IntegerValue> term :
1081 integer_lp_[entry.first].terms) {
1082 const ColIndex
col = term.first;
1083 const IntegerValue coeff = term.second;
1084 non_zeros_.Set(
col);
1085 dense_cut[
col] += coeff * multiplier;
1088 used_rows.
assign(num_rows.value(),
false);
1089 used_rows[entry.first] =
true;
1094 const int kMaxAggregation = 5;
1095 for (
int i = 0; i < kMaxAggregation; ++i) {
1098 IntegerValue max_magnitude(0);
1100 std::vector<ColIndex> col_candidates;
1101 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1102 if (dense_cut[
col] == 0)
continue;
1105 const int col_degree =
1107 if (col_degree <= 1)
continue;
1112 const IntegerVariable
var = integer_variables_[
col.value()];
1113 const double lp_value = expanded_lp_solution_[
var];
1116 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1117 if (bound_distance > 1e-2) {
1118 weights.push_back(bound_distance);
1119 col_candidates.push_back(
col);
1122 if (col_candidates.empty())
break;
1124 const ColIndex var_to_eliminate =
1125 col_candidates[std::discrete_distribution<>(weights.begin(),
1126 weights.end())(*random_)];
1129 std::vector<RowIndex> possible_rows;
1132 const RowIndex
row = entry.row();
1140 if (used_rows[
row])
continue;
1141 used_rows[
row] =
true;
1149 bool add_row =
false;
1152 if (entry.coefficient() > 0.0) {
1153 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1155 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1160 if (entry.coefficient() > 0.0) {
1161 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1163 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1167 possible_rows.push_back(
row);
1168 weights.push_back(row_weights[
row]);
1171 if (possible_rows.empty())
break;
1173 const RowIndex row_to_combine =
1174 possible_rows[std::discrete_distribution<>(weights.begin(),
1175 weights.end())(*random_)];
1176 const IntegerValue to_combine_coeff =
1177 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1178 CHECK_NE(to_combine_coeff, 0);
1180 IntegerValue mult1 = -to_combine_coeff;
1181 IntegerValue mult2 = dense_cut[var_to_eliminate];
1188 const IntegerValue gcd = IntegerValue(
1198 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1199 max_magnitude =
std::max(max_magnitude, entry.second);
1201 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1203 std::abs(mult2.value()))) ==
kint64max) {
1207 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1208 entry.second *= mult1;
1210 integer_multipliers.push_back({row_to_combine, mult2});
1213 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1214 integer_multipliers)) {
1220 if (i + 1 == kMaxAggregation)
break;
1222 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1223 dense_cut[
col] *= mult1;
1225 for (
const std::pair<ColIndex, IntegerValue> term :
1226 integer_lp_[row_to_combine].terms) {
1227 const ColIndex
col = term.first;
1228 const IntegerValue coeff = term.second;
1229 non_zeros_.Set(
col);
1230 dense_cut[
col] += coeff * mult2;
1236 void LinearProgrammingConstraint::AddZeroHalfCuts() {
1239 tmp_lp_values_.clear();
1240 tmp_var_lbs_.clear();
1241 tmp_var_ubs_.clear();
1242 for (
const IntegerVariable
var : integer_variables_) {
1243 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1251 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1259 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1261 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1267 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1271 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1272 const int64 min_iter,
const int64 max_iter) {
1273 if (sat_parameters_.linearization_level() < 2)
return;
1274 const int64 num_degenerate_columns = CalculateDegeneracy();
1276 if (num_cols <= 0) {
1279 CHECK_GT(num_cols, 0);
1280 const int64 decrease_factor = (10 * num_degenerate_columns) / num_cols;
1285 if (is_degenerate_) {
1288 next_simplex_iter_ *= 2;
1291 if (is_degenerate_) {
1292 next_simplex_iter_ /=
std::max(
int64{1}, 2 * decrease_factor);
1296 next_simplex_iter_ = num_cols / 40;
1299 next_simplex_iter_ =
1304 UpdateBoundsOfLpVariables();
1309 if ( (
false) && objective_is_defined_) {
1318 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1319 100.0 * kCpEpsilon));
1328 parameters.set_max_number_of_iterations(2000);
1330 parameters.set_max_number_of_iterations(next_simplex_iter_);
1332 if (sat_parameters_.use_exact_lp_reason()) {
1333 parameters.set_change_status_to_imprecise(
false);
1334 parameters.set_primal_feasibility_tolerance(1e-7);
1335 parameters.set_dual_feasibility_tolerance(1e-7);
1340 if (!SolveLp())
return true;
1343 const int max_cuts_rounds =
1345 ? sat_parameters_.max_cut_rounds_at_level_zero()
1349 cuts_round < max_cuts_rounds) {
1353 if (!integer_lp_.empty()) {
1356 expanded_lp_solution_);
1363 if (sat_parameters_.add_mir_cuts()) AddMirCuts();
1364 if (sat_parameters_.add_cg_cuts()) AddCGCuts();
1365 if (sat_parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1369 if (!cut_generators_.empty() &&
1371 !sat_parameters_.only_add_cuts_at_level_zero())) {
1372 for (
const CutGenerator& generator : cut_generators_) {
1373 generator.generate_cuts(expanded_lp_solution_, &constraint_manager_);
1378 expanded_lp_solution_, &constraint_manager_);
1382 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1384 if (!CreateLpFromConstraintManager()) {
1388 if (!SolveLp())
return true;
1390 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1397 lp_at_level_zero_is_final_ =
true;
1405 if (sat_parameters_.use_exact_lp_reason()) {
1406 if (!FillExactDualRayReason())
return true;
1415 UpdateSimplexIterationLimit(10, 1000);
1418 if (objective_is_defined_ &&
1425 const IntegerValue approximate_new_lb(
1426 static_cast<int64>(std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1430 if (sat_parameters_.use_exact_lp_reason()) {
1431 if (!ExactLpReasonning())
return false;
1434 const IntegerValue propagated_lb =
1436 if (approximate_new_lb > propagated_lb) {
1437 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1439 <<
" ] approx_lb += "
1440 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1441 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1444 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1445 const double objective_cp_ub =
1447 ReducedCostStrengtheningDeductions(objective_cp_ub -
1448 relaxed_optimal_objective);
1449 if (!deductions_.empty()) {
1450 deductions_reason_ = integer_reason_;
1451 deductions_reason_.push_back(
1456 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1459 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1465 if (!deductions_.empty()) {
1466 const int trail_index_with_same_reason = integer_trail_->
Index();
1468 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1469 trail_index_with_same_reason)) {
1479 CHECK(lp_solution_is_set_);
1482 lp_solution_is_integer_ =
true;
1483 const int num_vars = integer_variables_.size();
1484 for (
int i = 0; i < num_vars; i++) {
1487 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1489 lp_solution_is_integer_ =
false;
1493 if (compute_reduced_cost_averages_) {
1494 UpdateAverageReducedCosts();
1498 if (sat_parameters_.use_branching_in_lp() && objective_is_defined_ &&
1500 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1501 sat_parameters_.linearization_level() >= 2 &&
1502 compute_reduced_cost_averages_ &&
1504 count_since_last_branching_++;
1505 if (count_since_last_branching_ < branching_frequency_) {
1508 count_since_last_branching_ = 0;
1509 bool branching_successful =
false;
1512 const int max_num_branches = 3;
1513 const int num_vars = integer_variables_.size();
1514 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1515 for (
int i = 0; i < num_vars; ++i) {
1516 const IntegerVariable
var = integer_variables_[i];
1521 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1535 const double cost_i = rc_scores_[i];
1536 std::pair<double, IntegerVariable> branching_var =
1537 std::make_pair(-cost_i, positive_var);
1538 auto iterator = std::lower_bound(branching_vars.begin(),
1539 branching_vars.end(), branching_var);
1541 branching_vars.insert(iterator, branching_var);
1542 if (branching_vars.size() > max_num_branches) {
1543 branching_vars.resize(max_num_branches);
1547 for (
const std::pair<double, IntegerVariable>& branching_var :
1549 const IntegerVariable positive_var = branching_var.second;
1550 VLOG(2) <<
"Branching on: " << positive_var;
1551 if (BranchOnVar(positive_var)) {
1552 VLOG(2) <<
"Branching successful.";
1553 branching_successful =
true;
1558 if (!branching_successful) {
1559 branching_frequency_ *= 2;
1568 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1570 IntegerValue lower_bound(0);
1571 const int size = terms.
vars.size();
1572 for (
int i = 0; i < size; ++i) {
1573 const IntegerVariable
var = terms.
vars[i];
1574 const IntegerValue coeff = terms.
coeffs[i];
1583 bool LinearProgrammingConstraint::PossibleOverflow(
1584 const LinearConstraint& constraint) {
1585 IntegerValue lower_bound(0);
1586 const int size = constraint.vars.size();
1587 for (
int i = 0; i < size; ++i) {
1588 const IntegerVariable
var = constraint.vars[i];
1589 const IntegerValue coeff = constraint.coeffs[i];
1597 const int64 slack =
CapAdd(lower_bound.value(), -constraint.ub.value());
1606 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1607 absl::int128 div128(positive_div.value());
1608 absl::int128 result = x / div128;
1609 if (result * div128 > x)
return result - 1;
1615 void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1624 const int size = constraint->vars.size();
1625 for (
int i = 0; i < size; ++i) {
1626 const IntegerVariable
var = constraint->vars[i];
1627 const double coeff =
ToDouble(constraint->coeffs[i]);
1633 const double max_value =
std::max(sum_max, -sum_min);
1635 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1636 if (divisor <= 1)
return;
1651 absl::int128 adjust = 0;
1652 for (
int i = 0; i < size; ++i) {
1653 const IntegerValue old_coeff = constraint->coeffs[i];
1654 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1657 const absl::int128 remainder =
1658 absl::int128(old_coeff.value()) -
1659 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1665 if (new_coeff == 0)
continue;
1666 constraint->vars[new_size] = constraint->vars[i];
1667 constraint->coeffs[new_size] = new_coeff;
1670 constraint->vars.resize(new_size);
1671 constraint->coeffs.resize(new_size);
1673 constraint->ub = IntegerValue(
static_cast<int64>(
1674 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1679 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1680 const LinearConstraint& terms, IntegerValue slack) {
1681 integer_reason_.clear();
1682 std::vector<IntegerValue> magnitudes;
1683 const int size = terms.vars.size();
1684 for (
int i = 0; i < size; ++i) {
1685 const IntegerVariable
var = terms.vars[i];
1686 const IntegerValue coeff = terms.coeffs[i];
1689 magnitudes.push_back(coeff);
1692 magnitudes.push_back(-coeff);
1704 std::vector<std::pair<RowIndex, IntegerValue>>
1705 LinearProgrammingConstraint::ScaleLpMultiplier(
1706 bool take_objective_into_account,
1708 int max_pow)
const {
1709 double max_sum = 0.0;
1710 std::vector<std::pair<RowIndex, Fractional>> cp_multipliers;
1711 for (RowIndex
row(0);
row < dense_lp_multipliers.size(); ++
row) {
1716 if (std::abs(lp_multi) < 1e-12)
continue;
1732 cp_multipliers.push_back({
row, cp_multi});
1733 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1738 if (take_objective_into_account) {
1739 max_sum +=
ToDouble(objective_infinity_norm_);
1743 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1744 if (max_sum == 0.0) {
1746 return integer_multipliers;
1751 const double threshold = std::ldexp(1, max_pow) / max_sum;
1752 if (threshold < 1.0) {
1755 return integer_multipliers;
1757 while (2 * *scaling <= threshold) *scaling *= 2;
1762 for (
const auto entry : cp_multipliers) {
1763 const IntegerValue coeff(std::round(entry.second * (*scaling)));
1764 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1766 return integer_multipliers;
1769 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1770 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1771 ScatteredIntegerVector* scattered_vector, IntegerValue* upper_bound)
const {
1774 scattered_vector->ClearAndResize(integer_variables_.size());
1778 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1779 const RowIndex
row = term.first;
1780 const IntegerValue multiplier = term.second;
1781 CHECK_LT(
row, integer_lp_.size());
1784 if (!scattered_vector->AddLinearExpressionMultiple(
1785 multiplier, integer_lp_[
row].terms)) {
1790 const IntegerValue
bound =
1791 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1799 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1800 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1801 ScatteredIntegerVector* scattered_vector, IntegerValue* upper_bound)
const {
1802 const IntegerValue kMaxWantedCoeff(1e18);
1803 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1804 const RowIndex
row = term.first;
1805 const IntegerValue multiplier = term.second;
1806 if (multiplier == 0)
continue;
1810 IntegerValue negative_limit = kMaxWantedCoeff;
1811 IntegerValue positive_limit = kMaxWantedCoeff;
1815 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1816 if (multiplier > 0) {
1817 negative_limit =
std::min(negative_limit, multiplier);
1819 positive_limit =
std::min(positive_limit, -multiplier);
1824 const IntegerValue row_bound =
1825 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1826 if (row_bound != 0) {
1830 const IntegerValue limit2 =
1832 if (*upper_bound > 0 == row_bound > 0) {
1833 positive_limit =
std::min(positive_limit, limit1);
1834 negative_limit =
std::min(negative_limit, limit2);
1836 negative_limit =
std::min(negative_limit, limit1);
1837 positive_limit =
std::min(positive_limit, limit2);
1849 double positive_diff =
ToDouble(row_bound);
1850 double negative_diff =
ToDouble(row_bound);
1855 for (
const auto entry : integer_lp_[
row].terms) {
1856 const ColIndex
col = entry.first;
1857 const IntegerValue coeff = entry.second;
1858 const IntegerValue abs_coef =
IntTypeAbs(coeff);
1861 const IntegerVariable
var = integer_variables_[
col.value()];
1868 const IntegerValue current = (*scattered_vector)[
col];
1870 const IntegerValue overflow_limit(
1872 positive_limit =
std::min(positive_limit, overflow_limit);
1873 negative_limit =
std::min(negative_limit, overflow_limit);
1890 const IntegerValue current_magnitude =
IntTypeAbs(current);
1891 const IntegerValue other_direction_limit =
FloorRatio(
1893 ? kMaxWantedCoeff +
std::min(current_magnitude,
1895 : current_magnitude,
1897 const IntegerValue same_direction_limit(
FloorRatio(
1898 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
1900 if (current > 0 == coeff > 0) {
1901 negative_limit =
std::min(negative_limit, other_direction_limit);
1902 positive_limit =
std::min(positive_limit, same_direction_limit);
1904 negative_limit =
std::min(negative_limit, same_direction_limit);
1905 positive_limit =
std::min(positive_limit, other_direction_limit);
1909 const IntegerValue implied = current > 0 ? lb : ub;
1920 IntegerValue to_add(0);
1921 if (positive_diff <= -1.0 && positive_limit > 0) {
1922 to_add = positive_limit;
1924 if (negative_diff >= 1.0 && negative_limit > 0) {
1927 std::abs(
ToDouble(negative_limit) * negative_diff) >
1928 std::abs(
ToDouble(positive_limit) * positive_diff)) {
1929 to_add = -negative_limit;
1933 term.second += to_add;
1934 *upper_bound += to_add * row_bound;
1938 CHECK(scattered_vector->AddLinearExpressionMultiple(
1939 to_add, integer_lp_[
row].terms));
1958 bool LinearProgrammingConstraint::ExactLpReasonning() {
1960 integer_reason_.clear();
1961 deductions_.clear();
1962 deductions_reason_.clear();
1969 for (RowIndex
row(0);
row < num_rows; ++
row) {
1974 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
1975 ScaleLpMultiplier(
true, lp_multipliers,
1979 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
1981 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
1987 const IntegerValue obj_scale(std::round(scaling));
1988 if (obj_scale == 0) {
1989 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
1993 integer_objective_));
1994 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
1999 LinearConstraint new_constraint;
2002 new_constraint.vars.push_back(objective_cp_);
2003 new_constraint.coeffs.push_back(-obj_scale);
2005 PreventOverflow(&new_constraint);
2006 DCHECK(!PossibleOverflow(new_constraint));
2009 IntegerSumLE* cp_constraint =
2010 new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
2011 new_constraint.ub, model_);
2015 optimal_constraints_.clear();
2017 optimal_constraints_.emplace_back(cp_constraint);
2018 rev_optimal_constraints_size_ = optimal_constraints_.size();
2019 return cp_constraint->Propagate();
2022 bool LinearProgrammingConstraint::FillExactDualRayReason() {
2024 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2025 ScaleLpMultiplier(
false,
2028 IntegerValue new_constraint_ub;
2029 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2030 &new_constraint_ub)) {
2031 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2035 AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2036 &new_constraint_ub);
2038 LinearConstraint new_constraint;
2040 integer_variables_, new_constraint_ub, &new_constraint);
2042 PreventOverflow(&new_constraint);
2043 DCHECK(!PossibleOverflow(new_constraint));
2046 const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
2047 if (implied_lb <= new_constraint.ub) {
2048 VLOG(1) <<
"LP exact dual ray not infeasible,"
2049 <<
" implied_lb: " << implied_lb.value() / scaling
2050 <<
" ub: " << new_constraint.ub.value() / scaling;
2053 const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
2054 SetImpliedLowerBoundReason(new_constraint, slack);
2058 int64 LinearProgrammingConstraint::CalculateDegeneracy() {
2060 int num_non_basic_with_zero_rc = 0;
2061 for (glop::ColIndex i(0); i < num_vars; ++i) {
2063 if (rc != 0.0)
continue;
2067 num_non_basic_with_zero_rc++;
2070 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2071 return num_non_basic_with_zero_rc;
2074 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2075 double cp_objective_delta) {
2076 deductions_.clear();
2081 const double lp_objective_delta =
2083 const int num_vars = integer_variables_.size();
2084 for (
int i = 0; i < num_vars; i++) {
2085 const IntegerVariable cp_var = integer_variables_[i];
2086 const glop::ColIndex lp_var = glop::ColIndex(i);
2090 if (rc == 0.0)
continue;
2091 const double lp_other_bound =
value + lp_objective_delta / rc;
2092 const double cp_other_bound =
2095 if (rc > kLpEpsilon) {
2097 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2102 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2105 }
else if (rc < -kLpEpsilon) {
2107 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2109 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2110 deductions_.push_back(
2124 void AddOutgoingCut(
int num_nodes,
int subset_size,
2125 const std::vector<bool>& in_subset,
2126 const std::vector<int>& tails,
2127 const std::vector<int>& heads,
2128 const std::vector<Literal>& literals,
2129 const std::vector<double>& literal_lp_values,
2130 int64 rhs_lower_bound,
2132 LinearConstraintManager* manager, Model*
model) {
2139 int num_optional_nodes_in = 0;
2140 int num_optional_nodes_out = 0;
2141 int optional_loop_in = -1;
2142 int optional_loop_out = -1;
2143 for (
int i = 0; i < tails.size(); ++i) {
2144 if (tails[i] != heads[i])
continue;
2145 if (in_subset[tails[i]]) {
2146 num_optional_nodes_in++;
2147 if (optional_loop_in == -1 ||
2148 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2149 optional_loop_in = i;
2152 num_optional_nodes_out++;
2153 if (optional_loop_out == -1 ||
2154 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2155 optional_loop_out = i;
2162 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2163 CHECK_GE(rhs_lower_bound, 1);
2164 rhs_lower_bound = 1;
2167 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2169 double sum_outgoing = 0.0;
2172 for (
int i = 0; i < tails.size(); ++i) {
2173 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2174 sum_outgoing += literal_lp_values[i];
2175 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2180 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2182 if (num_optional_nodes_in == subset_size &&
2183 (optional_loop_in == -1 ||
2184 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2187 if (num_optional_nodes_out == num_nodes - subset_size &&
2188 (optional_loop_out == -1 ||
2189 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2194 if (num_optional_nodes_in == subset_size) {
2196 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2197 sum_outgoing += literal_lp_values[optional_loop_in];
2201 if (num_optional_nodes_out == num_nodes - subset_size) {
2202 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2204 sum_outgoing += literal_lp_values[optional_loop_out];
2208 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2209 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2222 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2223 const std::vector<Literal>& literals,
2227 if (num_nodes <= 2)
return;
2236 std::vector<Arc> relevant_arcs;
2239 std::vector<double> literal_lp_values(literals.size());
2240 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2242 for (
int i = 0; i < literals.size(); ++i) {
2244 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2246 lp_value = lp_values[direct_view];
2249 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2251 literal_lp_values[i] = lp_value;
2253 if (lp_value < 1e-6)
continue;
2254 relevant_arcs.push_back({tails[i], heads[i], lp_value});
2255 arc_by_decreasing_lp_values.push_back({lp_value, i});
2257 std::sort(arc_by_decreasing_lp_values.begin(),
2258 arc_by_decreasing_lp_values.end(),
2259 std::greater<std::pair<double, int>>());
2269 int num_components = num_nodes;
2270 std::vector<int> parent(num_nodes);
2271 std::vector<int> root(num_nodes);
2272 for (
int i = 0; i < num_nodes; ++i) {
2276 auto get_root_and_compress_path = [&root](
int node) {
2278 while (root[r] != r) r = root[r];
2279 while (root[node] != r) {
2280 const int next = root[node];
2286 for (
const auto pair : arc_by_decreasing_lp_values) {
2287 if (num_components == 2)
break;
2288 const int tail = get_root_and_compress_path(tails[pair.second]);
2289 const int head = get_root_and_compress_path(heads[pair.second]);
2293 const int new_node = parent.size();
2294 parent.push_back(new_node);
2295 parent[
head] = new_node;
2296 parent[
tail] = new_node;
2300 root.push_back(new_node);
2301 root[
head] = new_node;
2302 root[
tail] = new_node;
2313 std::vector<int> pre_order(num_nodes);
2314 std::vector<absl::Span<const int>> subsets;
2316 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2317 for (
int i = 0; i < parent.size(); ++i) {
2318 if (parent[i] != i) graph[parent[i]].push_back(i);
2320 std::vector<int> queue;
2321 std::vector<bool> seen(graph.size(),
false);
2322 std::vector<int> start_index(parent.size());
2323 for (
int i = num_nodes; i < parent.size(); ++i) {
2327 CHECK(graph[i].empty() || graph[i].size() == 2);
2328 if (parent[i] != i)
continue;
2333 while (!queue.empty()) {
2334 const int node = queue.back();
2339 const int start = start_index[node];
2340 if (new_size - start > 1) {
2341 subsets.emplace_back(&pre_order[start], new_size - start);
2346 start_index[node] = new_size;
2347 if (node < num_nodes) pre_order[new_size++] = node;
2348 for (
const int child : graph[node]) {
2349 if (!seen[child]) queue.push_back(child);
2357 int64 total_demands = 0;
2358 if (!demands.empty()) {
2363 CHECK_EQ(pre_order.size(), num_nodes);
2364 std::vector<bool> in_subset(num_nodes,
false);
2365 for (
const absl::Span<const int> subset : subsets) {
2366 CHECK_GT(subset.size(), 1);
2367 CHECK_LT(subset.size(), num_nodes);
2370 bool contain_depot =
false;
2371 int64 subset_demand = 0;
2374 for (
const int n : subset) {
2375 in_subset[n] =
true;
2376 if (!demands.empty()) {
2377 if (n == 0) contain_depot =
true;
2378 subset_demand += demands[n];
2395 int64 min_outgoing_flow = 1;
2396 if (!demands.empty()) {
2418 double outgoing_flow = 0.0;
2419 for (
const auto arc : relevant_arcs) {
2420 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2421 outgoing_flow += arc.lp_value;
2426 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2427 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2428 literals, literal_lp_values,
2429 min_outgoing_flow, lp_values, manager,
2434 for (
const int n : subset) in_subset[n] =
false;
2441 std::vector<IntegerVariable> GetAssociatedVariables(
2442 const std::vector<Literal>& literals, Model*
model) {
2443 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2444 std::vector<IntegerVariable> result;
2445 for (
const Literal l : literals) {
2446 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2448 result.push_back(direct_view);
2450 result.push_back(encoder->GetLiteralView(l.Negated()));
2463 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2464 const std::vector<Literal>& literals,
Model*
model) {
2466 result.
vars = GetAssociatedVariables(literals,
model);
2468 [num_nodes, tails, heads, literals,
model](
2472 num_nodes, tails, heads, literals, lp_values,
2473 {}, 0, manager,
model);
2479 const std::vector<int>& tails,
2480 const std::vector<int>& heads,
2481 const std::vector<Literal>& literals,
2482 const std::vector<int64>& demands,
2485 result.
vars = GetAssociatedVariables(literals,
model);
2487 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2491 lp_values, demands,
capacity, manager,
2497 std::function<LiteralIndex()>
2502 std::vector<IntegerVariable> variables;
2503 for (IntegerVariable
var : integer_variables_) {
2506 variables.push_back(
var);
2509 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2512 return [
this, variables, integer_trail, integer_encoder]() {
2516 double fractional_distance_best = -1.0;
2517 for (
const IntegerVariable
var : variables) {
2522 if (lb == ub)
continue;
2526 const double fractional_distance =
2528 lp_value - std::floor(lp_value +
kEpsilon));
2529 if (fractional_distance <
kEpsilon)
continue;
2532 if (fractional_distance > fractional_distance_best) {
2533 fractional_var =
var;
2534 fractional_distance_best = fractional_distance;
2539 return integer_encoder
2548 std::function<LiteralIndex()>
2551 std::vector<IntegerVariable> variables;
2552 for (IntegerVariable
var : integer_variables_) {
2555 variables.push_back(
var);
2558 VLOG(1) <<
"HeuristicLPPseudoCostBinary has " << variables.size()
2564 const int num_vars = variables.size();
2565 std::vector<double> cost_to_zero(num_vars, 0.0);
2566 std::vector<int> num_cost_to_zero(num_vars);
2570 return [=]()
mutable {
2575 if (num_calls == 10000) {
2576 for (
int i = 0; i < num_vars; i++) {
2577 cost_to_zero[i] /= 2;
2578 num_cost_to_zero[i] /= 2;
2584 for (
int i = 0; i < num_vars; i++) {
2585 const IntegerVariable
var = variables[i];
2590 if (lb == ub)
continue;
2594 if (std::abs(rc) <
kEpsilon)
continue;
2597 if (
value == 1.0 && rc < 0.0) {
2598 cost_to_zero[i] -= rc;
2599 num_cost_to_zero[i]++;
2604 int selected_index = -1;
2605 double best_cost = 0.0;
2606 for (
int i = 0; i < num_vars; i++) {
2607 const IntegerVariable
var = variables[i];
2612 if (num_cost_to_zero[i] > 0 &&
2613 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2614 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2619 if (selected_index >= 0) {
2623 return decision.
Index();
2630 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2631 const int num_vars = integer_variables_.size();
2632 if (sum_cost_down_.size() < num_vars) {
2633 sum_cost_down_.resize(num_vars, 0.0);
2634 num_cost_down_.resize(num_vars, 0);
2635 sum_cost_up_.resize(num_vars, 0.0);
2636 num_cost_up_.resize(num_vars, 0);
2637 rc_scores_.resize(num_vars, 0.0);
2641 num_calls_since_reduced_cost_averages_reset_++;
2642 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2643 for (
int i = 0; i < num_vars; i++) {
2644 sum_cost_up_[i] /= 2;
2645 num_cost_up_[i] /= 2;
2646 sum_cost_down_[i] /= 2;
2647 num_cost_down_[i] /= 2;
2649 num_calls_since_reduced_cost_averages_reset_ = 0;
2653 for (
int i = 0; i < num_vars; i++) {
2654 const IntegerVariable
var = integer_variables_[i];
2661 const double rc = lp_reduced_cost_[i];
2662 if (std::abs(rc) < kCpEpsilon)
continue;
2665 sum_cost_down_[i] -= rc;
2666 num_cost_down_[i]++;
2668 sum_cost_up_[i] += rc;
2675 rc_rev_int_repository_.
SetLevel(0);
2681 positions_by_decreasing_rc_score_.clear();
2682 for (
int i = 0; i < num_vars; i++) {
2687 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2688 const double a_down =
2689 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2690 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2691 rc_scores_[i] =
std::min(a_up, a_down);
2693 rc_scores_[i] = 0.5 * (a_down + a_up);
2698 if (rc_scores_[i] > 0.0) {
2699 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2702 std::sort(positions_by_decreasing_rc_score_.begin(),
2703 positions_by_decreasing_rc_score_.end());
2706 std::function<LiteralIndex()>
2708 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2711 LiteralIndex LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2713 int selected_index = -1;
2714 const int size = positions_by_decreasing_rc_score_.size();
2715 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2716 for (
int i = rev_rc_start_; i < size; ++i) {
2717 const int index = positions_by_decreasing_rc_score_[i].second;
2718 const IntegerVariable
var = integer_variables_[
index];
2721 selected_index =
index;
2727 const IntegerVariable
var = integer_variables_[selected_index];
2734 const IntegerValue value_ceil(
2736 if (value_ceil >= ub) {
2740 return result.Index();
2746 const IntegerValue value_floor(
2748 if (value_floor <= lb) {
2752 <<
" " << lb <<
" " << ub;
2753 return result.
Index();
2759 num_cost_up_[selected_index] > 0
2760 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2762 const double a_down =
2763 num_cost_down_[selected_index] > 0
2764 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2766 if (a_down < a_up) {
2770 return result.Index();
2775 return result.Index();