28#include "absl/container/flat_hash_map.h"
29#include "absl/container/inlined_vector.h"
30#include "absl/meta/type_traits.h"
31#include "absl/numeric/int128.h"
32#include "absl/random/distributions.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.h"
39#include "ortools/glop/parameters.pb.h"
55#include "ortools/sat/sat_parameters.pb.h"
74 for (
const glop::ColIndex
col : non_zeros_) {
75 dense_vector_[
col] = IntegerValue(0);
77 dense_vector_.
resize(size, IntegerValue(0));
79 dense_vector_.
assign(size, IntegerValue(0));
81 for (
const glop::ColIndex
col : non_zeros_) {
82 is_zeros_[
col] =
true;
84 is_zeros_.
resize(size,
true);
90 const int64_t add =
CapAdd(
value.value(), dense_vector_[
col].value());
94 dense_vector_[
col] = IntegerValue(add);
95 if (is_sparse_ && is_zeros_[
col]) {
96 is_zeros_[
col] =
false;
97 non_zeros_.push_back(
col);
103 IntegerValue multiplier,
104 const std::vector<std::pair<glop::ColIndex, IntegerValue>>& terms) {
105 const double threshold = 0.1 *
static_cast<double>(dense_vector_.
size());
106 if (is_sparse_ &&
static_cast<double>(terms.size()) < threshold) {
107 for (
const std::pair<glop::ColIndex, IntegerValue>& term : terms) {
108 if (is_zeros_[term.first]) {
109 is_zeros_[term.first] =
false;
110 non_zeros_.push_back(term.first);
112 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
116 if (
static_cast<double>(non_zeros_.size()) > threshold) {
121 for (
const std::pair<glop::ColIndex, IntegerValue>& term : terms) {
122 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
131 const std::vector<IntegerVariable>& integer_variables,
133 result->
vars.clear();
136 std::sort(non_zeros_.begin(), non_zeros_.end());
137 for (
const glop::ColIndex
col : non_zeros_) {
138 const IntegerValue
coeff = dense_vector_[
col];
139 if (
coeff == 0)
continue;
140 result->
vars.push_back(integer_variables[
col.value()]);
144 const int size = dense_vector_.
size();
145 for (glop::ColIndex
col(0);
col < size; ++
col) {
146 const IntegerValue
coeff = dense_vector_[
col];
147 if (
coeff == 0)
continue;
148 result->
vars.push_back(integer_variables[
col.value()]);
156std::vector<std::pair<glop::ColIndex, IntegerValue>>
158 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
160 std::sort(non_zeros_.begin(), non_zeros_.end());
161 for (
const glop::ColIndex
col : non_zeros_) {
162 const IntegerValue
coeff = dense_vector_[
col];
166 const int size = dense_vector_.
size();
167 for (glop::ColIndex
col(0);
col < size; ++
col) {
168 const IntegerValue
coeff = dense_vector_[
col];
178 : constraint_manager_(
model),
179 parameters_(*(
model->GetOrCreate<SatParameters>())),
186 implied_bounds_processor_({}, integer_trail_,
189 expanded_lp_solution_(
195 if (parameters_.use_branching_in_lp() ||
196 parameters_.search_branching() == SatParameters::LP_SEARCH) {
197 compute_reduced_cost_averages_ =
true;
201 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
206 DCHECK(!lp_constraint_is_registered_);
207 constraint_manager_.
Add(
ct);
214 for (
const IntegerVariable
var :
ct.vars) {
219glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
220 IntegerVariable positive_variable) {
222 const auto it = mirror_lp_variable_.find(positive_variable);
223 if (it == mirror_lp_variable_.end()) {
224 const glop::ColIndex
col(integer_variables_.size());
226 mirror_lp_variable_[positive_variable] =
col;
227 integer_variables_.push_back(positive_variable);
228 lp_solution_.push_back(std::numeric_limits<double>::infinity());
229 lp_reduced_cost_.push_back(0.0);
230 (*dispatcher_)[positive_variable] =
this;
234 if (
index >= expanded_lp_solution_.
size()) {
243 IntegerValue
coeff) {
244 CHECK(!lp_constraint_is_registered_);
245 objective_is_defined_ =
true;
250 const glop::ColIndex
col = GetOrCreateMirrorVariable(pos_var);
251 integer_objective_.push_back({
col,
coeff});
252 objective_infinity_norm_ =
269bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
272 infinity_norms_.
clear();
273 const auto& all_constraints = constraint_manager_.
AllConstraints();
277 integer_lp_.
push_back(LinearConstraintInternal());
278 LinearConstraintInternal& new_ct = integer_lp_.
back();
281 const int size =
ct.vars.size();
282 IntegerValue infinity_norm(0);
284 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
293 for (
int i = 0; i < size; ++i) {
295 IntegerVariable
var =
ct.vars[i];
296 IntegerValue
coeff =
ct.coeffs[i];
302 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var),
coeff});
304 infinity_norms_.
push_back(infinity_norm);
307 std::sort(new_ct.terms.begin(), new_ct.terms.end());
312 for (
int i = 0; i < integer_variables_.size(); ++i) {
319 objective_infinity_norm_ = 0;
320 for (
const auto& entry : integer_objective_) {
321 const IntegerVariable
var = integer_variables_[entry.first.value()];
323 integer_objective_offset_ +=
327 objective_infinity_norm_ =
329 integer_objective_[new_size++] = entry;
332 objective_infinity_norm_ =
334 integer_objective_.resize(new_size);
337 for (
const LinearConstraintInternal&
ct : integer_lp_) {
340 for (
const auto& term :
ct.terms) {
352 const int num_vars = integer_variables_.size();
353 for (
int i = 0; i < num_vars; i++) {
354 const IntegerVariable cp_var = integer_variables_[i];
362 glop::GlopParameters params;
363 params.set_cost_scaling(glop::GlopParameters::MEAN_COST_SCALING);
364 scaler_.
Scale(params, &lp_data_);
365 UpdateBoundsOfLpVariables();
370 if (parameters_.polish_lp_solution()) {
372 for (
int i = 0; i < num_vars; ++i) {
373 const IntegerVariable cp_var = integer_variables_[i];
376 if (lb != 0 || ub != 1)
continue;
386 <<
" Managed constraints.";
390LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
392 glop::BasisState basis_state = simplex_.
GetState();
394 const glop::Status
status = simplex_.
Solve(lp_data_, time_limit_);
398 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
407 info.new_obj_bound = IntegerValue(
408 static_cast<int64_t
>(std::ceil(info.lp_objective - kCpEpsilon)));
413void LinearProgrammingConstraint::FillReducedCostReasonIn(
415 std::vector<IntegerLiteral>* integer_reason) {
416 integer_reason->clear();
417 const int num_vars = integer_variables_.size();
418 for (
int i = 0; i < num_vars; i++) {
419 const double rc = reduced_costs[glop::ColIndex(i)];
420 if (rc > kLpEpsilon) {
421 integer_reason->push_back(
423 }
else if (rc < -kLpEpsilon) {
424 integer_reason->push_back(
432bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
434 DCHECK(lp_solution_is_set_);
436 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
439 integer_reason_.clear();
441 bool deductions_were_made =
false;
443 UpdateBoundsOfLpVariables();
445 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
449 const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
453 if (current_value < current_lb || current_value > current_ub) {
458 const double new_ub = std::floor(current_value);
461 LPSolveInfo lower_branch_info = SolveLpForBranching();
471 positive_var, IntegerValue(std::ceil(current_value)));
472 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
475 deductions_were_made =
true;
476 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
481 const double new_lb = std::ceil(current_value);
484 LPSolveInfo upper_branch_info = SolveLpForBranching();
488 return deductions_were_made;
495 positive_var, IntegerValue(std::floor(current_value)));
496 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
497 return deductions_were_made;
499 deductions_were_made =
true;
501 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
502 return deductions_were_made;
511 approximate_obj_lb = upper_branch_info.new_obj_bound;
513 approximate_obj_lb = lower_branch_info.new_obj_bound;
515 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
516 upper_branch_info.new_obj_bound);
521 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
524 const IntegerLiteral deduction =
526 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
527 return deductions_were_made;
534 DCHECK(!lp_constraint_is_registered_);
535 lp_constraint_is_registered_ =
true;
540 std::sort(integer_objective_.begin(), integer_objective_.end());
543 if (!parameters_.add_lp_constraints_lazily()) {
546 if (!CreateLpFromConstraintManager()) {
552 const int watcher_id = watcher->
Register(
this);
553 const int num_vars = integer_variables_.size();
554 for (
int i = 0; i < num_vars; i++) {
557 if (objective_is_defined_) {
570 optimal_constraints_.resize(rev_optimal_constraints_size_);
571 if (lp_solution_is_set_ && level < lp_solution_level_) {
572 lp_solution_is_set_ =
false;
580 if (level == 0 && !level_zero_lp_solution_.empty()) {
581 lp_solution_is_set_ =
true;
582 lp_solution_ = level_zero_lp_solution_;
583 lp_solution_level_ = 0;
584 for (
int i = 0; i < lp_solution_.size(); i++) {
585 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
586 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
593 for (
const IntegerVariable
var : generator.
vars) {
596 cut_generators_.push_back(std::move(generator));
600 const std::vector<int>& watch_indices) {
601 if (!lp_solution_is_set_)
return Propagate();
611 for (
const int index : watch_indices) {
617 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
632 glop::ColIndex
var) {
637 IntegerVariable variable)
const {
638 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
642 IntegerVariable variable)
const {
643 return lp_reduced_cost_[
gtl::FindOrDie(mirror_lp_variable_, variable)
647void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
648 const int num_vars = integer_variables_.size();
649 for (
int i = 0; i < num_vars; i++) {
650 const IntegerVariable cp_var = integer_variables_[i];
658bool LinearProgrammingConstraint::SolveLp() {
660 lp_at_level_zero_is_final_ =
false;
663 const auto status = simplex_.
Solve(lp_data_, time_limit_);
666 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
670 average_degeneracy_.
AddData(CalculateDegeneracy());
672 VLOG(2) <<
"High average degeneracy: "
677 if (status_as_int >= num_solves_by_status_.size()) {
678 num_solves_by_status_.resize(status_as_int + 1);
681 num_solves_by_status_[status_as_int]++;
688 lp_solution_is_set_ =
true;
690 const int num_vars = integer_variables_.size();
691 for (
int i = 0; i < num_vars; i++) {
693 GetVariableValueAtCpScale(glop::ColIndex(i));
694 lp_solution_[i] =
value;
695 expanded_lp_solution_[integer_variables_[i]] =
value;
699 if (lp_solution_level_ == 0) {
700 level_zero_lp_solution_ = lp_solution_;
706bool LinearProgrammingConstraint::AddCutFromConstraints(
707 const std::string&
name,
708 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
719 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
721 VLOG(1) <<
"Issue, overflow!";
740 if (std::abs(activity -
ToDouble(cut_.
ub)) / norm > 1e-4) {
741 VLOG(1) <<
"Cut not tight " << activity <<
" <= " <<
ToDouble(cut_.
ub);
751 const IntegerVariable first_new_var(expanded_lp_solution_.
size());
752 CHECK_EQ(first_new_var.value() % 2, 0);
754 LinearConstraint copy_in_debug;
756 copy_in_debug = cut_;
766 tmp_ib_slack_infos_.clear();
768 false, first_new_var,
769 expanded_lp_solution_, &cut_, &tmp_ib_slack_infos_);
771 cut_, tmp_ib_slack_infos_));
779 tmp_lp_values_.clear();
780 tmp_var_lbs_.clear();
781 tmp_var_ubs_.clear();
782 for (
const IntegerVariable
var : cut_.
vars) {
783 if (
var >= first_new_var) {
786 tmp_ib_slack_infos_[(
var.value() - first_new_var.value()) / 2];
787 tmp_lp_values_.push_back(info.lp_value);
788 tmp_var_lbs_.push_back(info.lb);
789 tmp_var_ubs_.push_back(info.ub);
791 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
799 const IntegerVariable first_slack(
800 first_new_var + IntegerVariable(2 * tmp_ib_slack_infos_.size()));
801 tmp_slack_rows_.clear();
802 tmp_slack_bounds_.clear();
803 for (
const auto& pair : integer_multipliers) {
804 const RowIndex
row = pair.first;
805 const IntegerValue
coeff = pair.second;
809 tmp_lp_values_.push_back(0.0);
810 cut_.
vars.push_back(first_slack +
811 2 * IntegerVariable(tmp_slack_rows_.size()));
812 tmp_slack_rows_.push_back(
row);
815 const IntegerValue diff(
816 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value()));
818 tmp_slack_bounds_.push_back(integer_lp_[
row].ub);
819 tmp_var_lbs_.push_back(IntegerValue(0));
820 tmp_var_ubs_.push_back(diff);
822 tmp_slack_bounds_.push_back(integer_lp_[
row].lb);
823 tmp_var_lbs_.push_back(-diff);
824 tmp_var_ubs_.push_back(IntegerValue(0));
828 bool at_least_one_added =
false;
834 at_least_one_added |= PostprocessAndAddCut(
835 absl::StrCat(
name,
"_K"), cover_cut_helper_.
Info(), first_new_var,
836 first_slack, tmp_ib_slack_infos_, cover_cut_helper_.
mutable_cut());
842 RoundingOptions options;
843 options.max_scaling = parameters_.max_integer_rounding_scaling();
844 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
845 tmp_var_lbs_, tmp_var_ubs_,
846 &implied_bounds_processor_, &cut_);
847 at_least_one_added |= PostprocessAndAddCut(
849 absl::StrCat(
"num_lifted_booleans=",
851 first_new_var, first_slack, tmp_ib_slack_infos_, &cut_);
853 return at_least_one_added;
856bool LinearProgrammingConstraint::PostprocessAndAddCut(
857 const std::string&
name,
const std::string& info,
858 IntegerVariable first_new_var, IntegerVariable first_slack,
859 const std::vector<ImpliedBoundsProcessor::SlackInfo>& ib_slack_infos,
860 LinearConstraint* cut) {
864 double activity = 0.0;
865 for (
int i = 0; i < cut->vars.size(); ++i) {
866 if (cut->vars[i] < first_new_var) {
868 ToDouble(cut->coeffs[i]) * expanded_lp_solution_[cut->vars[i]];
871 const double kMinViolation = 1e-4;
872 const double violation = activity -
ToDouble(cut->ub);
873 if (violation < kMinViolation) {
874 VLOG(3) <<
"Bad cut " << activity <<
" <= " <<
ToDouble(cut->ub);
882 IntegerValue cut_ub = cut->ub;
883 bool overflow =
false;
884 for (
int i = 0; i < cut->vars.size(); ++i) {
885 const IntegerVariable
var = cut->vars[i];
888 if (
var < first_new_var) {
889 const glop::ColIndex
col =
892 tmp_scattered_vector_.
Add(
col, cut->coeffs[i]);
894 tmp_scattered_vector_.
Add(
col, -cut->coeffs[i]);
900 if (
var < first_slack) {
901 const IntegerValue multiplier = cut->coeffs[i];
902 const int index = (
var.value() - first_new_var.value()) / 2;
906 for (
const std::pair<IntegerVariable, IntegerValue>& term :
907 ib_slack_infos[
index].terms) {
908 tmp_terms_.push_back(
927 const int slack_index = (
var.value() - first_slack.value()) / 2;
928 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
929 const IntegerValue multiplier = -cut->coeffs[i];
931 multiplier, integer_lp_[
row].terms)) {
937 if (!
AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
944 VLOG(1) <<
"Overflow in slack removal.";
948 VLOG(3) <<
" num_slack: " << num_slack;
954 const std::string extra_info =
955 absl::StrCat(info,
" num_ib_substitutions=", ib_slack_infos.size());
957 const double new_violation =
959 if (std::abs(violation - new_violation) >= 1e-4) {
960 VLOG(1) <<
"Violation discrepancy after slack removal. "
961 <<
" before = " << violation <<
" after = " << new_violation;
965 return constraint_manager_.
AddCut(*cut,
name, expanded_lp_solution_,
973void LinearProgrammingConstraint::AddCGCuts() {
975 for (RowIndex
row(0);
row < num_rows; ++
row) {
977 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
985 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
989 if (basis_col >= integer_variables_.size())
continue;
994 double magnitude = 0.0;
995 tmp_lp_multipliers_.clear();
997 if (lambda.non_zeros.empty()) {
998 for (RowIndex
row(0);
row < num_rows; ++
row) {
1000 if (std::abs(
value) < kZeroTolerance)
continue;
1006 VLOG(1) <<
"BASIC row not expected! " <<
value;
1011 tmp_lp_multipliers_.push_back({
row,
value});
1014 for (
const ColIndex
col : lambda.non_zeros) {
1016 const double value = lambda.values[
col];
1017 if (std::abs(
value) < kZeroTolerance)
continue;
1021 VLOG(1) <<
"BASIC row not expected! " <<
value;
1026 tmp_lp_multipliers_.push_back({
row,
value});
1029 if (tmp_lp_multipliers_.empty())
continue;
1032 for (
int i = 0; i < 2; ++i) {
1038 for (std::pair<RowIndex, double>& p : tmp_lp_multipliers_) {
1039 p.second = -p.second;
1045 tmp_integer_multipliers_ =
1046 ScaleLpMultiplier(
false,
1047 tmp_lp_multipliers_, &scaling, 52);
1048 AddCutFromConstraints(
"CG", tmp_integer_multipliers_);
1056void RandomPick(
const std::vector<RowIndex>&
a,
const std::vector<RowIndex>&
b,
1057 ModelRandomGenerator* random,
1058 std::vector<std::pair<RowIndex, RowIndex>>* output) {
1059 if (
a.empty() ||
b.empty())
return;
1060 for (
const RowIndex
row :
a) {
1061 const RowIndex other =
b[absl::Uniform<int>(*random, 0,
b.size())];
1063 output->push_back({
row, other});
1068template <
class ListOfTerms>
1069IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1070 for (
const auto& term : terms) {
1071 if (term.first ==
col)
return term.second;
1073 return IntegerValue(0);
1087void LinearProgrammingConstraint::AddObjectiveCut() {
1088 if (integer_objective_.size() <= 1)
return;
1094 const IntegerValue obj_lower_bound =
1096 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1098 tmp_lp_values_.clear();
1099 tmp_var_lbs_.clear();
1100 tmp_var_ubs_.clear();
1103 LinearConstraint objective_ct;
1105 objective_ct.ub = integer_objective_offset_ -
1107 IntegerValue obj_coeff_magnitude(0);
1108 for (
const auto& [
col,
coeff] : integer_objective_) {
1109 const IntegerVariable
var = integer_variables_[
col.value()];
1110 objective_ct.vars.push_back(
var);
1111 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1114 objective_ct.coeffs.push_back(-
coeff);
1121 if (obj_coeff_magnitude < 1e9) {
1122 const bool added = constraint_manager_.
AddCut(objective_ct,
"Objective",
1123 expanded_lp_solution_);
1129 cut_ = objective_ct;
1132 constraint_manager_.
AddCut(cut_,
"Objective_K", expanded_lp_solution_);
1138 cut_ = objective_ct;
1139 RoundingOptions options;
1140 options.max_scaling = parameters_.max_integer_rounding_scaling();
1141 integer_rounding_cut_helper_.
ComputeCut(options, tmp_lp_values_,
1142 tmp_var_lbs_, tmp_var_ubs_,
1143 &implied_bounds_processor_, &cut_);
1146 constraint_manager_.
AddCut(cut_,
"Objective_MIR", expanded_lp_solution_);
1150void LinearProgrammingConstraint::AddMirCuts() {
1166 integer_variables_.size(), IntegerValue(0));
1167 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1172 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1174 for (RowIndex
row(0);
row < num_rows; ++
row) {
1181 base_rows.push_back({
row, IntegerValue(1)});
1185 base_rows.push_back({
row, IntegerValue(-1)});
1206 std::vector<double> weights;
1208 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1209 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1219 integer_multipliers = {entry};
1220 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1225 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1226 dense_cut[
col] = IntegerValue(0);
1228 non_zeros_.SparseClearAll();
1231 const IntegerValue multiplier = entry.second;
1232 for (
const std::pair<ColIndex, IntegerValue>& term :
1233 integer_lp_[entry.first].terms) {
1234 const ColIndex
col = term.first;
1235 const IntegerValue
coeff = term.second;
1236 non_zeros_.Set(
col);
1237 dense_cut[
col] +=
coeff * multiplier;
1240 used_rows.
assign(num_rows.value(),
false);
1241 used_rows[entry.first] =
true;
1246 const int kMaxAggregation = 5;
1247 for (
int i = 0; i < kMaxAggregation; ++i) {
1250 IntegerValue max_magnitude(0);
1252 std::vector<ColIndex> col_candidates;
1253 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1254 if (dense_cut[
col] == 0)
continue;
1257 const int col_degree =
1259 if (col_degree <= 1)
continue;
1264 const IntegerVariable
var = integer_variables_[
col.value()];
1265 const double lp_value = expanded_lp_solution_[
var];
1268 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1269 if (bound_distance > 1e-2) {
1270 weights.push_back(bound_distance);
1271 col_candidates.push_back(
col);
1274 if (col_candidates.empty())
break;
1276 const ColIndex var_to_eliminate =
1277 col_candidates[std::discrete_distribution<>(weights.begin(),
1278 weights.end())(*random_)];
1281 std::vector<RowIndex> possible_rows;
1284 const RowIndex
row = entry.row();
1292 if (used_rows[
row])
continue;
1293 used_rows[
row] =
true;
1301 bool add_row =
false;
1304 if (entry.coefficient() > 0.0) {
1305 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1307 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1312 if (entry.coefficient() > 0.0) {
1313 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1315 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1320 weights.push_back(row_weights[
row]);
1323 if (possible_rows.empty())
break;
1325 const RowIndex row_to_combine =
1326 possible_rows[std::discrete_distribution<>(weights.begin(),
1327 weights.end())(*random_)];
1328 const IntegerValue to_combine_coeff =
1329 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1332 IntegerValue mult1 = -to_combine_coeff;
1333 IntegerValue mult2 = dense_cut[var_to_eliminate];
1340 const IntegerValue gcd = IntegerValue(
1350 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1353 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1355 std::abs(mult2.value()))) ==
1360 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1361 entry.second *= mult1;
1363 integer_multipliers.push_back({row_to_combine, mult2});
1366 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1367 integer_multipliers)) {
1373 if (i + 1 == kMaxAggregation)
break;
1375 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1376 dense_cut[
col] *= mult1;
1378 for (
const std::pair<ColIndex, IntegerValue>& term :
1379 integer_lp_[row_to_combine].terms) {
1380 const ColIndex
col = term.first;
1381 const IntegerValue
coeff = term.second;
1382 non_zeros_.Set(
col);
1389void LinearProgrammingConstraint::AddZeroHalfCuts() {
1392 tmp_lp_values_.clear();
1393 tmp_var_lbs_.clear();
1394 tmp_var_ubs_.clear();
1395 for (
const IntegerVariable
var : integer_variables_) {
1396 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1404 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1412 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1414 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1422 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1426void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1427 const int64_t min_iter,
const int64_t max_iter) {
1428 if (parameters_.linearization_level() < 2)
return;
1429 const int64_t num_degenerate_columns = CalculateDegeneracy();
1431 if (num_cols <= 0) {
1435 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1440 if (is_degenerate_) {
1441 next_simplex_iter_ /=
std::max(int64_t{1}, decrease_factor);
1443 next_simplex_iter_ *= 2;
1446 if (is_degenerate_) {
1447 next_simplex_iter_ /=
std::max(int64_t{1}, 2 * decrease_factor);
1451 next_simplex_iter_ = num_cols / 40;
1454 next_simplex_iter_ =
1459 UpdateBoundsOfLpVariables();
1464 if ( (
false) && objective_is_defined_) {
1473 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1474 100.0 * kCpEpsilon));
1483 parameters.set_max_number_of_iterations(2000);
1485 parameters.set_max_number_of_iterations(next_simplex_iter_);
1487 if (parameters_.use_exact_lp_reason()) {
1488 parameters.set_change_status_to_imprecise(
false);
1489 parameters.set_primal_feasibility_tolerance(1e-7);
1490 parameters.set_dual_feasibility_tolerance(1e-7);
1495 if (!SolveLp())
return true;
1498 const int max_cuts_rounds =
1499 parameters_.cut_level() <= 0
1502 ? parameters_.max_cut_rounds_at_level_zero()
1506 cuts_round < max_cuts_rounds) {
1511 if (num_solves_ > 1) {
1514 expanded_lp_solution_);
1521 if (parameters_.add_objective_cut()) AddObjectiveCut();
1522 if (parameters_.add_mir_cuts()) AddMirCuts();
1523 if (parameters_.add_cg_cuts()) AddCGCuts();
1524 if (parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1528 if (!cut_generators_.empty() &&
1530 !parameters_.only_add_cuts_at_level_zero())) {
1531 for (
const CutGenerator& generator : cut_generators_) {
1532 if (!generator.generate_cuts(expanded_lp_solution_,
1533 &constraint_manager_)) {
1540 expanded_lp_solution_, &constraint_manager_);
1544 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state)) {
1546 if (!CreateLpFromConstraintManager()) {
1550 if (!SolveLp())
return true;
1552 VLOG(1) <<
"Relaxation improvement " << old_obj <<
" -> "
1559 lp_at_level_zero_is_final_ =
true;
1567 if (parameters_.use_exact_lp_reason()) {
1568 if (!FillExactDualRayReason())
return true;
1577 UpdateSimplexIterationLimit(10, 1000);
1580 if (objective_is_defined_ &&
1585 if (parameters_.use_exact_lp_reason()) {
1586 if (!ExactLpReasonning())
return false;
1591 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1592 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1593 const IntegerValue propagated_lb =
1595 if (approximate_new_lb > propagated_lb) {
1596 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1598 <<
" ] approx_lb += "
1599 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1600 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
1607 FillReducedCostReasonIn(simplex_.
GetReducedCosts(), &integer_reason_);
1608 const double objective_cp_ub =
1611 ReducedCostStrengtheningDeductions(objective_cp_ub -
1612 relaxed_optimal_objective);
1613 if (!deductions_.empty()) {
1614 deductions_reason_ = integer_reason_;
1615 deductions_reason_.push_back(
1620 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1621 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1622 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
1625 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
1631 if (!deductions_.empty()) {
1632 const int trail_index_with_same_reason = integer_trail_->
Index();
1634 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
1635 trail_index_with_same_reason)) {
1645 CHECK(lp_solution_is_set_);
1648 lp_solution_is_integer_ =
true;
1649 const int num_vars = integer_variables_.size();
1650 for (
int i = 0; i < num_vars; i++) {
1653 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1655 lp_solution_is_integer_ =
false;
1659 if (compute_reduced_cost_averages_) {
1660 UpdateAverageReducedCosts();
1664 if (parameters_.use_branching_in_lp() && objective_is_defined_ &&
1666 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1667 parameters_.linearization_level() >= 2 &&
1668 compute_reduced_cost_averages_ &&
1670 count_since_last_branching_++;
1671 if (count_since_last_branching_ < branching_frequency_) {
1674 count_since_last_branching_ = 0;
1675 bool branching_successful =
false;
1678 const int max_num_branches = 3;
1679 const int num_vars = integer_variables_.size();
1680 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1681 for (
int i = 0; i < num_vars; ++i) {
1682 const IntegerVariable
var = integer_variables_[i];
1687 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1701 const double cost_i = rc_scores_[i];
1702 std::pair<double, IntegerVariable> branching_var =
1703 std::make_pair(-cost_i, positive_var);
1705 branching_vars.end(), branching_var);
1707 branching_vars.insert(iterator, branching_var);
1708 if (branching_vars.size() > max_num_branches) {
1709 branching_vars.resize(max_num_branches);
1713 for (
const std::pair<double, IntegerVariable>& branching_var :
1715 const IntegerVariable positive_var = branching_var.second;
1716 VLOG(2) <<
"Branching on: " << positive_var;
1717 if (BranchOnVar(positive_var)) {
1718 VLOG(2) <<
"Branching successful.";
1719 branching_successful =
true;
1724 if (!branching_successful) {
1725 branching_frequency_ *= 2;
1734IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1737 const int size = terms.
vars.size();
1738 for (
int i = 0; i < size; ++i) {
1739 const IntegerVariable
var = terms.
vars[i];
1749bool LinearProgrammingConstraint::PossibleOverflow(
1750 const LinearConstraint& constraint) {
1752 const int size = constraint.vars.size();
1753 for (
int i = 0; i < size; ++i) {
1754 const IntegerVariable
var = constraint.vars[i];
1755 const IntegerValue
coeff = constraint.coeffs[i];
1774absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1775 absl::int128 div128(positive_div.value());
1776 absl::int128 result = x / div128;
1777 if (result * div128 > x)
return result - 1;
1783void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1792 const int size = constraint->vars.size();
1793 for (
int i = 0; i < size; ++i) {
1794 const IntegerVariable
var = constraint->vars[i];
1803 const double max_value =
std::max({sum_max, -sum_min, sum_max - sum_min});
1805 const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1806 if (divisor <= 1)
return;
1821 absl::int128 adjust = 0;
1822 for (
int i = 0; i < size; ++i) {
1823 const IntegerValue old_coeff = constraint->coeffs[i];
1824 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1827 const absl::int128 remainder =
1828 absl::int128(old_coeff.value()) -
1829 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1835 if (new_coeff == 0)
continue;
1836 constraint->vars[new_size] = constraint->vars[i];
1837 constraint->coeffs[new_size] = new_coeff;
1840 constraint->vars.resize(new_size);
1841 constraint->coeffs.resize(new_size);
1843 constraint->ub = IntegerValue(
static_cast<int64_t
>(
1844 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1849void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1850 const LinearConstraint& terms, IntegerValue slack) {
1851 integer_reason_.clear();
1852 std::vector<IntegerValue> magnitudes;
1853 const int size = terms.vars.size();
1854 for (
int i = 0; i < size; ++i) {
1855 const IntegerVariable
var = terms.vars[i];
1856 const IntegerValue
coeff = terms.coeffs[i];
1859 magnitudes.push_back(
coeff);
1862 magnitudes.push_back(-
coeff);
1873std::vector<std::pair<RowIndex, IntegerValue>>
1874LinearProgrammingConstraint::ScaleLpMultiplier(
1875 bool take_objective_into_account,
1876 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1878 double max_sum = 0.0;
1879 tmp_cp_multipliers_.clear();
1880 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1881 const RowIndex
row = p.first;
1886 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1902 tmp_cp_multipliers_.push_back({
row, cp_multi});
1903 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
1908 if (take_objective_into_account) {
1909 max_sum +=
ToDouble(objective_infinity_norm_);
1913 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1914 if (max_sum == 0.0) {
1916 return integer_multipliers;
1921 const double threshold = std::ldexp(1, max_pow) / max_sum;
1922 if (threshold < 1.0) {
1925 return integer_multipliers;
1927 while (2 * *scaling <= threshold) *scaling *= 2;
1932 for (
const auto& entry : tmp_cp_multipliers_) {
1933 const IntegerValue
coeff(std::round(entry.second * (*scaling)));
1934 if (
coeff != 0) integer_multipliers.push_back({entry.first,
coeff});
1936 return integer_multipliers;
1939bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1940 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1941 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1944 scattered_vector->ClearAndResize(integer_variables_.size());
1948 for (
const std::pair<RowIndex, IntegerValue>& term : integer_multipliers) {
1949 const RowIndex
row = term.first;
1950 const IntegerValue multiplier = term.second;
1954 if (!scattered_vector->AddLinearExpressionMultiple(
1955 multiplier, integer_lp_[
row].terms)) {
1960 const IntegerValue
bound =
1961 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1969void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1970 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1971 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
1972 const IntegerValue kMaxWantedCoeff(1e18);
1973 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1974 const RowIndex
row = term.first;
1975 const IntegerValue multiplier = term.second;
1976 if (multiplier == 0)
continue;
1980 IntegerValue negative_limit = kMaxWantedCoeff;
1981 IntegerValue positive_limit = kMaxWantedCoeff;
1985 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
1986 if (multiplier > 0) {
1987 negative_limit =
std::min(negative_limit, multiplier);
1989 positive_limit =
std::min(positive_limit, -multiplier);
1994 const IntegerValue row_bound =
1995 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
1996 if (row_bound != 0) {
2000 const IntegerValue limit2 =
2003 positive_limit =
std::min(positive_limit, limit1);
2004 negative_limit =
std::min(negative_limit, limit2);
2006 negative_limit =
std::min(negative_limit, limit1);
2007 positive_limit =
std::min(positive_limit, limit2);
2019 double positive_diff =
ToDouble(row_bound);
2020 double negative_diff =
ToDouble(row_bound);
2025 for (
const auto& entry : integer_lp_[
row].terms) {
2026 const ColIndex
col = entry.first;
2027 const IntegerValue
coeff = entry.second;
2031 const IntegerVariable
var = integer_variables_[
col.value()];
2038 const IntegerValue current = (*scattered_vector)[
col];
2040 const IntegerValue overflow_limit(
2042 positive_limit =
std::min(positive_limit, overflow_limit);
2043 negative_limit =
std::min(negative_limit, overflow_limit);
2060 const IntegerValue current_magnitude =
IntTypeAbs(current);
2061 const IntegerValue other_direction_limit =
FloorRatio(
2063 ? kMaxWantedCoeff +
std::min(current_magnitude,
2065 : current_magnitude,
2067 const IntegerValue same_direction_limit(
FloorRatio(
2068 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
2070 if ((current > 0) == (
coeff > 0)) {
2071 negative_limit =
std::min(negative_limit, other_direction_limit);
2072 positive_limit =
std::min(positive_limit, same_direction_limit);
2074 negative_limit =
std::min(negative_limit, same_direction_limit);
2075 positive_limit =
std::min(positive_limit, other_direction_limit);
2079 const IntegerValue implied = current > 0 ? lb : ub;
2090 IntegerValue to_add(0);
2091 if (positive_diff <= -1.0 && positive_limit > 0) {
2092 to_add = positive_limit;
2094 if (negative_diff >= 1.0 && negative_limit > 0) {
2097 std::abs(
ToDouble(negative_limit) * negative_diff) >
2098 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2099 to_add = -negative_limit;
2103 term.second += to_add;
2108 CHECK(scattered_vector->AddLinearExpressionMultiple(
2109 to_add, integer_lp_[
row].terms));
2128bool LinearProgrammingConstraint::ExactLpReasonning() {
2130 integer_reason_.clear();
2131 deductions_.clear();
2132 deductions_reason_.clear();
2138 tmp_lp_multipliers_.clear();
2139 for (RowIndex
row(0);
row < num_rows; ++
row) {
2141 if (std::abs(
value) < kZeroTolerance)
continue;
2142 tmp_lp_multipliers_.push_back({
row,
value});
2146 tmp_integer_multipliers_ = ScaleLpMultiplier(
2147 true, tmp_lp_multipliers_, &scaling);
2150 if (!ComputeNewLinearConstraint(tmp_integer_multipliers_,
2151 &tmp_scattered_vector_, &rc_ub)) {
2152 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2158 const IntegerValue obj_scale(std::round(scaling));
2159 if (obj_scale == 0) {
2160 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2164 integer_objective_));
2166 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2173 tmp_constraint_.
vars.push_back(objective_cp_);
2174 tmp_constraint_.
coeffs.push_back(-obj_scale);
2176 PreventOverflow(&tmp_constraint_);
2177 DCHECK(!PossibleOverflow(tmp_constraint_));
2181 if (tmp_constraint_.
vars.empty()) {
2183 return tmp_constraint_.
ub >= 0;
2186 IntegerSumLE* cp_constraint =
2187 new IntegerSumLE({}, tmp_constraint_.
vars, tmp_constraint_.
coeffs,
2188 tmp_constraint_.
ub, model_);
2192 optimal_constraints_.clear();
2194 optimal_constraints_.emplace_back(cp_constraint);
2195 rev_optimal_constraints_size_ = optimal_constraints_.size();
2196 if (!cp_constraint->PropagateAtLevelZero())
return false;
2197 return cp_constraint->Propagate();
2200bool LinearProgrammingConstraint::FillExactDualRayReason() {
2203 tmp_lp_multipliers_.clear();
2204 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2206 if (std::abs(
value) < kZeroTolerance)
continue;
2207 tmp_lp_multipliers_.push_back({
row,
value});
2209 tmp_integer_multipliers_ = ScaleLpMultiplier(
2210 false, tmp_lp_multipliers_, &scaling);
2212 IntegerValue new_constraint_ub;
2213 if (!ComputeNewLinearConstraint(tmp_integer_multipliers_,
2214 &tmp_scattered_vector_, &new_constraint_ub)) {
2215 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2219 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2220 &new_constraint_ub);
2223 integer_variables_, new_constraint_ub, &tmp_constraint_);
2225 PreventOverflow(&tmp_constraint_);
2226 DCHECK(!PossibleOverflow(tmp_constraint_));
2229 const IntegerValue implied_lb = GetImpliedLowerBound(tmp_constraint_);
2230 if (implied_lb <= tmp_constraint_.
ub) {
2231 VLOG(1) <<
"LP exact dual ray not infeasible,"
2232 <<
" implied_lb: " << implied_lb.value() / scaling
2233 <<
" ub: " << tmp_constraint_.
ub.value() / scaling;
2236 const IntegerValue slack = (implied_lb - tmp_constraint_.
ub) - 1;
2237 SetImpliedLowerBoundReason(tmp_constraint_, slack);
2241int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2243 int num_non_basic_with_zero_rc = 0;
2244 for (glop::ColIndex i(0); i < num_vars; ++i) {
2246 if (rc != 0.0)
continue;
2250 num_non_basic_with_zero_rc++;
2253 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2254 return num_non_basic_with_zero_rc;
2257void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2258 double cp_objective_delta) {
2259 deductions_.clear();
2264 const double lp_objective_delta =
2266 const int num_vars = integer_variables_.size();
2267 for (
int i = 0; i < num_vars; i++) {
2268 const IntegerVariable cp_var = integer_variables_[i];
2269 const glop::ColIndex lp_var = glop::ColIndex(i);
2273 if (rc == 0.0)
continue;
2274 const double lp_other_bound =
value + lp_objective_delta / rc;
2275 const double cp_other_bound =
2278 if (rc > kLpEpsilon) {
2280 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2285 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2288 }
else if (rc < -kLpEpsilon) {
2290 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2292 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2293 deductions_.push_back(
2308 int num_nodes,
int subset_size,
const std::vector<bool>& in_subset,
2309 const std::vector<int>& tails,
const std::vector<int>& heads,
2310 const std::vector<Literal>& literals,
2311 const std::vector<double>& literal_lp_values, int64_t rhs_lower_bound,
2313 LinearConstraintManager* manager, Model*
model) {
2320 int num_optional_nodes_in = 0;
2321 int num_optional_nodes_out = 0;
2322 int optional_loop_in = -1;
2323 int optional_loop_out = -1;
2324 for (
int i = 0; i < tails.size(); ++i) {
2325 if (tails[i] != heads[i])
continue;
2326 if (in_subset[tails[i]]) {
2327 num_optional_nodes_in++;
2328 if (optional_loop_in == -1 ||
2329 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2330 optional_loop_in = i;
2333 num_optional_nodes_out++;
2334 if (optional_loop_out == -1 ||
2335 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2336 optional_loop_out = i;
2343 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2345 rhs_lower_bound = 1;
2348 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
2350 double sum_outgoing = 0.0;
2353 for (
int i = 0; i < tails.size(); ++i) {
2354 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2355 sum_outgoing += literal_lp_values[i];
2356 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2361 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2363 if (num_optional_nodes_in == subset_size &&
2364 (optional_loop_in == -1 ||
2365 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2368 if (num_optional_nodes_out == num_nodes - subset_size &&
2369 (optional_loop_out == -1 ||
2370 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2375 if (num_optional_nodes_in == subset_size) {
2377 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2378 sum_outgoing += literal_lp_values[optional_loop_in];
2382 if (num_optional_nodes_out == num_nodes - subset_size) {
2383 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2385 sum_outgoing += literal_lp_values[optional_loop_out];
2389 if (sum_outgoing < rhs_lower_bound - 1e-6) {
2390 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
2403 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2404 const std::vector<Literal>& literals,
2406 absl::Span<const int64_t> demands, int64_t
capacity,
2408 if (num_nodes <= 2)
return;
2417 std::vector<Arc> relevant_arcs;
2420 std::vector<double> literal_lp_values(literals.size());
2421 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2423 for (
int i = 0; i < literals.size(); ++i) {
2425 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
2427 lp_value = lp_values[direct_view];
2430 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2432 literal_lp_values[i] = lp_value;
2434 if (lp_value < 1e-6)
continue;
2435 relevant_arcs.
push_back({tails[i], heads[i], lp_value});
2436 arc_by_decreasing_lp_values.push_back({lp_value, i});
2438 std::sort(arc_by_decreasing_lp_values.begin(),
2439 arc_by_decreasing_lp_values.end(),
2440 std::greater<std::pair<double, int>>());
2450 int num_components = num_nodes;
2451 std::vector<int> parent(num_nodes);
2452 std::vector<int> root(num_nodes);
2453 for (
int i = 0; i < num_nodes; ++i) {
2457 auto get_root_and_compress_path = [&root](
int node) {
2459 while (root[r] != r) r = root[r];
2460 while (root[node] != r) {
2461 const int next = root[node];
2467 for (
const auto& pair : arc_by_decreasing_lp_values) {
2468 if (num_components == 2)
break;
2469 const int tail = get_root_and_compress_path(tails[pair.second]);
2470 const int head = get_root_and_compress_path(heads[pair.second]);
2474 const int new_node = parent.size();
2475 parent.push_back(new_node);
2476 parent[
head] = new_node;
2477 parent[
tail] = new_node;
2481 root.push_back(new_node);
2482 root[
head] = new_node;
2483 root[
tail] = new_node;
2494 std::vector<int> pre_order(num_nodes);
2495 std::vector<absl::Span<const int>> subsets;
2497 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2498 for (
int i = 0; i < parent.size(); ++i) {
2499 if (parent[i] != i) graph[parent[i]].push_back(i);
2501 std::vector<int> queue;
2502 std::vector<bool> seen(graph.size(),
false);
2503 std::vector<int> start_index(parent.size());
2504 for (
int i = num_nodes; i < parent.size(); ++i) {
2508 CHECK(graph[i].empty() || graph[i].size() == 2);
2509 if (parent[i] != i)
continue;
2514 while (!queue.empty()) {
2515 const int node = queue.back();
2520 const int start = start_index[node];
2521 if (new_size -
start > 1) {
2522 subsets.emplace_back(&pre_order[
start], new_size -
start);
2527 start_index[node] = new_size;
2528 if (node < num_nodes) pre_order[new_size++] = node;
2529 for (
const int child : graph[node]) {
2530 if (!seen[child]) queue.push_back(child);
2538 int64_t total_demands = 0;
2539 if (!demands.empty()) {
2540 for (
const int64_t
demand : demands) total_demands +=
demand;
2544 CHECK_EQ(pre_order.size(), num_nodes);
2545 std::vector<bool> in_subset(num_nodes,
false);
2546 for (
const absl::Span<const int> subset : subsets) {
2548 CHECK_LT(subset.size(), num_nodes);
2551 bool contain_depot =
false;
2552 int64_t subset_demand = 0;
2555 for (
const int n : subset) {
2556 in_subset[n] =
true;
2557 if (!demands.empty()) {
2558 if (n == 0) contain_depot =
true;
2559 subset_demand += demands[n];
2576 int64_t min_outgoing_flow = 1;
2577 if (!demands.empty()) {
2587 min_outgoing_flow =
std::max(min_outgoing_flow, int64_t{1});
2599 double outgoing_flow = 0.0;
2600 for (
const auto arc : relevant_arcs) {
2601 if (in_subset[
arc.tail] && !in_subset[
arc.head]) {
2602 outgoing_flow +=
arc.lp_value;
2607 if (outgoing_flow < min_outgoing_flow - 1e-6) {
2608 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2609 literals, literal_lp_values,
2610 min_outgoing_flow, lp_values, manager,
2615 for (
const int n : subset) in_subset[n] =
false;
2622std::vector<IntegerVariable> GetAssociatedVariables(
2623 const std::vector<Literal>& literals, Model*
model) {
2624 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
2625 std::vector<IntegerVariable> result;
2626 for (
const Literal l : literals) {
2627 const IntegerVariable direct_view = encoder->GetLiteralView(l);
2629 result.push_back(direct_view);
2631 result.push_back(encoder->GetLiteralView(l.Negated()));
2644 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
2645 const std::vector<Literal>& literals,
Model*
model) {
2647 result.
vars = GetAssociatedVariables(literals,
model);
2649 [num_nodes, tails, heads, literals,
model](
2653 num_nodes, tails, heads, literals, lp_values,
2654 {}, 0, manager,
model);
2661 const std::vector<int>& tails,
2662 const std::vector<int>& heads,
2663 const std::vector<Literal>& literals,
2664 const std::vector<int64_t>& demands,
2667 result.
vars = GetAssociatedVariables(literals,
model);
2669 [num_nodes, tails, heads, demands,
capacity, literals,
model](
2673 lp_values, demands,
capacity, manager,
2680std::function<IntegerLiteral()>
2683 std::vector<IntegerVariable> variables;
2684 for (IntegerVariable
var : integer_variables_) {
2687 variables.push_back(
var);
2690 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2693 return [
this, variables]() {
2697 double fractional_distance_best = -1.0;
2698 for (
const IntegerVariable
var : variables) {
2703 if (lb == ub)
continue;
2707 const double fractional_distance =
2709 lp_value - std::floor(lp_value +
kEpsilon));
2710 if (fractional_distance <
kEpsilon)
continue;
2713 if (fractional_distance > fractional_distance_best) {
2714 fractional_var =
var;
2715 fractional_distance_best = fractional_distance;
2729 std::vector<IntegerVariable> variables;
2730 for (IntegerVariable
var : integer_variables_) {
2733 variables.push_back(
var);
2736 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2742 const int num_vars = variables.size();
2743 std::vector<double> cost_to_zero(num_vars, 0.0);
2744 std::vector<int> num_cost_to_zero(num_vars);
2747 return [=]()
mutable {
2752 if (num_calls == 10000) {
2753 for (
int i = 0; i < num_vars; i++) {
2754 cost_to_zero[i] /= 2;
2755 num_cost_to_zero[i] /= 2;
2761 for (
int i = 0; i < num_vars; i++) {
2762 const IntegerVariable
var = variables[i];
2767 if (lb == ub)
continue;
2771 if (std::abs(rc) <
kEpsilon)
continue;
2774 if (
value == 1.0 && rc < 0.0) {
2775 cost_to_zero[i] -= rc;
2776 num_cost_to_zero[i]++;
2781 int selected_index = -1;
2782 double best_cost = 0.0;
2783 for (
int i = 0; i < num_vars; i++) {
2784 const IntegerVariable
var = variables[i];
2789 if (num_cost_to_zero[i] > 0 &&
2790 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2791 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2796 if (selected_index >= 0) {
2804void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2805 const int num_vars = integer_variables_.size();
2806 if (sum_cost_down_.size() < num_vars) {
2807 sum_cost_down_.resize(num_vars, 0.0);
2808 num_cost_down_.resize(num_vars, 0);
2809 sum_cost_up_.resize(num_vars, 0.0);
2810 num_cost_up_.resize(num_vars, 0);
2811 rc_scores_.resize(num_vars, 0.0);
2815 num_calls_since_reduced_cost_averages_reset_++;
2816 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2817 for (
int i = 0; i < num_vars; i++) {
2818 sum_cost_up_[i] /= 2;
2819 num_cost_up_[i] /= 2;
2820 sum_cost_down_[i] /= 2;
2821 num_cost_down_[i] /= 2;
2823 num_calls_since_reduced_cost_averages_reset_ = 0;
2827 for (
int i = 0; i < num_vars; i++) {
2828 const IntegerVariable
var = integer_variables_[i];
2835 const double rc = lp_reduced_cost_[i];
2836 if (std::abs(rc) < kCpEpsilon)
continue;
2839 sum_cost_down_[i] -= rc;
2840 num_cost_down_[i]++;
2842 sum_cost_up_[i] += rc;
2849 rc_rev_int_repository_.
SetLevel(0);
2855 positions_by_decreasing_rc_score_.clear();
2856 for (
int i = 0; i < num_vars; i++) {
2861 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2862 const double a_down =
2863 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2864 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2865 rc_scores_[i] =
std::min(a_up, a_down);
2867 rc_scores_[i] = 0.5 * (a_down + a_up);
2872 if (rc_scores_[i] > 0.0) {
2873 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2876 std::sort(positions_by_decreasing_rc_score_.begin(),
2877 positions_by_decreasing_rc_score_.end());
2881std::function<IntegerLiteral()>
2883 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2886IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2888 int selected_index = -1;
2889 const int size = positions_by_decreasing_rc_score_.size();
2890 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2891 for (
int i = rev_rc_start_; i < size; ++i) {
2892 const int index = positions_by_decreasing_rc_score_[i].second;
2893 const IntegerVariable
var = integer_variables_[
index];
2896 selected_index =
index;
2901 if (selected_index == -1)
return IntegerLiteral();
2902 const IntegerVariable
var = integer_variables_[selected_index];
2909 const IntegerValue value_ceil(
2911 if (value_ceil >= ub) {
2918 const IntegerValue value_floor(
2920 if (value_floor <= lb) {
2927 num_cost_up_[selected_index] > 0
2928 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2930 const double a_down =
2931 num_cost_down_[selected_index] > 0
2932 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2934 if (a_down < a_up) {
2942 std::string result =
"LP statistics:\n";
2943 absl::StrAppend(&result,
" final dimension: ",
DimensionString(),
"\n");
2944 absl::StrAppend(&result,
" total number of simplex iterations: ",
2945 total_num_simplex_iterations_,
"\n");
2946 absl::StrAppend(&result,
" num solves: \n");
2947 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
2948 if (num_solves_by_status_[i] == 0)
continue;
2949 absl::StrAppend(&result,
" - #",
2951 num_solves_by_status_[i],
"\n");
2953 absl::StrAppend(&result, constraint_manager_.
Statistics());
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
void assign(size_type n, const value_type &val)
void resize(size_type new_size)
void push_back(const value_type &x)
static int64_t GCD64(int64_t x, int64_t y)
void SetLevel(int level) final
void SaveState(T *object)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
void SetObjectiveOffset(Fractional objective_offset)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
ColIndex CreateNewVariable()
void NotifyThatColumnsAreClean()
void SetObjectiveCoefficient(ColIndex col, Fractional value)
RowIndex CreateNewConstraint()
std::string GetDimensionString() const
Fractional objective_scaling_factor() const
const SparseColumn & GetSparseColumn(ColIndex col) const
RowIndex num_constraints() const
void Scale(LinearProgram *lp)
Fractional VariableScalingFactor(ColIndex col) const
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Fractional UnscaleReducedCost(ColIndex col, Fractional value) const
Fractional UnscaleDualValue(RowIndex row, Fractional value) const
const GlopParameters & GetParameters() const
const DenseRow & GetDualRayRowCombination() const
Fractional GetVariableValue(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
VariableStatus GetVariableStatus(ColIndex col) const
Fractional GetReducedCost(ColIndex col) const
const DenseColumn & GetDualRay() const
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
Fractional GetObjectiveValue() const
Fractional GetDualValue(RowIndex row) const
void ClearIntegralityScales()
void NotifyThatMatrixIsUnchangedForNextSolve()
ConstraintStatus GetConstraintStatus(RowIndex row) const
ColIndex GetProblemNumCols() const
void LoadStateForNextSolve(const BasisState &state)
RowIndex GetProblemNumRows() const
void ClearStateForNextSolve()
int64_t GetNumberOfIterations() const
const BasisState & GetState() const
ColIndex GetBasis(RowIndex row) const
void SetParameters(const GlopParameters ¶meters)
EntryIndex num_entries() const
LinearConstraint * mutable_cut()
bool TrySimpleKnapsack(const LinearConstraint base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
void AlwaysCallAtLevelZero(int id)
void RegisterReversibleInt(int id, int *rev)
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
void AddLpVariable(IntegerVariable var)
void ProcessUpperBoundedConstraintWithSlackCreation(bool substitute_only_inner_variables, IntegerVariable first_slack, const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraint *cut, std::vector< SlackInfo > *slack_infos)
bool DebugSlack(IntegerVariable first_slack, const LinearConstraint &initial_cut, const LinearConstraint &cut, const std::vector< SlackInfo > &info)
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
void AddData(double new_record)
double CurrentAverage() const
const IntegerVariable GetLiteralView(Literal lit) const
int NumLiftedBooleans() const
void ComputeCut(RoundingOptions options, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds, ImpliedBoundsProcessor *ib_processor, LinearConstraint *cut)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
bool IsCurrentlyIgnored(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
bool IsFixedAtLevelZero(IntegerVariable var) const
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
void RegisterReversibleClass(ReversibleInterface *rev)
bool ChangeLp(const absl::StrongVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
bool DebugCheckConstraint(const LinearConstraint &cut)
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
const std::vector< ConstraintIndex > & LpConstraints() const
std::string Statistics() const
void AddAllConstraintsToLp()
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
const absl::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
bool Propagate() override
std::string DimensionString() const
double GetSolutionValue(IntegerVariable variable) const
void RegisterWith(Model *model)
glop::RowIndex ConstraintIndex
std::function< IntegerLiteral()> HeuristicLpReducedCostAverageBranching()
LinearProgrammingConstraint(Model *model)
std::string Statistics() const
std::function< IntegerLiteral()> HeuristicLpReducedCostBinary(Model *model)
void AddLinearConstraint(const LinearConstraint &ct)
bool IncrementalPropagate(const std::vector< int > &watch_indices) override
void SetLevel(int level) override
std::function< IntegerLiteral()> HeuristicLpMostInfeasibleBinary(Model *model)
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
double GetSolutionReducedCost(IntegerVariable variable) const
void AddCutGenerator(CutGenerator generator)
Class that owns everything related to a particular optimization model.
void ConvertToLinearConstraint(const std::vector< IntegerVariable > &integer_variables, IntegerValue upper_bound, LinearConstraint *result)
bool Add(glop::ColIndex col, IntegerValue value)
bool AddLinearExpressionMultiple(IntegerValue multiplier, const std::vector< std::pair< glop::ColIndex, IntegerValue > > &terms)
void ClearAndResize(int size)
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
std::vector< Literal > * MutableConflict()
int CurrentDecisionLevel() const
void ProcessVariables(const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
void AddOneConstraint(glop::RowIndex, const std::vector< std::pair< glop::ColIndex, IntegerValue > > &terms, IntegerValue lb, IntegerValue ub)
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
StrictITIVector< ColIndex, Fractional > DenseRow
std::string GetProblemStatusString(ProblemStatus problem_status)
ColIndex RowToColIndex(RowIndex row)
RowIndex ColToRowIndex(ColIndex col)
StrictITIVector< RowIndex, Fractional > DenseColumn
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
CutGenerator CreateCVRPCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const std::vector< int64_t > &demands, int64_t capacity, Model *model)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
IntegerVariable PositiveVariable(IntegerVariable i)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
void SeparateSubtourInequalities(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const absl::StrongVector< IntegerVariable, double > &lp_values, absl::Span< const int64_t > demands, int64_t capacity, LinearConstraintManager *manager, Model *model)
bool VariableIsPositive(IntegerVariable i)
void DivideByGCD(LinearConstraint *constraint)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
std::pair< int64_t, int64_t > Arc
int64_t CapProd(int64_t x, int64_t y)
std::vector< IntegerVariable > vars
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
#define VLOG_IS_ON(verboselevel)