24 #include "absl/container/flat_hash_map.h"
25 #include "absl/numeric/int128.h"
49 const double LinearProgrammingConstraint::kCpEpsilon = 1e-4;
50 const double LinearProgrammingConstraint::kLpEpsilon = 1e-6;
55 : constraint_manager_(
model),
56 sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
64 implied_bounds_processor_({}, integer_trail_,
67 expanded_lp_solution_(
73 if (sat_parameters_.use_branching_in_lp() ||
74 sat_parameters_.search_branching() == SatParameters::LP_SEARCH) {
75 compute_reduced_cost_averages_ =
true;
79 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
83 VLOG(1) <<
"Total number of simplex iterations: "
84 << total_num_simplex_iterations_;
89 DCHECK(!lp_constraint_is_registered_);
90 constraint_manager_.
Add(
ct);
97 for (
const IntegerVariable
var :
ct.vars) {
102 glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
103 IntegerVariable positive_variable) {
105 const auto it = mirror_lp_variable_.find(positive_variable);
106 if (it == mirror_lp_variable_.end()) {
107 const glop::ColIndex
col(integer_variables_.size());
109 mirror_lp_variable_[positive_variable] =
col;
110 integer_variables_.push_back(positive_variable);
111 lp_solution_.push_back(std::numeric_limits<double>::infinity());
112 lp_reduced_cost_.push_back(0.0);
113 (*dispatcher_)[positive_variable] =
this;
117 if (
index >= expanded_lp_solution_.
size()) {
126 IntegerValue coeff) {
127 CHECK(!lp_constraint_is_registered_);
128 objective_is_defined_ =
true;
130 if (ivar != pos_var) coeff = -coeff;
133 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
134 integer_objective_.push_back({
col, coeff});
135 objective_infinity_norm_ =
152 bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
155 infinity_norms_.
clear();
156 const auto& all_constraints = constraint_manager_.
AllConstraints();
160 integer_lp_.
push_back(LinearConstraintInternal());
161 LinearConstraintInternal& new_ct = integer_lp_.
back();
164 const int size =
ct.vars.size();
165 IntegerValue infinity_norm(0);
167 LOG(INFO) <<
"Trivial infeasible bound in an LP constraint";
176 for (
int i = 0; i < size; ++i) {
178 IntegerVariable
var =
ct.vars[i];
179 IntegerValue coeff =
ct.coeffs[i];
185 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var), coeff});
187 infinity_norms_.
push_back(infinity_norm);
190 std::sort(new_ct.terms.begin(), new_ct.terms.end());
195 for (
int i = 0; i < integer_variables_.size(); ++i) {
198 for (
const auto entry : integer_objective_) {
201 for (
const LinearConstraintInternal&
ct : integer_lp_) {
204 for (
const auto& term :
ct.terms) {
216 const int num_vars = integer_variables_.size();
217 for (
int i = 0; i < num_vars; i++) {
218 const IntegerVariable cp_var = integer_variables_[i];
224 scaler_.
Scale(&lp_data_);
225 UpdateBoundsOfLpVariables();
231 <<
" Managed constraints.";
235 LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
237 glop::BasisState basis_state = simplex_.
GetState();
239 const glop::Status status = simplex_.
Solve(lp_data_, time_limit_);
243 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
252 info.new_obj_bound = IntegerValue(
253 static_cast<int64>(std::ceil(info.lp_objective - kCpEpsilon)));
258 void LinearProgrammingConstraint::FillReducedCostReasonIn(
260 std::vector<IntegerLiteral>* integer_reason) {
261 integer_reason->clear();
262 const int num_vars = integer_variables_.size();
263 for (
int i = 0; i < num_vars; i++) {
264 const double rc = reduced_costs[glop::ColIndex(i)];
265 if (rc > kLpEpsilon) {
266 integer_reason->push_back(
268 }
else if (rc < -kLpEpsilon) {
269 integer_reason->push_back(
277 bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
279 DCHECK(lp_solution_is_set_);
281 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
284 integer_reason_.clear();
286 bool deductions_were_made =
false;
288 UpdateBoundsOfLpVariables();
290 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
294 const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
298 if (current_value < current_lb || current_value > current_ub) {
303 const double new_ub = std::floor(current_value);
306 LPSolveInfo lower_branch_info = SolveLpForBranching();
316 positive_var, IntegerValue(std::ceil(current_value)));
317 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
320 deductions_were_made =
true;
321 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
326 const double new_lb = std::ceil(current_value);
329 LPSolveInfo upper_branch_info = SolveLpForBranching();
333 return deductions_were_made;
340 positive_var, IntegerValue(std::floor(current_value)));
341 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
342 return deductions_were_made;
344 deductions_were_made =
true;
346 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
347 return deductions_were_made;
356 approximate_obj_lb = upper_branch_info.new_obj_bound;
358 approximate_obj_lb = lower_branch_info.new_obj_bound;
360 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
361 upper_branch_info.new_obj_bound);
366 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
369 const IntegerLiteral deduction =
371 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
372 return deductions_were_made;
379 DCHECK(!lp_constraint_is_registered_);
380 lp_constraint_is_registered_ =
true;
385 std::sort(integer_objective_.begin(), integer_objective_.end());
388 if (!sat_parameters_.add_lp_constraints_lazily()) {
391 if (!CreateLpFromConstraintManager()) {
397 const int watcher_id = watcher->
Register(
this);
398 const int num_vars = integer_variables_.size();
399 for (
int i = 0; i < num_vars; i++) {
402 if (objective_is_defined_) {
408 if (integer_variables_.size() >= 20) {
421 optimal_constraints_.resize(rev_optimal_constraints_size_);
422 if (lp_solution_is_set_ && level < lp_solution_level_) {
423 lp_solution_is_set_ =
false;
431 if (level == 0 && !level_zero_lp_solution_.empty()) {
432 lp_solution_is_set_ =
true;
433 lp_solution_ = level_zero_lp_solution_;
434 lp_solution_level_ = 0;
435 for (
int i = 0; i < lp_solution_.size(); i++) {
436 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
437 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
444 for (
const IntegerVariable
var : generator.
vars) {
447 cut_generators_.push_back(std::move(generator));
451 const std::vector<int>& watch_indices) {
452 if (!lp_solution_is_set_)
return Propagate();
462 for (
const int index : watch_indices) {
468 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
483 glop::ColIndex
var) {
488 IntegerVariable variable)
const {
489 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
493 IntegerVariable variable)
const {
494 return lp_reduced_cost_[
gtl::FindOrDie(mirror_lp_variable_, variable)
498 void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
499 const int num_vars = integer_variables_.size();
500 for (
int i = 0; i < num_vars; i++) {
501 const IntegerVariable cp_var = integer_variables_[i];
509 bool LinearProgrammingConstraint::SolveLp() {
511 lp_at_level_zero_is_final_ =
false;
514 const auto status = simplex_.
Solve(lp_data_, time_limit_);
517 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
521 average_degeneracy_.
AddData(CalculateDegeneracy());
523 VLOG(2) <<
"High average degeneracy: "
528 lp_solution_is_set_ =
true;
530 const int num_vars = integer_variables_.size();
531 for (
int i = 0; i < num_vars; i++) {
533 GetVariableValueAtCpScale(glop::ColIndex(i));
534 lp_solution_[i] =
value;
535 expanded_lp_solution_[integer_variables_[i]] =
value;
539 if (lp_solution_level_ == 0) {
540 level_zero_lp_solution_ = lp_solution_;
546 void LinearProgrammingConstraint::ConvertToLinearConstraint(
548 IntegerValue upper_bound, LinearConstraint* result) {
549 result->vars.clear();
550 result->coeffs.clear();
551 const int size = dense_vector.
size();
552 for (ColIndex
col(0);
col < size; ++
col) {
553 const IntegerValue coeff = dense_vector[
col];
554 if (coeff == 0)
continue;
555 const IntegerVariable
var = integer_variables_[
col.value()];
556 result->vars.push_back(
var);
557 result->coeffs.push_back(coeff);
560 result->ub = upper_bound;
566 bool AddLinearExpressionMultiple(
567 IntegerValue multiplier,
568 const std::vector<std::pair<ColIndex, IntegerValue>>& terms,
570 for (
const std::pair<ColIndex, IntegerValue> term : terms) {
571 if (!
AddProductTo(multiplier, term.second, &(*dense_vector)[term.first])) {
580 bool LinearProgrammingConstraint::AddCutFromConstraints(
581 const std::string&
name,
582 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
593 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_dense_vector_,
595 VLOG(1) <<
"Issue, overflow!";
601 ConvertToLinearConstraint(tmp_dense_vector_, cut_ub, &cut_);
609 VLOG(1) <<
"Cut not tight " <<
ComputeActivity(cut_, expanded_lp_solution_)
619 const IntegerVariable first_new_var(expanded_lp_solution_.
size());
620 CHECK_EQ(first_new_var.value() % 2, 0);
622 LinearConstraint copy_in_debug;
624 copy_in_debug = cut_;
634 std::vector<ImpliedBoundsProcessor::SlackInfo> ib_slack_infos;
635 std::vector<LinearConstraint> implied_bound_cuts;
637 false, first_new_var,
638 expanded_lp_solution_, &cut_, &ib_slack_infos, &implied_bound_cuts);
639 for (LinearConstraint& ib_cut : implied_bound_cuts) {
642 constraint_manager_.
AddCut(ib_cut,
"IB", expanded_lp_solution_);
644 DCHECK(implied_bounds_processor_.
DebugSlack(first_new_var, copy_in_debug,
645 cut_, ib_slack_infos));
653 tmp_lp_values_.clear();
654 tmp_var_lbs_.clear();
655 tmp_var_ubs_.clear();
656 for (
const IntegerVariable
var : cut_.
vars) {
657 if (
var >= first_new_var) {
660 ib_slack_infos[(
var.value() - first_new_var.value()) / 2];
661 tmp_lp_values_.push_back(info.lp_value);
662 tmp_var_lbs_.push_back(info.lb);
663 tmp_var_ubs_.push_back(info.ub);
665 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
673 const IntegerVariable first_slack(first_new_var +
674 IntegerVariable(2 * ib_slack_infos.size()));
675 tmp_slack_rows_.clear();
676 tmp_slack_bounds_.clear();
677 for (
const auto pair : integer_multipliers) {
678 const RowIndex
row = pair.first;
679 const IntegerValue coeff = pair.second;
683 tmp_lp_values_.push_back(0.0);
684 cut_.
vars.push_back(first_slack +
685 2 * IntegerVariable(tmp_slack_rows_.size()));
686 tmp_slack_rows_.push_back(
row);
687 cut_.
coeffs.push_back(coeff);
689 const IntegerValue diff(
690 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value()));
692 tmp_slack_bounds_.push_back(integer_lp_[
row].ub);
693 tmp_var_lbs_.push_back(IntegerValue(0));
694 tmp_var_ubs_.push_back(diff);
696 tmp_slack_bounds_.push_back(integer_lp_[
row].lb);
697 tmp_var_lbs_.push_back(-diff);
698 tmp_var_ubs_.push_back(IntegerValue(0));
703 RoundingOptions options;
704 options.max_scaling = sat_parameters_.max_integer_rounding_scaling();
705 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_, tmp_var_lbs_,
707 &implied_bounds_processor_, &cut_);
712 double activity = 0.0;
713 for (
int i = 0; i < cut_.
vars.size(); ++i) {
714 if (cut_.
vars[i] < first_new_var) {
719 const double kMinViolation = 1e-4;
720 const double violation = activity -
ToDouble(cut_.
ub);
721 if (violation < kMinViolation) {
722 VLOG(3) <<
"Bad cut " << activity <<
" <= " <<
ToDouble(cut_.
ub);
729 tmp_dense_vector_.
assign(integer_variables_.size(), IntegerValue(0));
730 IntegerValue cut_ub = cut_.
ub;
731 bool overflow =
false;
732 for (
int i = 0; i < cut_.
vars.size(); ++i) {
733 const IntegerVariable
var = cut_.
vars[i];
736 if (
var < first_new_var) {
737 const glop::ColIndex
col =
740 tmp_dense_vector_[
col] += cut_.
coeffs[i];
742 tmp_dense_vector_[
col] -= cut_.
coeffs[i];
748 if (
var < first_slack) {
749 const IntegerValue multiplier = cut_.
coeffs[i];
750 const int index = (
var.value() - first_new_var.value()) / 2;
751 CHECK_LT(
index, ib_slack_infos.size());
753 std::vector<std::pair<ColIndex, IntegerValue>> terms;
754 for (
const std::pair<IntegerVariable, IntegerValue>& term :
755 ib_slack_infos[
index].terms) {
761 if (!AddLinearExpressionMultiple(multiplier, terms,
762 &tmp_dense_vector_)) {
775 const int slack_index = (
var.value() - first_slack.value()) / 2;
776 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
777 const IntegerValue multiplier = -cut_.
coeffs[i];
778 if (!AddLinearExpressionMultiple(multiplier, integer_lp_[
row].terms,
779 &tmp_dense_vector_)) {
785 if (!
AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
792 VLOG(1) <<
"Overflow in slack removal.";
796 VLOG(3) <<
" num_slack: " << num_slack;
797 ConvertToLinearConstraint(tmp_dense_vector_, cut_ub, &cut_);
801 const std::string extra_info = absl::StrCat(
802 "num_ib_substitutions=", ib_slack_infos.size(),
" num_lifted_booleans=",
805 const double new_violation =
807 if (std::abs(violation - new_violation) >= 1e-4) {
808 VLOG(1) <<
"Violation discrepancy after slack removal. "
809 <<
" before = " << violation <<
" after = " << new_violation;
813 return constraint_manager_.
AddCut(cut_,
name, expanded_lp_solution_,
817 void LinearProgrammingConstraint::AddCGCuts() {
820 for (RowIndex
row(0);
row < num_rows; ++
row) {
822 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
827 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
831 if (basis_col >= integer_variables_.size())
continue;
835 double magnitude = 0.0;
836 int num_non_zeros = 0;
837 for (RowIndex
row(0);
row < num_rows; ++
row) {
839 if (std::abs(lp_multipliers[
row]) < 1e-12) {
840 lp_multipliers[
row] = 0.0;
848 VLOG(1) <<
"BASIC row not expected! " << lp_multipliers[
row];
849 lp_multipliers[
row] = 0.0;
852 magnitude =
std::max(magnitude, std::abs(lp_multipliers[
row]));
853 if (lp_multipliers[
row] != 0.0) ++num_non_zeros;
855 if (num_non_zeros == 0)
continue;
858 for (
int i = 0; i < 2; ++i) {
864 for (RowIndex
row(0);
row < num_rows; ++
row) {
865 lp_multipliers[
row] = -lp_multipliers[
row];
871 const std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
872 ScaleLpMultiplier(
false,
873 lp_multipliers, &scaling, 52);
874 AddCutFromConstraints(
"CG", integer_multipliers);
882 void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
883 ModelRandomGenerator* random,
884 std::vector<std::pair<RowIndex, RowIndex>>* output) {
885 if (
a.empty() ||
b.empty())
return;
886 for (
const RowIndex
row :
a) {
887 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
889 output->push_back({
row, other});
894 template <
class ListOfTerms>
895 IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
896 for (
const auto& term : terms) {
897 if (term.first ==
col)
return term.second;
899 return IntegerValue(0);
904 void LinearProgrammingConstraint::AddMirCuts() {
923 SparseBitset<ColIndex> non_zeros(ColIndex(integer_variables_.size()));
928 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
930 for (RowIndex
row(0);
row < num_rows; ++
row) {
937 base_rows.push_back({
row, IntegerValue(1)});
941 base_rows.push_back({
row, IntegerValue(-1)});
962 std::vector<double> weights;
964 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
965 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
973 integer_multipliers = {entry};
974 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
979 for (
const ColIndex
col : non_zeros.PositionsSetAtLeastOnce()) {
980 dense_cut[
col] = IntegerValue(0);
982 non_zeros.SparseClearAll();
985 const IntegerValue multiplier = entry.second;
986 for (
const std::pair<ColIndex, IntegerValue> term :
987 integer_lp_[entry.first].terms) {
988 const ColIndex
col = term.first;
989 const IntegerValue coeff = term.second;
991 dense_cut[
col] += coeff * multiplier;
994 used_rows.
assign(num_rows.value(),
false);
995 used_rows[entry.first] =
true;
1000 const int kMaxAggregation = 5;
1001 for (
int i = 0; i < kMaxAggregation; ++i) {
1004 IntegerValue max_magnitude(0);
1006 std::vector<ColIndex> col_candidates;
1007 for (
const ColIndex
col : non_zeros.PositionsSetAtLeastOnce()) {
1008 if (dense_cut[
col] == 0)
continue;
1011 const int col_degree =
1013 if (col_degree <= 1)
continue;
1018 const IntegerVariable
var = integer_variables_[
col.value()];
1019 const double lp_value = expanded_lp_solution_[
var];
1022 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1023 if (bound_distance > 1e-2) {
1024 weights.push_back(bound_distance);
1025 col_candidates.push_back(
col);
1028 if (col_candidates.empty())
break;
1030 const ColIndex var_to_eliminate =
1031 col_candidates[std::discrete_distribution<>(weights.begin(),
1032 weights.end())(*random_)];
1035 std::vector<RowIndex> possible_rows;
1038 const RowIndex
row = entry.row();
1046 if (used_rows[
row])
continue;
1047 used_rows[
row] =
true;
1055 bool add_row =
false;
1058 if (entry.coefficient() > 0.0) {
1059 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1061 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1066 if (entry.coefficient() > 0.0) {
1067 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1069 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1073 possible_rows.push_back(
row);
1074 weights.push_back(row_weights[
row]);
1077 if (possible_rows.empty())
break;
1079 const RowIndex row_to_combine =
1080 possible_rows[std::discrete_distribution<>(weights.begin(),
1081 weights.end())(*random_)];
1082 const IntegerValue to_combine_coeff =
1083 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1084 CHECK_NE(to_combine_coeff, 0);
1086 IntegerValue mult1 = -to_combine_coeff;
1087 IntegerValue mult2 = dense_cut[var_to_eliminate];
1094 const IntegerValue gcd = IntegerValue(
1104 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1106 std::abs(mult2.value()))) ==
kint64max) {
1110 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1111 entry.second *= mult1;
1113 integer_multipliers.push_back({row_to_combine, mult2});
1116 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1117 integer_multipliers)) {
1123 if (i + 1 == kMaxAggregation)
break;
1125 for (ColIndex
col : non_zeros.PositionsSetAtLeastOnce()) {
1126 dense_cut[
col] *= mult1;
1128 for (
const std::pair<ColIndex, IntegerValue> term :
1129 integer_lp_[row_to_combine].terms) {
1130 const ColIndex
col = term.first;
1131 const IntegerValue coeff = term.second;
1133 dense_cut[
col] += coeff * mult2;
1139 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1140 const int64 min_iter,
const int64 max_iter) {
1141 if (sat_parameters_.linearization_level() < 2)
return;
1142 const int64 num_degenerate_columns = CalculateDegeneracy();
1144 if (num_cols <= 0) {
1147 CHECK_GT(num_cols, 0);
1148 const int64 decrease_factor = (10 * num_degenerate_columns) / num_cols;
1153 if (is_degenerate_) {
1156 next_simplex_iter_ *= 2;
1159 if (is_degenerate_) {
1160 next_simplex_iter_ /=
std::max(
int64{1}, 2 * decrease_factor);
1164 next_simplex_iter_ = num_cols / 40;
1167 next_simplex_iter_ =
1172 UpdateBoundsOfLpVariables();
1177 if ( (
false) && objective_is_defined_) {
1186 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1187 100.0 * kCpEpsilon));
1196 parameters.set_max_number_of_iterations(2000);
1198 parameters.set_max_number_of_iterations(next_simplex_iter_);
1200 if (sat_parameters_.use_exact_lp_reason()) {
1201 parameters.set_change_status_to_imprecise(
false);
1202 parameters.set_primal_feasibility_tolerance(1e-7);
1203 parameters.set_dual_feasibility_tolerance(1e-7);
1208 if (!SolveLp())
return true;
1211 const int max_cuts_rounds =
1213 ? sat_parameters_.max_cut_rounds_at_level_zero()
1217 cuts_round < max_cuts_rounds) {
1221 if (!integer_lp_.empty()) {
1228 if (sat_parameters_.add_mir_cuts()) AddMirCuts();
1229 if (sat_parameters_.add_cg_cuts()) AddCGCuts();
1233 if (!cut_generators_.empty() &&
1235 !sat_parameters_.only_add_cuts_at_level_zero())) {
1236 for (
const CutGenerator& generator : cut_generators_) {
1237 generator.generate_cuts(expanded_lp_solution_, &constraint_manager_);
1243 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1245 if (!CreateLpFromConstraintManager()) {
1249 if (!SolveLp())
return true;
1251 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1258 lp_at_level_zero_is_final_ =
true;
1266 if (sat_parameters_.use_exact_lp_reason()) {
1267 if (!FillExactDualRayReason())
return true;
1276 UpdateSimplexIterationLimit(10, 1000);
1279 if (objective_is_defined_ &&
1286 const IntegerValue approximate_new_lb(
1287 static_cast<int64>(std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1291 if (sat_parameters_.use_exact_lp_reason()) {
1292 if (!ExactLpReasonning())
return false;
1295 const IntegerValue propagated_lb =
1297 if (approximate_new_lb > propagated_lb) {
1298 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1300 <<
" ] approx_lb += "
1301 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1302 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1305 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1306 const double objective_cp_ub =
1308 ReducedCostStrengtheningDeductions(objective_cp_ub -
1309 relaxed_optimal_objective);
1310 if (!deductions_.empty()) {
1311 deductions_reason_ = integer_reason_;
1312 deductions_reason_.push_back(
1317 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1320 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1326 if (!deductions_.empty()) {
1327 const int trail_index_with_same_reason = integer_trail_->
Index();
1329 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1330 trail_index_with_same_reason)) {
1340 CHECK(lp_solution_is_set_);
1343 lp_solution_is_integer_ =
true;
1344 const int num_vars = integer_variables_.size();
1345 for (
int i = 0; i < num_vars; i++) {
1348 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1350 lp_solution_is_integer_ =
false;
1354 if (compute_reduced_cost_averages_) {
1355 UpdateAverageReducedCosts();
1359 if (sat_parameters_.use_branching_in_lp() && objective_is_defined_ &&
1361 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1362 sat_parameters_.linearization_level() >= 2 &&
1363 compute_reduced_cost_averages_ &&
1365 count_since_last_branching_++;
1366 if (count_since_last_branching_ < branching_frequency_) {
1369 count_since_last_branching_ = 0;
1370 bool branching_successful =
false;
1373 const int max_num_branches = 3;
1374 const int num_vars = integer_variables_.size();
1375 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1376 for (
int i = 0; i < num_vars; ++i) {
1377 const IntegerVariable
var = integer_variables_[i];
1382 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1396 const double cost_i = rc_scores_[i];
1397 std::pair<double, IntegerVariable> branching_var =
1398 std::make_pair(-cost_i, positive_var);
1399 auto iterator = std::lower_bound(branching_vars.begin(),
1400 branching_vars.end(), branching_var);
1402 branching_vars.insert(iterator, branching_var);
1403 if (branching_vars.size() > max_num_branches) {
1404 branching_vars.resize(max_num_branches);
1408 for (
const std::pair<double, IntegerVariable>& branching_var :
1410 const IntegerVariable positive_var = branching_var.second;
1411 VLOG(2) <<
"Branching on: " << positive_var;
1412 if (BranchOnVar(positive_var)) {
1413 VLOG(2) <<
"Branching successful.";
1414 branching_successful =
true;
1419 if (!branching_successful) {
1420 branching_frequency_ *= 2;
1429 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1431 IntegerValue lower_bound(0);
1432 const int size = terms.
vars.size();
1433 for (
int i = 0; i < size; ++i) {
1434 const IntegerVariable
var = terms.
vars[i];
1435 const IntegerValue coeff = terms.
coeffs[i];
1444 bool LinearProgrammingConstraint::PossibleOverflow(
1445 const LinearConstraint& constraint) {
1446 IntegerValue lower_bound(0);
1447 const int size = constraint.vars.size();
1448 for (
int i = 0; i < size; ++i) {
1449 const IntegerVariable
var = constraint.vars[i];
1450 const IntegerValue coeff = constraint.coeffs[i];
1458 const int64 slack =
CapAdd(lower_bound.value(), -constraint.ub.value());
1467 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1468 absl::int128 div128(positive_div.value());
1469 absl::int128 result = x / div128;
1470 if (result * div128 > x)
return result - 1;
1476 void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1485 const int size = constraint->vars.size();
1486 for (
int i = 0; i < size; ++i) {
1487 const IntegerVariable
var = constraint->vars[i];
1488 const double coeff =
ToDouble(constraint->coeffs[i]);
1494 const double max_value =
std::max(sum_max, -sum_min);
1496 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1497 if (divisor <= 1)
return;
1512 absl::int128 adjust = 0;
1513 for (
int i = 0; i < size; ++i) {
1514 const IntegerValue old_coeff = constraint->coeffs[i];
1515 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1518 const absl::int128 remainder =
1519 absl::int128(old_coeff.value()) -
1520 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1526 if (new_coeff == 0)
continue;
1527 constraint->vars[new_size] = constraint->vars[i];
1528 constraint->coeffs[new_size] = new_coeff;
1531 constraint->vars.resize(new_size);
1532 constraint->coeffs.resize(new_size);
1534 constraint->ub = IntegerValue(
static_cast<int64>(
1535 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1540 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1541 const LinearConstraint& terms, IntegerValue slack) {
1542 integer_reason_.clear();
1543 std::vector<IntegerValue> magnitudes;
1544 const int size = terms.vars.size();
1545 for (
int i = 0; i < size; ++i) {
1546 const IntegerVariable
var = terms.vars[i];
1547 const IntegerValue coeff = terms.coeffs[i];
1550 magnitudes.push_back(coeff);
1553 magnitudes.push_back(-coeff);
1565 std::vector<std::pair<RowIndex, IntegerValue>>
1566 LinearProgrammingConstraint::ScaleLpMultiplier(
1567 bool take_objective_into_account,
1569 int max_pow)
const {
1570 double max_sum = 0.0;
1571 std::vector<std::pair<RowIndex, Fractional>> cp_multipliers;
1572 for (RowIndex
row(0);
row < dense_lp_multipliers.size(); ++
row) {
1577 if (std::abs(lp_multi) < 1e-12)
continue;
1593 cp_multipliers.push_back({
row, cp_multi});
1594 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1599 if (take_objective_into_account) {
1600 max_sum +=
ToDouble(objective_infinity_norm_);
1603 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1604 if (max_sum == 0.0) {
1607 return integer_multipliers;
1612 const double threshold = std::ldexp(1, max_pow) / max_sum;
1614 while (2 * *scaling <= threshold) *scaling *= 2;
1619 for (
const auto entry : cp_multipliers) {
1620 const IntegerValue coeff(std::round(entry.second * (*scaling)));
1621 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1623 return integer_multipliers;
1626 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1627 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1629 IntegerValue* upper_bound)
const {
1632 dense_terms->
assign(integer_variables_.size(), IntegerValue(0));
1636 const ColIndex num_cols(integer_variables_.size());
1637 for (
const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1638 const RowIndex
row = term.first;
1639 const IntegerValue multiplier = term.second;
1640 CHECK_LT(
row, integer_lp_.size());
1643 if (!AddLinearExpressionMultiple(multiplier, integer_lp_[
row].terms,
1649 const IntegerValue
bound =
1650 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1658 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1659 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1661 IntegerValue* upper_bound)
const {
1662 const IntegerValue kMaxWantedCoeff(1e18);
1663 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1664 const RowIndex
row = term.first;
1665 const IntegerValue multiplier = term.second;
1666 if (multiplier == 0)
continue;
1670 IntegerValue negative_limit = kMaxWantedCoeff;
1671 IntegerValue positive_limit = kMaxWantedCoeff;
1675 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1676 if (multiplier > 0) {
1677 negative_limit =
std::min(negative_limit, multiplier);
1679 positive_limit =
std::min(positive_limit, -multiplier);
1684 const IntegerValue row_bound =
1685 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1686 if (row_bound != 0) {
1690 const IntegerValue limit2 =
1692 if (*upper_bound > 0 == row_bound > 0) {
1693 positive_limit =
std::min(positive_limit, limit1);
1694 negative_limit =
std::min(negative_limit, limit2);
1696 negative_limit =
std::min(negative_limit, limit1);
1697 positive_limit =
std::min(positive_limit, limit2);
1709 double positive_diff =
ToDouble(row_bound);
1710 double negative_diff =
ToDouble(row_bound);
1715 for (
const auto entry : integer_lp_[
row].terms) {
1716 const ColIndex
col = entry.first;
1717 const IntegerValue coeff = entry.second;
1718 const IntegerValue abs_coef =
IntTypeAbs(coeff);
1721 const IntegerVariable
var = integer_variables_[
col.value()];
1728 const IntegerValue current = (*dense_terms)[
col];
1730 const IntegerValue overflow_limit(
1732 positive_limit =
std::min(positive_limit, overflow_limit);
1733 negative_limit =
std::min(negative_limit, overflow_limit);
1750 const IntegerValue current_magnitude =
IntTypeAbs(current);
1751 const IntegerValue other_direction_limit =
FloorRatio(
1753 ? kMaxWantedCoeff +
std::min(current_magnitude,
1755 : current_magnitude,
1757 const IntegerValue same_direction_limit(
FloorRatio(
1758 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
1760 if (current > 0 == coeff > 0) {
1761 negative_limit =
std::min(negative_limit, other_direction_limit);
1762 positive_limit =
std::min(positive_limit, same_direction_limit);
1764 negative_limit =
std::min(negative_limit, same_direction_limit);
1765 positive_limit =
std::min(positive_limit, other_direction_limit);
1769 const IntegerValue implied = current > 0 ? lb : ub;
1780 IntegerValue to_add(0);
1781 if (positive_diff <= -1.0 && positive_limit > 0) {
1782 to_add = positive_limit;
1784 if (negative_diff >= 1.0 && negative_limit > 0) {
1787 std::abs(
ToDouble(negative_limit) * negative_diff) >
1788 std::abs(
ToDouble(positive_limit) * positive_diff)) {
1789 to_add = -negative_limit;
1793 term.second += to_add;
1794 *upper_bound += to_add * row_bound;
1795 for (
const auto entry : integer_lp_[
row].terms) {
1796 const ColIndex
col = entry.first;
1797 const IntegerValue coeff = entry.second;
1798 (*dense_terms)[
col] += to_add * coeff;
1818 bool LinearProgrammingConstraint::ExactLpReasonning() {
1820 integer_reason_.clear();
1821 deductions_.clear();
1822 deductions_reason_.clear();
1829 for (RowIndex
row(0);
row < num_rows; ++
row) {
1834 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
1835 ScaleLpMultiplier(
true, lp_multipliers,
1839 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_dense_vector_,
1841 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
1847 const IntegerValue obj_scale(std::round(scaling));
1848 if (obj_scale == 0) {
1849 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
1852 CHECK(AddLinearExpressionMultiple(obj_scale, integer_objective_,
1853 &tmp_dense_vector_));
1855 AdjustNewLinearConstraint(&integer_multipliers, &tmp_dense_vector_, &rc_ub);
1859 LinearConstraint new_constraint;
1860 ConvertToLinearConstraint(tmp_dense_vector_, rc_ub, &new_constraint);
1861 new_constraint.vars.push_back(objective_cp_);
1862 new_constraint.coeffs.push_back(-obj_scale);
1864 PreventOverflow(&new_constraint);
1865 DCHECK(!PossibleOverflow(new_constraint));
1868 IntegerSumLE* cp_constraint =
1869 new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
1870 new_constraint.ub, model_);
1874 optimal_constraints_.clear();
1876 optimal_constraints_.emplace_back(cp_constraint);
1877 rev_optimal_constraints_size_ = optimal_constraints_.size();
1878 return cp_constraint->Propagate();
1881 bool LinearProgrammingConstraint::FillExactDualRayReason() {
1883 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
1884 ScaleLpMultiplier(
false,
1887 IntegerValue new_constraint_ub;
1888 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_dense_vector_,
1889 &new_constraint_ub)) {
1890 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
1894 AdjustNewLinearConstraint(&integer_multipliers, &tmp_dense_vector_,
1895 &new_constraint_ub);
1897 LinearConstraint new_constraint;
1898 ConvertToLinearConstraint(tmp_dense_vector_, new_constraint_ub,
1901 PreventOverflow(&new_constraint);
1902 DCHECK(!PossibleOverflow(new_constraint));
1905 const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
1906 if (implied_lb <= new_constraint.ub) {
1907 VLOG(1) <<
"LP exact dual ray not infeasible,"
1908 <<
" implied_lb: " << implied_lb.value() / scaling
1909 <<
" ub: " << new_constraint.ub.value() / scaling;
1912 const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
1913 SetImpliedLowerBoundReason(new_constraint, slack);
1917 int64 LinearProgrammingConstraint::CalculateDegeneracy() {
1919 int num_non_basic_with_zero_rc = 0;
1920 for (glop::ColIndex i(0); i < num_vars; ++i) {
1922 if (rc != 0.0)
continue;
1926 num_non_basic_with_zero_rc++;
1929 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
1930 return num_non_basic_with_zero_rc;
1933 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
1934 double cp_objective_delta) {
1935 deductions_.clear();
1940 const double lp_objective_delta =
1942 const int num_vars = integer_variables_.size();
1943 for (
int i = 0; i < num_vars; i++) {
1944 const IntegerVariable cp_var = integer_variables_[i];
1945 const glop::ColIndex lp_var = glop::ColIndex(i);
1949 if (rc == 0.0)
continue;
1950 const double lp_other_bound =
value + lp_objective_delta / rc;
1951 const double cp_other_bound =
1954 if (rc > kLpEpsilon) {
1956 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
1961 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
1964 }
else if (rc < -kLpEpsilon) {
1966 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
1968 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
1969 deductions_.push_back(
1983 void AddOutgoingCut(
int num_nodes,
int subset_size,
1984 const std::vector<bool>& in_subset,
1985 const std::vector<int>& tails,
1986 const std::vector<int>& heads,
1987 const std::vector<Literal>& literals,
1988 const std::vector<double>& literal_lp_values,
1989 int64 rhs_lower_bound,
1991 LinearConstraintManager* manager, Model*
model) {
1998 int num_optional_nodes_in = 0;
1999 int num_optional_nodes_out = 0;
2000 int optional_loop_in = -1;
2001 int optional_loop_out = -1;
2002 for (
int i = 0; i < tails.size(); ++i) {
2003 if (tails[i] != heads[i])
continue;
2004 if (in_subset[tails[i]]) {
2005 num_optional_nodes_in++;
2006 if (optional_loop_in == -1 ||
2007 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2008 optional_loop_in = i;
2011 num_optional_nodes_out++;
2012 if (optional_loop_out == -1 ||
2013 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2014 optional_loop_out = i;
2021 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2022 CHECK_GE(rhs_lower_bound, 1);
2023 rhs_lower_bound = 1;
2026 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2028 double sum_outgoing = 0.0;
2031 for (
int i = 0; i < tails.size(); ++i) {
2032 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2033 sum_outgoing += literal_lp_values[i];
2034 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2039 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2041 if (num_optional_nodes_in == subset_size &&
2042 (optional_loop_in == -1 ||
2043 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2046 if (num_optional_nodes_out == num_nodes - subset_size &&
2047 (optional_loop_out == -1 ||
2048 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2053 if (num_optional_nodes_in == subset_size) {
2055 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2056 sum_outgoing += literal_lp_values[optional_loop_in];
2060 if (num_optional_nodes_out == num_nodes - subset_size) {
2061 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2063 sum_outgoing += literal_lp_values[optional_loop_out];
2067 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2068 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2081 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2082 const std::vector<Literal>& literals,
2086 if (num_nodes <= 2)
return;
2095 std::vector<Arc> relevant_arcs;
2098 std::vector<double> literal_lp_values(literals.size());
2099 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2101 for (
int i = 0; i < literals.size(); ++i) {
2103 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2105 lp_value = lp_values[direct_view];
2108 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2110 literal_lp_values[i] = lp_value;
2112 if (lp_value < 1e-6)
continue;
2113 relevant_arcs.push_back({tails[i], heads[i], lp_value});
2114 arc_by_decreasing_lp_values.push_back({lp_value, i});
2116 std::sort(arc_by_decreasing_lp_values.begin(),
2117 arc_by_decreasing_lp_values.end(),
2118 std::greater<std::pair<double, int>>());
2128 int num_components = num_nodes;
2129 std::vector<int> parent(num_nodes);
2130 std::vector<int> root(num_nodes);
2131 for (
int i = 0; i < num_nodes; ++i) {
2135 auto get_root_and_compress_path = [&root](
int node) {
2137 while (root[r] != r) r = root[r];
2138 while (root[node] != r) {
2139 const int next = root[node];
2145 for (
const auto pair : arc_by_decreasing_lp_values) {
2146 if (num_components == 2)
break;
2147 const int tail = get_root_and_compress_path(tails[pair.second]);
2148 const int head = get_root_and_compress_path(heads[pair.second]);
2152 const int new_node = parent.size();
2153 parent.push_back(new_node);
2154 parent[
head] = new_node;
2155 parent[
tail] = new_node;
2159 root.push_back(new_node);
2160 root[
head] = new_node;
2161 root[
tail] = new_node;
2172 std::vector<int> pre_order(num_nodes);
2173 std::vector<absl::Span<const int>> subsets;
2175 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2176 for (
int i = 0; i < parent.size(); ++i) {
2177 if (parent[i] != i) graph[parent[i]].push_back(i);
2179 std::vector<int> queue;
2180 std::vector<bool> seen(graph.size(),
false);
2181 std::vector<int> start_index(parent.size());
2182 for (
int i = num_nodes; i < parent.size(); ++i) {
2186 CHECK(graph[i].empty() || graph[i].size() == 2);
2187 if (parent[i] != i)
continue;
2192 while (!queue.empty()) {
2193 const int node = queue.back();
2198 const int start = start_index[node];
2199 if (new_size - start > 1) {
2200 subsets.emplace_back(&pre_order[start], new_size - start);
2205 start_index[node] = new_size;
2206 if (node < num_nodes) pre_order[new_size++] = node;
2207 for (
const int child : graph[node]) {
2208 if (!seen[child]) queue.push_back(child);
2216 int64 total_demands = 0;
2217 if (!demands.empty()) {
2222 CHECK_EQ(pre_order.size(), num_nodes);
2223 std::vector<bool> in_subset(num_nodes,
false);
2224 for (
const absl::Span<const int> subset : subsets) {
2225 CHECK_GT(subset.size(), 1);
2226 CHECK_LT(subset.size(), num_nodes);
2229 bool contain_depot =
false;
2230 int64 subset_demand = 0;
2233 for (
const int n : subset) {
2234 in_subset[n] =
true;
2235 if (!demands.empty()) {
2236 if (n == 0) contain_depot =
true;
2237 subset_demand += demands[n];
2254 int64 min_outgoing_flow = 1;
2255 if (!demands.empty()) {
2277 double outgoing_flow = 0.0;
2278 for (
const auto arc : relevant_arcs) {
2279 if (in_subset[arc.tail] && !in_subset[arc.head]) {
2280 outgoing_flow += arc.lp_value;
2285 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2286 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2287 literals, literal_lp_values,
2288 min_outgoing_flow, lp_values, manager,
2293 for (
const int n : subset) in_subset[n] =
false;
2300 std::vector<IntegerVariable> GetAssociatedVariables(
2301 const std::vector<Literal>& literals, Model*
model) {
2302 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2303 std::vector<IntegerVariable> result;
2304 for (
const Literal l : literals) {
2305 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2307 result.push_back(direct_view);
2309 result.push_back(encoder->GetLiteralView(l.Negated()));
2322 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2323 const std::vector<Literal>& literals,
Model*
model) {
2325 result.
vars = GetAssociatedVariables(literals,
model);
2327 [num_nodes, tails, heads, literals,
model](
2331 num_nodes, tails, heads, literals, lp_values,
2332 {}, 0, manager,
model);
2338 const std::vector<int>& tails,
2339 const std::vector<int>& heads,
2340 const std::vector<Literal>& literals,
2341 const std::vector<int64>& demands,
2344 result.
vars = GetAssociatedVariables(literals,
model);
2346 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2350 lp_values, demands,
capacity, manager,
2356 std::function<LiteralIndex()>
2361 std::vector<IntegerVariable> variables;
2362 for (IntegerVariable
var : integer_variables_) {
2365 variables.push_back(
var);
2368 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2371 return [
this, variables, integer_trail, integer_encoder]() {
2375 double fractional_distance_best = -1.0;
2376 for (
const IntegerVariable
var : variables) {
2381 if (lb == ub)
continue;
2385 const double fractional_distance =
2387 lp_value - std::floor(lp_value +
kEpsilon));
2388 if (fractional_distance <
kEpsilon)
continue;
2391 if (fractional_distance > fractional_distance_best) {
2392 fractional_var =
var;
2393 fractional_distance_best = fractional_distance;
2398 return integer_encoder
2407 std::function<LiteralIndex()>
2410 std::vector<IntegerVariable> variables;
2411 for (IntegerVariable
var : integer_variables_) {
2414 variables.push_back(
var);
2417 VLOG(1) <<
"HeuristicLPPseudoCostBinary has " << variables.size()
2423 const int num_vars = variables.size();
2424 std::vector<double> cost_to_zero(num_vars, 0.0);
2425 std::vector<int> num_cost_to_zero(num_vars);
2429 return [=]()
mutable {
2434 if (num_calls == 10000) {
2435 for (
int i = 0; i < num_vars; i++) {
2436 cost_to_zero[i] /= 2;
2437 num_cost_to_zero[i] /= 2;
2443 for (
int i = 0; i < num_vars; i++) {
2444 const IntegerVariable
var = variables[i];
2449 if (lb == ub)
continue;
2453 if (std::abs(rc) <
kEpsilon)
continue;
2456 if (
value == 1.0 && rc < 0.0) {
2457 cost_to_zero[i] -= rc;
2458 num_cost_to_zero[i]++;
2463 int selected_index = -1;
2464 double best_cost = 0.0;
2465 for (
int i = 0; i < num_vars; i++) {
2466 const IntegerVariable
var = variables[i];
2471 if (num_cost_to_zero[i] > 0 &&
2472 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2473 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2478 if (selected_index >= 0) {
2482 return decision.
Index();
2489 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2490 const int num_vars = integer_variables_.size();
2491 if (sum_cost_down_.size() < num_vars) {
2492 sum_cost_down_.resize(num_vars, 0.0);
2493 num_cost_down_.resize(num_vars, 0);
2494 sum_cost_up_.resize(num_vars, 0.0);
2495 num_cost_up_.resize(num_vars, 0);
2496 rc_scores_.resize(num_vars, 0.0);
2500 num_calls_since_reduced_cost_averages_reset_++;
2501 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2502 for (
int i = 0; i < num_vars; i++) {
2503 sum_cost_up_[i] /= 2;
2504 num_cost_up_[i] /= 2;
2505 sum_cost_down_[i] /= 2;
2506 num_cost_down_[i] /= 2;
2508 num_calls_since_reduced_cost_averages_reset_ = 0;
2512 for (
int i = 0; i < num_vars; i++) {
2513 const IntegerVariable
var = integer_variables_[i];
2520 const double rc = lp_reduced_cost_[i];
2521 if (std::abs(rc) < kCpEpsilon)
continue;
2524 sum_cost_down_[i] -= rc;
2525 num_cost_down_[i]++;
2527 sum_cost_up_[i] += rc;
2534 rc_rev_int_repository_.
SetLevel(0);
2540 positions_by_decreasing_rc_score_.clear();
2541 for (
int i = 0; i < num_vars; i++) {
2546 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2547 const double a_down =
2548 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2549 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2550 rc_scores_[i] =
std::min(a_up, a_down);
2552 rc_scores_[i] = 0.5 * (a_down + a_up);
2557 if (rc_scores_[i] > 0.0) {
2558 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2561 std::sort(positions_by_decreasing_rc_score_.begin(),
2562 positions_by_decreasing_rc_score_.end());
2565 std::function<LiteralIndex()>
2567 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2570 LiteralIndex LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2572 int selected_index = -1;
2573 const int size = positions_by_decreasing_rc_score_.size();
2574 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2575 for (
int i = rev_rc_start_; i < size; ++i) {
2576 const int index = positions_by_decreasing_rc_score_[i].second;
2577 const IntegerVariable
var = integer_variables_[
index];
2580 selected_index =
index;
2586 const IntegerVariable
var = integer_variables_[selected_index];
2593 const IntegerValue value_ceil(
2595 if (value_ceil >= ub) {
2599 return result.Index();
2605 const IntegerValue value_floor(
2607 if (value_floor <= lb) {
2611 <<
" " << lb <<
" " << ub;
2612 return result.
Index();
2618 num_cost_up_[selected_index] > 0
2619 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2621 const double a_down =
2622 num_cost_down_[selected_index] > 0
2623 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2625 if (a_down < a_up) {
2629 return result.Index();
2634 return result.Index();