30#include "absl/container/flat_hash_map.h"
31#include "absl/log/check.h"
32#include "absl/numeric/int128.h"
33#include "absl/strings/str_cat.h"
34#include "absl/strings/string_view.h"
35#include "absl/types/span.h"
40#include "ortools/glop/parameters.pb.h"
58#include "ortools/sat/sat_parameters.pb.h"
78 for (
const glop::ColIndex
col : non_zeros_) {
79 dense_vector_[
col] = IntegerValue(0);
85 for (
const glop::ColIndex
col : non_zeros_) {
86 is_zeros_[
col] =
true;
94 const int64_t add =
CapAdd(
value.value(), dense_vector_[
col].value());
95 if (add == std::numeric_limits<int64_t>::min() ||
96 add == std::numeric_limits<int64_t>::max())
98 dense_vector_[
col] = IntegerValue(add);
99 if (is_sparse_ && is_zeros_[
col]) {
100 is_zeros_[
col] =
false;
101 non_zeros_.push_back(
col);
106template <
bool check_overflow>
108 const IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
109 absl::Span<const IntegerValue> coeffs) {
110 const double threshold = 0.1 *
static_cast<double>(dense_vector_.size());
111 const int num_terms = cols.size();
112 if (is_sparse_ &&
static_cast<double>(num_terms) < threshold) {
113 for (
int i = 0;
i < num_terms; ++
i) {
114 if (is_zeros_[cols[
i]]) {
115 is_zeros_[cols[
i]] =
false;
116 non_zeros_.push_back(cols[
i]);
118 if (check_overflow) {
119 if (!
AddProductTo(multiplier, coeffs[
i], &dense_vector_[cols[
i]])) {
123 dense_vector_[cols[
i]] += multiplier * coeffs[
i];
126 if (
static_cast<double>(non_zeros_.size()) > threshold) {
131 for (
int i = 0;
i < num_terms; ++
i) {
132 if (check_overflow) {
133 if (!
AddProductTo(multiplier, coeffs[
i], &dense_vector_[cols[
i]])) {
137 dense_vector_[cols[
i]] += multiplier * coeffs[
i];
145 absl::Span<const IntegerVariable> integer_variables,
147 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term) {
151 for (
const glop::ColIndex
col : non_zeros_) {
152 const IntegerValue coeff = dense_vector_[
col];
153 if (coeff == 0)
continue;
157 for (
const IntegerValue coeff : dense_vector_) {
158 if (coeff != 0) ++final_size;
161 if (extra_term != std::nullopt) ++final_size;
165 result.
resize(final_size);
170 std::sort(non_zeros_.begin(), non_zeros_.end());
171 for (
const glop::ColIndex
col : non_zeros_) {
172 const IntegerValue coeff = dense_vector_[
col];
173 if (coeff == 0)
continue;
174 result.
vars[new_size] = integer_variables[
col.value()];
175 result.
coeffs[new_size] = coeff;
179 const int size = dense_vector_.size();
181 const IntegerValue coeff = dense_vector_[
col];
182 if (coeff == 0)
continue;
183 result.
vars[new_size] = integer_variables[
col.value()];
184 result.
coeffs[new_size] = coeff;
192 if (extra_term != std::nullopt) {
193 result.
vars[new_size] += extra_term->first;
194 result.
coeffs[new_size] += extra_term->second;
198 CHECK_EQ(new_size, final_size);
204 absl::int128 rhs, absl::Span<const IntegerVariable> integer_variables,
205 absl::Span<const double> lp_solution,
IntegerTrail* integer_trail,
207 result->
terms.clear();
210 std::sort(non_zeros_.begin(), non_zeros_.end());
211 for (
const glop::ColIndex
col : non_zeros_) {
212 const IntegerValue coeff = dense_vector_[
col];
213 if (coeff == 0)
continue;
214 const IntegerVariable
var = integer_variables[
col.value()];
220 const int size = dense_vector_.size();
222 const IntegerValue coeff = dense_vector_[
col];
223 if (coeff == 0)
continue;
224 const IntegerVariable
var = integer_variables[
col.value()];
232std::vector<std::pair<glop::ColIndex, IntegerValue>>
234 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
236 std::sort(non_zeros_.begin(), non_zeros_.end());
237 for (
const glop::ColIndex
col : non_zeros_) {
238 const IntegerValue coeff = dense_vector_[
col];
239 if (coeff != 0) result.push_back({
col, coeff});
242 const int size = dense_vector_.size();
244 const IntegerValue coeff = dense_vector_[
col];
245 if (coeff != 0) result.push_back({
col, coeff});
254 Model*
model, absl::Span<const IntegerVariable> vars)
255 : constraint_manager_(
model),
256 parameters_(*(
model->GetOrCreate<SatParameters>())),
268 rlt_cut_helper_(
model),
269 implied_bounds_processor_({}, integer_trail_,
274 simplex_params_.set_use_dual_simplex(
true);
275 simplex_params_.set_cost_scaling(glop::GlopParameters::MEAN_COST_SCALING);
276 simplex_params_.set_primal_feasibility_tolerance(
277 parameters_.lp_primal_tolerance());
278 simplex_params_.set_dual_feasibility_tolerance(
279 parameters_.lp_dual_tolerance());
280 if (parameters_.use_exact_lp_reason()) {
281 simplex_params_.set_change_status_to_imprecise(
false);
283 simplex_.SetParameters(simplex_params_);
284 if (parameters_.search_branching() == SatParameters::LP_SEARCH) {
285 compute_reduced_cost_averages_ =
true;
289 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
292 auto* stats =
model->GetOrCreate<SharedStatistics>();
293 integer_rounding_cut_helper_.SetSharedStatistics(stats);
294 cover_cut_helper_.SetSharedStatistics(stats);
297 CHECK(std::is_sorted(vars.begin(), vars.end()));
299 integer_variables_.assign(vars.begin(), vars.end());
301 for (
const IntegerVariable positive_variable : vars) {
303 implied_bounds_processor_.AddLpVariable(positive_variable);
304 (*dispatcher_)[positive_variable] =
this;
305 mirror_lp_variable_[positive_variable] =
col;
309 lp_solution_.assign(vars.size(), std::numeric_limits<double>::infinity());
310 lp_reduced_cost_.assign(vars.size(), 0.0);
313 const int max_index =
NegationOf(vars.back()).value();
314 if (max_index >= expanded_lp_solution_.size()) {
315 expanded_lp_solution_.assign(max_index + 1, 0.0);
321 DCHECK(!lp_constraint_is_registered_);
322 constraint_manager_.
Add(std::move(
ct));
325glop::ColIndex LinearProgrammingConstraint::GetMirrorVariable(
326 IntegerVariable positive_variable) {
328 return mirror_lp_variable_.at(positive_variable);
332 IntegerValue coeff) {
333 CHECK(!lp_constraint_is_registered_);
334 objective_is_defined_ =
true;
336 if (ivar != pos_var) coeff = -coeff;
339 const glop::ColIndex
col = GetMirrorVariable(pos_var);
340 integer_objective_.push_back({
col, coeff});
341 objective_infinity_norm_ =
342 std::max(objective_infinity_norm_,
IntTypeAbs(coeff));
358bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
363 integer_lp_cols_.clear();
364 integer_lp_coeffs_.clear();
366 infinity_norms_.clear();
367 const auto& all_constraints = constraint_manager_.
AllConstraints();
371 integer_lp_.
push_back(LinearConstraintInternal());
372 LinearConstraintInternal& new_ct = integer_lp_.back();
375 new_ct.lb_is_trivial = all_constraints[
index].lb_is_trivial;
376 new_ct.ub_is_trivial = all_constraints[
index].ub_is_trivial;
377 const int size =
ct.num_terms;
379 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
383 IntegerValue infinity_norm = 0;
384 infinity_norm = std::max(infinity_norm,
IntTypeAbs(
ct.lb));
385 infinity_norm = std::max(infinity_norm,
IntTypeAbs(
ct.ub));
386 new_ct.start_in_buffer = integer_lp_cols_.size();
387 new_ct.num_terms =
size;
388 for (
int i = 0;
i <
size; ++
i) {
390 const IntegerVariable
var =
ct.vars[
i];
391 const IntegerValue coeff =
ct.coeffs[
i];
392 infinity_norm = std::max(infinity_norm,
IntTypeAbs(coeff));
393 integer_lp_cols_.push_back(GetMirrorVariable(
var));
394 integer_lp_coeffs_.push_back(coeff);
396 infinity_norms_.
push_back(infinity_norm);
399 DCHECK(std::is_sorted(
400 integer_lp_cols_.data() + new_ct.start_in_buffer,
401 integer_lp_cols_.data() + new_ct.start_in_buffer + new_ct.num_terms));
406 for (
int i = 0;
i < integer_variables_.size(); ++
i) {
413 objective_infinity_norm_ = 0;
414 for (
const auto& entry : integer_objective_) {
415 const IntegerVariable
var = integer_variables_[entry.first.value()];
417 integer_objective_offset_ +=
421 objective_infinity_norm_ =
422 std::max(objective_infinity_norm_,
IntTypeAbs(entry.second));
423 integer_objective_[new_size++] = entry;
426 objective_infinity_norm_ =
427 std::max(objective_infinity_norm_,
IntTypeAbs(integer_objective_offset_));
428 integer_objective_.resize(new_size);
431 for (
const LinearConstraintInternal&
ct : integer_lp_) {
438 const double infinity = std::numeric_limits<double>::infinity();
442 for (
int i = 0;
i <
ct.num_terms; ++
i) {
443 const int index =
ct.start_in_buffer +
i;
456 const int num_vars = integer_variables_.size();
457 for (
int i = 0;
i < num_vars;
i++) {
458 const IntegerVariable cp_var = integer_variables_[
i];
466 scaler_.
Scale(simplex_params_, &lp_data_);
467 UpdateBoundsOfLpVariables();
472 if (parameters_.polish_lp_solution()) {
474 for (
int i = 0;
i < num_vars; ++
i) {
475 const IntegerVariable cp_var = integer_variables_[
i];
478 if (lb != 0 || ub != 1)
continue;
488 <<
" Managed constraints.";
492void LinearProgrammingConstraint::FillReducedCostReasonIn(
494 std::vector<IntegerLiteral>* integer_reason) {
495 integer_reason->clear();
496 const int num_vars = integer_variables_.size();
497 for (
int i = 0;
i < num_vars;
i++) {
498 const double rc = reduced_costs[glop::ColIndex(
i)];
499 if (rc > kLpEpsilon) {
500 integer_reason->push_back(
502 }
else if (rc < -kLpEpsilon) {
503 integer_reason->push_back(
512 DCHECK(!lp_constraint_is_registered_);
513 lp_constraint_is_registered_ =
true;
518 std::sort(integer_objective_.begin(), integer_objective_.end());
525 if (!parameters_.add_lp_constraints_lazily() &&
529 if (!CreateLpFromConstraintManager()) {
534 watcher_id_ = watcher_->
Register(
this);
535 const int num_vars = integer_variables_.size();
536 for (
int i = 0;
i < num_vars;
i++) {
539 if (objective_is_defined_) {
553 if (level == 0) rev_optimal_constraints_size_ = 0;
554 optimal_constraints_.resize(rev_optimal_constraints_size_);
555 cumulative_optimal_constraint_sizes_.resize(rev_optimal_constraints_size_);
556 if (lp_solution_is_set_ && level < lp_solution_level_) {
557 lp_solution_is_set_ =
false;
560 if (level < previous_level_) {
561 lp_at_optimal_ =
false;
562 lp_objective_lower_bound_ = -std::numeric_limits<double>::infinity();
564 previous_level_ = level;
573 if (level == 0 && !lp_solution_is_set_ && !level_zero_lp_solution_.empty()) {
574 lp_solution_is_set_ =
true;
575 lp_solution_ = level_zero_lp_solution_;
576 lp_solution_level_ = 0;
577 for (
int i = 0;
i < lp_solution_.size();
i++) {
578 expanded_lp_solution_[integer_variables_[
i]] = lp_solution_[
i];
579 expanded_lp_solution_[
NegationOf(integer_variables_[
i])] =
586 cut_generators_.push_back(std::move(generator));
590 const std::vector<int>& watch_indices) {
591 if (!enabled_)
return true;
601 if (!cumulative_optimal_constraint_sizes_.empty()) {
602 const double current_size =
603 static_cast<double>(cumulative_optimal_constraint_sizes_.back());
604 const double low_limit = 1e7;
605 if (current_size > low_limit) {
608 const double num_enqueues =
static_cast<double>(integer_trail_->
Index());
609 if ((current_size - low_limit) > 100 * num_enqueues)
return true;
613 if (!lp_solution_is_set_) {
625 for (
const int index : watch_indices) {
652 glop::ColIndex
var) {
657 IntegerVariable variable)
const {
658 return lp_solution_[mirror_lp_variable_.at(variable).value()];
662 IntegerVariable variable)
const {
663 return lp_reduced_cost_[mirror_lp_variable_.at(variable).value()];
666void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
667 const int num_vars = integer_variables_.size();
668 for (
int i = 0;
i < num_vars;
i++) {
669 const IntegerVariable cp_var = integer_variables_[
i];
677bool LinearProgrammingConstraint::SolveLp() {
680 lp_at_level_zero_is_final_ =
false;
683 const auto status = simplex_.
Solve(lp_data_, time_limit_);
687 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
691 average_degeneracy_.
AddData(CalculateDegeneracy());
693 VLOG(2) <<
"High average degeneracy: "
702 if (status_as_int >= num_solves_by_status_.size()) {
703 num_solves_by_status_.resize(status_as_int + 1);
706 num_solves_by_status_[status_as_int]++;
722 lp_solution_is_set_ =
true;
724 const int num_vars = integer_variables_.size();
725 for (
int i = 0;
i < num_vars;
i++) {
727 GetVariableValueAtCpScale(glop::ColIndex(
i));
729 expanded_lp_solution_[integer_variables_[
i]] =
value;
733 if (lp_solution_level_ == 0) {
734 level_zero_lp_solution_ = lp_solution_;
741bool LinearProgrammingConstraint::AnalyzeLp() {
744 if (parameters_.use_exact_lp_reason()) {
745 return PropagateExactDualRay();
753 UpdateSimplexIterationLimit(10, 1000);
757 if (objective_is_defined_ &&
762 if (parameters_.use_exact_lp_reason()) {
763 if (!PropagateExactLpReason())
return false;
768 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
769 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
770 const IntegerValue propagated_lb =
772 if (approximate_new_lb > propagated_lb) {
773 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
775 <<
" ] approx_lb += "
776 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
777 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
785 const double objective_cp_ub =
788 ReducedCostStrengtheningDeductions(objective_cp_ub -
789 relaxed_optimal_objective);
790 if (!deductions_.empty()) {
791 deductions_reason_ = integer_reason_;
792 deductions_reason_.push_back(
797 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
798 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
799 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
800 const IntegerLiteral deduction =
802 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
808 if (!deductions_.empty()) {
809 const int trail_index_with_same_reason = integer_trail_->
Index();
810 for (
const IntegerLiteral deduction : deductions_) {
811 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
812 trail_index_with_same_reason)) {
822 CHECK(lp_solution_is_set_);
823 lp_solution_is_integer_ =
true;
824 const int num_vars = integer_variables_.size();
825 for (
int i = 0;
i < num_vars;
i++) {
828 if (std::abs(lp_solution_[
i] - std::round(lp_solution_[
i])) >
830 lp_solution_is_integer_ =
false;
834 if (compute_reduced_cost_averages_) {
835 UpdateAverageReducedCosts();
845 if (objective_is_defined_ &&
862bool LinearProgrammingConstraint::PreprocessCut(IntegerVariable first_slack,
865 cut->ComplementForPositiveCoefficients();
867 problem_proven_infeasible_by_cuts_ =
true;
874 for (
const CutTerm& term : cut->terms) {
875 reachable_.
Add(term.coeff.value());
879 if (cut->rhs < absl::int128(reachable_.
LastValue()) &&
881 problem_proven_infeasible_by_cuts_ =
true;
885 bool some_fixed_terms =
false;
886 bool some_relevant_positions =
false;
887 for (CutTerm& term : cut->terms) {
888 const absl::int128 magnitude128 = term.coeff.value();
889 const absl::int128
range =
890 absl::int128(term.bound_diff.value()) * magnitude128;
892 IntegerValue new_diff = term.bound_diff;
893 if (
range > cut->rhs) {
894 new_diff =
static_cast<int64_t
>(cut->rhs / magnitude128);
898 absl::int128 rest128 =
899 cut->rhs - absl::int128(new_diff.value()) * magnitude128;
900 while (rest128 < absl::int128(reachable_.
LastValue()) &&
902 ++total_num_eq_propagations_;
903 CHECK_GT(new_diff, 0);
905 rest128 += magnitude128;
909 if (new_diff < term.bound_diff) {
910 term.bound_diff = new_diff;
912 const IntegerVariable
var = term.expr_vars[0];
913 if (
var < first_slack) {
915 ++total_num_cut_propagations_;
918 if (term.expr_coeffs[0] == 1) {
922 var, term.bound_diff - term.expr_offset),
924 problem_proven_infeasible_by_cuts_ =
true;
928 CHECK_EQ(term.expr_coeffs[0], -1);
932 var, term.expr_offset - term.bound_diff),
934 problem_proven_infeasible_by_cuts_ =
true;
942 const int slack_index = (
var.value() - first_slack.value()) / 2;
943 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
944 if (term.expr_coeffs[0] == 1) {
946 const IntegerValue new_ub = term.bound_diff - term.expr_offset;
948 integer_lp_[
row].ub = new_ub;
952 CHECK_EQ(term.expr_coeffs[0], -1);
953 const IntegerValue new_lb = term.expr_offset - term.bound_diff;
955 integer_lp_[
row].lb = new_lb;
961 if (term.bound_diff == 0) {
962 some_fixed_terms =
true;
964 if (term.HasRelevantLpValue()) {
965 some_relevant_positions =
true;
971 if (some_fixed_terms) {
973 for (
const CutTerm& term : cut->terms) {
974 if (term.bound_diff == 0)
continue;
975 cut->terms[new_size++] = term;
977 cut->terms.resize(new_size);
979 return some_relevant_positions;
982bool LinearProgrammingConstraint::AddCutFromConstraints(
983 absl::string_view
name,
984 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers) {
995 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
997 ++num_cut_overflows_;
998 VLOG(1) <<
"Issue, overflow!";
1010 lp_solution_, integer_trail_,
1015 ImpliedBoundsProcessor* ib_processor =
nullptr;
1017 bool some_ints =
false;
1018 bool some_relevant_positions =
false;
1019 for (
const CutTerm& term : base_ct_.
terms) {
1020 if (term.bound_diff > 1) some_ints =
true;
1021 if (term.HasRelevantLpValue()) some_relevant_positions =
true;
1025 if (!some_relevant_positions)
return false;
1026 if (some_ints) ib_processor = &implied_bounds_processor_;
1031 const IntegerVariable first_slack(expanded_lp_solution_.size());
1032 CHECK_EQ(first_slack.value() % 2, 0);
1033 tmp_slack_rows_.clear();
1034 for (
const auto [
row, coeff] : integer_multipliers) {
1035 if (integer_lp_[
row].lb == integer_lp_[
row].ub)
continue;
1038 entry.coeff = coeff > 0 ? coeff : -coeff;
1039 entry.lp_value = 0.0;
1040 entry.bound_diff = integer_lp_[
row].ub - integer_lp_[
row].lb;
1041 entry.expr_vars[0] =
1042 first_slack + 2 * IntegerVariable(tmp_slack_rows_.size());
1043 entry.expr_coeffs[1] = 0;
1048 entry.lp_value =
ToDouble(integer_lp_[
row].ub) - activity;
1049 entry.expr_coeffs[0] = IntegerValue(-1);
1050 entry.expr_offset = integer_lp_[
row].ub;
1053 entry.lp_value = activity -
ToDouble(integer_lp_[
row].lb);
1054 entry.expr_coeffs[0] = IntegerValue(1);
1055 entry.expr_offset = -integer_lp_[
row].lb;
1058 base_ct_.
terms.push_back(entry);
1059 tmp_slack_rows_.push_back(
row);
1063 if (!PreprocessCut(first_slack, &base_ct_))
return false;
1066 if (base_ct_.
rhs == 1)
return false;
1070 double activity = 0.0;
1072 for (
const CutTerm& term : base_ct_.
terms) {
1073 const double coeff =
ToDouble(term.coeff);
1074 activity += term.lp_value * coeff;
1075 norm += coeff * coeff;
1077 if (std::abs(activity -
static_cast<double>(base_ct_.
rhs)) / norm > 1e-4) {
1078 VLOG(1) <<
"Cut not tight " << activity
1079 <<
" <= " <<
static_cast<double>(base_ct_.
rhs);
1084 bool at_least_one_added =
false;
1090 if (integer_multipliers.size() == 1 && parameters_.add_rlt_cuts()) {
1092 at_least_one_added |= PostprocessAndAddCut(
1093 absl::StrCat(
name,
"_RLT"), rlt_cut_helper_.
Info(), first_slack,
1094 rlt_cut_helper_.
cut());
1099 if (ib_processor !=
nullptr) {
1100 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1101 ib_processor =
nullptr;
1111 at_least_one_added |= PostprocessAndAddCut(
1112 absl::StrCat(
name,
"_FF"), cover_cut_helper_.
Info(), first_slack,
1113 cover_cut_helper_.
cut());
1116 at_least_one_added |= PostprocessAndAddCut(
1117 absl::StrCat(
name,
"_K"), cover_cut_helper_.
Info(), first_slack,
1118 cover_cut_helper_.
cut());
1125 at_least_one_added |= PostprocessAndAddCut(
1126 absl::StrCat(
name,
"_KL"), cover_cut_helper_.
Info(), first_slack,
1127 cover_cut_helper_.
cut());
1135 RoundingOptions options;
1136 options.max_scaling = parameters_.max_integer_rounding_scaling();
1137 options.use_ib_before_heuristic =
false;
1138 if (integer_rounding_cut_helper_.
ComputeCut(options, base_ct_,
1140 at_least_one_added |= PostprocessAndAddCut(
1141 absl::StrCat(
name,
"_R"), integer_rounding_cut_helper_.
Info(),
1142 first_slack, integer_rounding_cut_helper_.
cut());
1145 options.use_ib_before_heuristic =
true;
1146 options.prefer_positive_ib =
false;
1147 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.
ComputeCut(
1148 options, base_ct_, ib_processor)) {
1149 at_least_one_added |= PostprocessAndAddCut(
1150 absl::StrCat(
name,
"_RB"), integer_rounding_cut_helper_.
Info(),
1151 first_slack, integer_rounding_cut_helper_.
cut());
1154 options.use_ib_before_heuristic =
true;
1155 options.prefer_positive_ib =
true;
1156 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.
ComputeCut(
1157 options, base_ct_, ib_processor)) {
1158 at_least_one_added |= PostprocessAndAddCut(
1159 absl::StrCat(
name,
"_RBP"), integer_rounding_cut_helper_.
Info(),
1160 first_slack, integer_rounding_cut_helper_.
cut());
1164 return at_least_one_added;
1167bool LinearProgrammingConstraint::PostprocessAndAddCut(
1168 const std::string&
name,
const std::string& info,
1169 IntegerVariable first_slack,
const CutData& cut) {
1170 if (cut.rhs > absl::int128(std::numeric_limits<int64_t>::max()) ||
1171 cut.rhs < absl::int128(std::numeric_limits<int64_t>::min())) {
1172 VLOG(2) <<
"RHS overflow " <<
name <<
" " << info;
1173 ++num_cut_overflows_;
1181 if (cut.ComputeViolation() < 1e-4) {
1182 VLOG(2) <<
"Bad cut " <<
name <<
" " << info;
1189 IntegerValue cut_ub =
static_cast<int64_t
>(cut.rhs);
1190 for (
const CutTerm& term : cut.terms) {
1191 if (term.coeff == 0)
continue;
1192 if (!
AddProductTo(-term.coeff, term.expr_offset, &cut_ub)) {
1193 VLOG(2) <<
"Overflow in conversion";
1194 ++num_cut_overflows_;
1197 for (
int i = 0;
i < 2; ++
i) {
1198 if (term.expr_coeffs[
i] == 0)
continue;
1199 const IntegerVariable
var = term.expr_vars[
i];
1200 const IntegerValue coeff =
1201 CapProd(term.coeff.value(), term.expr_coeffs[
i].value());
1203 VLOG(2) <<
"Overflow in conversion";
1204 ++num_cut_overflows_;
1209 if (
var < first_slack) {
1210 const glop::ColIndex
col =
1213 tmp_scattered_vector_.
Add(
col, coeff);
1215 tmp_scattered_vector_.
Add(
col, -coeff);
1220 const int slack_index = (
var.value() - first_slack.value()) / 2;
1221 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
1223 coeff, IntegerLpRowCols(
row), IntegerLpRowCoeffs(
row))) {
1224 VLOG(2) <<
"Overflow in slack removal";
1225 ++num_cut_overflows_;
1235 LinearConstraint converted_cut =
1242 top_n_cuts_.
AddCut(std::move(converted_cut),
name, expanded_lp_solution_);
1245 return constraint_manager_.
AddCut(std::move(converted_cut),
name, info);
1252void LinearProgrammingConstraint::AddCGCuts() {
1258 const bool old_gomory =
true;
1266 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
1274 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
1278 if (old_gomory && basis_col >= integer_variables_.size())
continue;
1281 tmp_lp_multipliers_.clear();
1283 if (lambda.non_zeros.empty()) {
1284 for (RowIndex
row(0);
row < num_rows; ++
row) {
1286 if (std::abs(
value) < kZeroTolerance)
continue;
1287 tmp_lp_multipliers_.push_back({
row,
value});
1290 for (
const ColIndex
col : lambda.non_zeros) {
1292 const double value = lambda.values[
col];
1293 if (std::abs(
value) < kZeroTolerance)
continue;
1294 tmp_lp_multipliers_.push_back({
row,
value});
1299 if (tmp_lp_multipliers_.size() <= 1)
continue;
1301 IntegerValue scaling;
1302 for (
int i = 0;
i < 2; ++
i) {
1308 for (std::pair<RowIndex, double>& p : tmp_lp_multipliers_) {
1309 p.second = -p.second;
1315 tmp_integer_multipliers_ = ScaleLpMultiplier(
1317 old_gomory, tmp_lp_multipliers_,
1320 AddCutFromConstraints(
"CG", tmp_integer_multipliers_);
1335void LinearProgrammingConstraint::AddObjectiveCut() {
1336 if (integer_objective_.size() <= 1)
return;
1342 const IntegerValue obj_lower_bound =
1344 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1347 LinearConstraint objective_ct;
1349 objective_ct.ub = integer_objective_offset_ -
1351 IntegerValue obj_coeff_magnitude(0);
1352 objective_ct.resize(integer_objective_.size());
1354 for (
const auto& [
col, coeff] : integer_objective_) {
1355 const IntegerVariable
var = integer_variables_[
col.value()];
1356 objective_ct.vars[
i] =
var;
1357 objective_ct.coeffs[
i] = -coeff;
1358 obj_coeff_magnitude = std::max(obj_coeff_magnitude,
IntTypeAbs(coeff));
1370 if (obj_coeff_magnitude < 1e9 &&
1371 constraint_manager_.
AddCut(std::move(objective_ct),
"Objective")) {
1377 ImpliedBoundsProcessor* ib_processor =
nullptr;
1379 bool some_ints =
false;
1380 bool some_relevant_positions =
false;
1381 for (
const CutTerm& term : base_ct_.
terms) {
1382 if (term.bound_diff > 1) some_ints =
true;
1383 if (term.HasRelevantLpValue()) some_relevant_positions =
true;
1387 if (!some_relevant_positions)
return;
1388 if (some_ints) ib_processor = &implied_bounds_processor_;
1392 const IntegerVariable first_slack(
1393 std::numeric_limits<IntegerVariable::ValueType>::max());
1394 if (ib_processor !=
nullptr) {
1395 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1396 ib_processor =
nullptr;
1404 PostprocessAndAddCut(
"Objective_K", cover_cut_helper_.
Info(), first_slack,
1405 cover_cut_helper_.
cut());
1409 RoundingOptions options;
1410 options.max_scaling = parameters_.max_integer_rounding_scaling();
1412 if (integer_rounding_cut_helper_.
ComputeCut(options, base_ct_,
1414 PostprocessAndAddCut(
"Objective_R", integer_rounding_cut_helper_.
Info(),
1415 first_slack, integer_rounding_cut_helper_.
cut());
1419void LinearProgrammingConstraint::AddMirCuts() {
1435 integer_variables_.size(), IntegerValue(0));
1436 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1441 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1445 for (RowIndex
row(0);
row < num_rows; ++
row) {
1458 base_rows.push_back({
row, IntegerValue(1)});
1464 base_rows.push_back({
row, IntegerValue(-1)});
1488 int64_t dtime_num_entries = 0;
1489 std::shuffle(base_rows.begin(), base_rows.end(), *random_);
1491 std::vector<double> weights;
1493 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1494 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1496 if (dtime_num_entries > 1e7)
break;
1498 const glop::RowIndex base_row = entry.first;
1499 const IntegerValue multiplier = entry.second;
1508 integer_multipliers = {entry};
1509 if ((multiplier > 0 && !integer_lp_[base_row].ub_is_trivial) ||
1510 (multiplier < 0 && !integer_lp_[base_row].lb_is_trivial)) {
1511 if (AddCutFromConstraints(
"MIR_1", integer_multipliers))
continue;
1515 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1516 dense_cut[
col] = IntegerValue(0);
1518 non_zeros_.SparseClearAll();
1521 const LinearConstraintInternal&
ct = integer_lp_[entry.first];
1522 for (
int i = 0;
i <
ct.num_terms; ++
i) {
1523 const int index =
ct.start_in_buffer +
i;
1524 const ColIndex
col = integer_lp_cols_[
index];
1525 const IntegerValue coeff = integer_lp_coeffs_[
index];
1526 non_zeros_.Set(
col);
1527 dense_cut[
col] += coeff * multiplier;
1530 used_rows.
assign(num_rows,
false);
1531 used_rows[entry.first] =
true;
1536 const int kMaxAggregation = 5;
1537 for (
int i = 0;
i < kMaxAggregation; ++
i) {
1540 IntegerValue max_magnitude(0);
1542 std::vector<ColIndex> col_candidates;
1543 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1544 if (dense_cut[
col] == 0)
continue;
1546 max_magnitude = std::max(max_magnitude,
IntTypeAbs(dense_cut[
col]));
1547 const int col_degree =
1549 if (col_degree <= 1)
continue;
1554 const IntegerVariable
var = integer_variables_[
col.value()];
1555 const double lp_value = expanded_lp_solution_[
var];
1558 const double bound_distance = std::min(ub - lp_value, lp_value - lb);
1559 if (bound_distance > 1e-2) {
1560 weights.push_back(bound_distance);
1561 col_candidates.push_back(
col);
1564 if (col_candidates.empty())
break;
1566 const ColIndex var_to_eliminate =
1570 std::vector<RowIndex> possible_rows;
1573 const RowIndex
row = entry.row();
1578 if (used_rows[
row])
continue;
1579 used_rows[
row] =
true;
1582 bool add_row =
false;
1584 if (entry.coefficient() > 0.0) {
1585 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1587 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1591 if (entry.coefficient() > 0.0) {
1592 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1594 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1598 possible_rows.push_back(
row);
1599 weights.push_back(row_weights[
row]);
1602 if (possible_rows.empty())
break;
1604 const RowIndex row_to_combine =
1608 IntegerValue to_combine_coeff = 0;
1609 const LinearConstraintInternal& ct_to_combine =
1610 integer_lp_[row_to_combine];
1611 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
1612 const int index = ct_to_combine.start_in_buffer +
i;
1613 if (integer_lp_cols_[
index] == var_to_eliminate) {
1614 to_combine_coeff = integer_lp_coeffs_[
index];
1618 CHECK_NE(to_combine_coeff, 0);
1620 IntegerValue mult1 = -to_combine_coeff;
1621 IntegerValue mult2 = dense_cut[var_to_eliminate];
1628 const IntegerValue gcd = IntegerValue(
1638 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1639 max_magnitude = std::max(max_magnitude,
IntTypeAbs(entry.second));
1641 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1643 std::abs(mult2.value()))) ==
1644 std::numeric_limits<int64_t>::max()) {
1648 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1649 dtime_num_entries += integer_lp_[entry.first].num_terms;
1650 entry.second *= mult1;
1652 dtime_num_entries += integer_lp_[row_to_combine].num_terms;
1653 integer_multipliers.push_back({row_to_combine, mult2});
1656 if (AddCutFromConstraints(absl::StrCat(
"MIR_",
i + 2),
1657 integer_multipliers)) {
1663 if (
i + 1 == kMaxAggregation)
break;
1665 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1666 dense_cut[
col] *= mult1;
1668 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
1669 const int index = ct_to_combine.start_in_buffer +
i;
1670 const ColIndex
col = integer_lp_cols_[
index];
1671 const IntegerValue coeff = integer_lp_coeffs_[
index];
1672 non_zeros_.Set(
col);
1673 dense_cut[
col] += coeff * mult2;
1679void LinearProgrammingConstraint::AddZeroHalfCuts() {
1682 tmp_lp_values_.clear();
1683 tmp_var_lbs_.clear();
1684 tmp_var_ubs_.clear();
1685 for (
const IntegerVariable
var : integer_variables_) {
1686 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1694 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1702 row, IntegerLpRowCols(
row), IntegerLpRowCoeffs(
row),
1703 integer_lp_[
row].lb, integer_lp_[
row].ub);
1705 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1713 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1717void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1718 const int64_t min_iter,
const int64_t max_iter) {
1719 if (parameters_.linearization_level() < 2)
return;
1720 const int64_t num_degenerate_columns = CalculateDegeneracy();
1722 if (num_cols <= 0) {
1725 CHECK_GT(num_cols, 0);
1726 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1731 if (is_degenerate_) {
1732 next_simplex_iter_ /= std::max(int64_t{1}, decrease_factor);
1734 next_simplex_iter_ *= 2;
1737 if (is_degenerate_) {
1738 next_simplex_iter_ /= std::max(int64_t{1}, 2 * decrease_factor);
1742 next_simplex_iter_ = num_cols / 40;
1745 next_simplex_iter_ =
1746 std::max(min_iter, std::min(max_iter, next_simplex_iter_));
1750 if (!enabled_)
return true;
1752 UpdateBoundsOfLpVariables();
1756 if ( (
false) && objective_is_defined_) {
1764 simplex_params_.set_objective_upper_limit(
1765 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1766 100.0 * kCpEpsilon));
1773 simplex_params_.set_max_number_of_iterations(
1774 parameters_.root_lp_iterations());
1776 simplex_params_.set_max_number_of_iterations(next_simplex_iter_);
1780 if (!SolveLp())
return true;
1781 if (!AnalyzeLp())
return false;
1785 ? parameters_.max_cut_rounds_at_level_zero()
1789 cuts_round < max_cuts_rounds) {
1794 if (parameters_.cut_level() > 0 &&
1795 (num_solves_ > 1 || !parameters_.add_lp_constraints_lazily())) {
1798 expanded_lp_solution_);
1799 if (parameters_.add_rlt_cuts()) {
1800 rlt_cut_helper_.
Initialize(mirror_lp_variable_);
1809 problem_proven_infeasible_by_cuts_ =
false;
1810 if (parameters_.add_objective_cut()) AddObjectiveCut();
1811 if (parameters_.add_mir_cuts()) AddMirCuts();
1812 if (parameters_.add_cg_cuts()) AddCGCuts();
1813 if (parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1814 if (problem_proven_infeasible_by_cuts_) {
1821 if (level == 0 || !parameters_.only_add_cuts_at_level_zero()) {
1822 for (
const CutGenerator& generator : cut_generators_) {
1823 if (level > 0 && generator.only_run_at_level_zero)
continue;
1824 if (!generator.generate_cuts(&constraint_manager_)) {
1831 &constraint_manager_);
1835 if (constraint_manager_.
ChangeLp(&state_, &num_added)) {
1838 if (!CreateLpFromConstraintManager()) {
1844 if (num_added == 0) {
1849 if (!SolveLp())
return true;
1850 if (!AnalyzeLp())
return false;
1852 VLOG(3) <<
"Relaxation improvement " << old_obj <<
" -> "
1859 lp_at_level_zero_is_final_ =
true;
1868absl::int128 LinearProgrammingConstraint::GetImpliedLowerBound(
1872 for (
int i = 0;
i <
size; ++
i) {
1873 const IntegerVariable
var = terms.
vars[
i];
1874 const IntegerValue coeff = terms.
coeffs[
i];
1882bool LinearProgrammingConstraint::ScalingCanOverflow(
1883 int power,
bool take_objective_into_account,
1884 absl::Span<
const std::pair<glop::RowIndex, double>> multipliers,
1885 int64_t overflow_cap)
const {
1887 const int64_t factor = int64_t{1} << power;
1888 const double factor_as_double =
static_cast<double>(factor);
1889 if (take_objective_into_account) {
1891 if (
bound >= overflow_cap)
return true;
1893 for (
const auto [
row, double_coeff] : multipliers) {
1894 const double magnitude =
1895 std::abs(std::round(double_coeff * factor_as_double));
1896 if (std::isnan(magnitude))
return true;
1897 if (magnitude >=
static_cast<double>(std::numeric_limits<int64_t>::max())) {
1902 if (
bound >= overflow_cap)
return true;
1904 return bound >= overflow_cap;
1907std::vector<std::pair<RowIndex, IntegerValue>>
1908LinearProgrammingConstraint::ScaleLpMultiplier(
1909 bool take_objective_into_account,
bool ignore_trivial_constraints,
1910 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1911 IntegerValue* scaling, int64_t overflow_cap)
const {
1915 tmp_cp_multipliers_.clear();
1916 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1917 const RowIndex
row = p.first;
1922 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1928 if (ignore_trivial_constraints) {
1929 if (lp_multi > 0.0 && integer_lp_[
row].ub_is_trivial) {
1932 if (lp_multi < 0.0 && integer_lp_[
row].lb_is_trivial) {
1937 tmp_cp_multipliers_.push_back(
1941 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1942 if (tmp_cp_multipliers_.empty()) {
1944 return integer_multipliers;
1949 if (ScalingCanOverflow(0, take_objective_into_account,
1950 tmp_cp_multipliers_, overflow_cap)) {
1951 ++num_scaling_issues_;
1952 return integer_multipliers;
1965 if (candidate >= 63)
return false;
1967 return !ScalingCanOverflow(candidate, take_objective_into_account,
1968 tmp_cp_multipliers_, overflow_cap);
1970 *scaling = int64_t{1} << power;
1974 int64_t gcd = scaling->value();
1975 const double scaling_as_double =
static_cast<double>(scaling->value());
1976 for (
const auto [
row, double_coeff] : tmp_cp_multipliers_) {
1977 const IntegerValue coeff(std::round(double_coeff * scaling_as_double));
1979 gcd = std::gcd(gcd, std::abs(coeff.value()));
1980 integer_multipliers.push_back({
row, coeff});
1985 for (
auto& entry : integer_multipliers) {
1986 entry.second /= gcd;
1989 return integer_multipliers;
1992template <
bool check_overflow>
1993bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1994 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers,
2002 for (
const std::pair<RowIndex, IntegerValue>& term : integer_multipliers) {
2003 const RowIndex
row = term.first;
2004 const IntegerValue multiplier = term.second;
2005 CHECK_LT(
row, integer_lp_.size());
2009 multiplier, IntegerLpRowCols(
row), IntegerLpRowCoeffs(
row))) {
2014 const IntegerValue
bound =
2015 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
2023void LinearProgrammingConstraint::AdjustNewLinearConstraint(
2024 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
2026 const IntegerValue kMaxWantedCoeff(1e18);
2027 bool adjusted =
false;
2028 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
2029 const RowIndex
row = term.first;
2030 const IntegerValue multiplier = term.second;
2031 if (multiplier == 0)
continue;
2037 IntegerValue negative_limit =
2039 IntegerValue positive_limit = negative_limit;
2043 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
2044 if (multiplier > 0) {
2045 negative_limit = std::min(negative_limit, multiplier);
2047 positive_limit = std::min(positive_limit, -multiplier);
2052 const IntegerValue row_bound =
2053 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
2054 if (row_bound != 0) {
2058 const IntegerValue limit2 =
2061 positive_limit = std::min(positive_limit, limit1);
2062 negative_limit = std::min(negative_limit, limit2);
2064 negative_limit = std::min(negative_limit, limit1);
2065 positive_limit = std::min(positive_limit, limit2);
2077 double common_diff =
ToDouble(row_bound);
2078 double positive_diff = 0.0;
2079 double negative_diff = 0.0;
2084 const LinearConstraintInternal&
ct = integer_lp_[
row];
2085 for (
int i = 0;
i <
ct.num_terms; ++
i) {
2086 const int index =
ct.start_in_buffer +
i;
2087 const ColIndex
col = integer_lp_cols_[
index];
2088 const IntegerValue coeff = integer_lp_coeffs_[
index];
2089 DCHECK_NE(coeff, 0);
2094 const IntegerValue current = (*scattered_vector)[
col];
2096 const IntegerVariable
var = integer_variables_[
col.value()];
2119 const IntegerValue abs_coeff =
IntTypeAbs(coeff);
2120 const IntegerValue current_magnitude =
IntTypeAbs(current);
2121 const IntegerValue overflow_threshold =
2122 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude);
2123 if ((current > 0) == (coeff > 0)) {
2124 if (negative_limit * abs_coeff > current_magnitude) {
2125 negative_limit = current_magnitude / abs_coeff;
2126 if (positive_limit == 0 && negative_limit == 0)
break;
2128 if (positive_limit * abs_coeff > overflow_threshold) {
2129 positive_limit = overflow_threshold / abs_coeff;
2130 if (positive_limit == 0 && negative_limit == 0)
break;
2133 if (negative_limit * abs_coeff > overflow_threshold) {
2134 negative_limit = overflow_threshold / abs_coeff;
2135 if (positive_limit == 0 && negative_limit == 0)
break;
2137 if (positive_limit * abs_coeff > current_magnitude) {
2138 positive_limit = current_magnitude / abs_coeff;
2139 if (positive_limit == 0 && negative_limit == 0)
break;
2144 const IntegerVariable
var = integer_variables_[
col.value()];
2145 const IntegerValue implied = current > 0
2153 positive_diff += common_diff;
2154 negative_diff += common_diff;
2160 IntegerValue to_add(0);
2161 if (positive_diff <= -1.0 && positive_limit > 0) {
2162 to_add = positive_limit;
2164 if (negative_diff >= 1.0 && negative_limit > 0) {
2167 std::abs(
ToDouble(negative_limit) * negative_diff) >
2168 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2169 to_add = -negative_limit;
2173 term.second += to_add;
2179 CHECK(scattered_vector
2180 ->AddLinearExpressionMultiple</*check_overflow=*/false>(
2181 to_add, IntegerLpRowCols(
row), IntegerLpRowCoeffs(
row)));
2184 if (adjusted) ++num_adjusts_;
2187bool LinearProgrammingConstraint::PropagateLpConstraint(LinearConstraint
ct) {
2191 const int num_terms =
ct.num_terms;
2192 if (num_terms == 0) {
2193 if (
ct.ub >= 0)
return true;
2197 std::unique_ptr<IntegerSumLE128> cp_constraint(
2202 if (!cp_constraint->PropagateAtLevelZero())
return false;
2208 const bool no_conflict = cp_constraint->Propagate();
2211 const int64_t current_size =
2212 cumulative_optimal_constraint_sizes_.empty()
2214 : cumulative_optimal_constraint_sizes_.back();
2215 optimal_constraints_.push_back(std::move(cp_constraint));
2216 cumulative_optimal_constraint_sizes_.push_back(current_size + num_terms);
2217 rev_optimal_constraints_size_ = optimal_constraints_.size();
2235bool LinearProgrammingConstraint::PropagateExactLpReason() {
2237 integer_reason_.clear();
2238 deductions_.clear();
2239 deductions_reason_.clear();
2245 tmp_lp_multipliers_.clear();
2246 for (RowIndex
row(0);
row < num_rows; ++
row) {
2248 if (std::abs(
value) < kZeroTolerance)
continue;
2249 tmp_lp_multipliers_.push_back({
row,
value});
2255 if (tmp_lp_multipliers_.empty())
return true;
2259 bool take_objective_into_account =
true;
2260 if (mirror_lp_variable_.contains(objective_cp_)) {
2263 CHECK_EQ(integer_objective_.size(), 1);
2264 CHECK_EQ(integer_objective_[0].first,
2265 mirror_lp_variable_.at(objective_cp_));
2266 CHECK_EQ(integer_objective_[0].second, IntegerValue(1));
2268 take_objective_into_account =
false;
2271 IntegerValue scaling = 0;
2272 tmp_integer_multipliers_ = ScaleLpMultiplier(
2273 take_objective_into_account,
2274 true, tmp_lp_multipliers_, &scaling);
2277 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2282 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2283 tmp_integer_multipliers_, &tmp_scattered_vector_, &rc_ub));
2285 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term =
2287 if (take_objective_into_account) {
2290 const IntegerValue obj_scale = scaling;
2294 tmp_coeffs_.clear();
2295 for (
const auto [
col, coeff] : integer_objective_) {
2296 tmp_cols_.push_back(
col);
2297 tmp_coeffs_.push_back(coeff);
2299 CHECK(tmp_scattered_vector_
2300 .AddLinearExpressionMultiple</*check_overflow=*/false>(
2301 obj_scale, tmp_cols_, tmp_coeffs_));
2302 CHECK(
AddProductTo(-obj_scale, integer_objective_offset_, &rc_ub));
2304 extra_term = {objective_cp_, -obj_scale};
2307 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2312 LinearConstraint explanation =
2317 if (explanation.num_terms == 0) {
2319 return explanation.ub >= 0;
2322 return PropagateLpConstraint(std::move(explanation));
2325bool LinearProgrammingConstraint::PropagateExactDualRay() {
2326 IntegerValue scaling;
2328 tmp_lp_multipliers_.clear();
2329 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2331 if (std::abs(
value) < kZeroTolerance)
continue;
2332 tmp_lp_multipliers_.push_back({
row,
value});
2334 tmp_integer_multipliers_ = ScaleLpMultiplier(
2336 true, tmp_lp_multipliers_, &scaling);
2338 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2342 IntegerValue new_constraint_ub;
2343 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2344 tmp_integer_multipliers_, &tmp_scattered_vector_, &new_constraint_ub))
2347 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2348 &new_constraint_ub);
2350 LinearConstraint explanation =
2355 if (VLOG_IS_ON(1)) {
2357 message = absl::StrCat(
"LP exact dual ray not infeasible,",
2358 " implied_lb: ", GetImpliedLowerBound(explanation),
2359 " ub: ", explanation.ub.value());
2363 if (!PropagateLpConstraint(std::move(explanation)))
return false;
2369int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2371 int num_non_basic_with_zero_rc = 0;
2372 for (glop::ColIndex
i(0);
i < num_vars; ++
i) {
2374 if (rc != 0.0)
continue;
2378 num_non_basic_with_zero_rc++;
2381 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2382 return num_non_basic_with_zero_rc;
2385void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2386 double cp_objective_delta) {
2387 deductions_.clear();
2392 const double lp_objective_delta =
2394 const int num_vars = integer_variables_.size();
2395 for (
int i = 0;
i < num_vars;
i++) {
2396 const IntegerVariable cp_var = integer_variables_[
i];
2397 const glop::ColIndex lp_var = glop::ColIndex(
i);
2401 if (rc == 0.0)
continue;
2402 const double lp_other_bound =
value + lp_objective_delta / rc;
2403 const double cp_other_bound =
2406 if (rc > kLpEpsilon) {
2408 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2413 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2416 }
else if (rc < -kLpEpsilon) {
2418 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2420 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2421 deductions_.push_back(
2428void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2429 const int num_vars = integer_variables_.size();
2430 if (sum_cost_down_.size() < num_vars) {
2431 sum_cost_down_.resize(num_vars, 0.0);
2432 num_cost_down_.resize(num_vars, 0);
2433 sum_cost_up_.resize(num_vars, 0.0);
2434 num_cost_up_.resize(num_vars, 0);
2435 rc_scores_.resize(num_vars, 0.0);
2439 num_calls_since_reduced_cost_averages_reset_++;
2440 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2441 for (
int i = 0;
i < num_vars;
i++) {
2442 sum_cost_up_[
i] /= 2;
2443 num_cost_up_[
i] /= 2;
2444 sum_cost_down_[
i] /= 2;
2445 num_cost_down_[
i] /= 2;
2447 num_calls_since_reduced_cost_averages_reset_ = 0;
2451 for (
int i = 0;
i < num_vars;
i++) {
2452 const IntegerVariable
var = integer_variables_[
i];
2458 const double rc = lp_reduced_cost_[
i];
2459 if (std::abs(rc) < kCpEpsilon)
continue;
2462 sum_cost_down_[
i] -= rc;
2463 num_cost_down_[
i]++;
2465 sum_cost_up_[
i] += rc;
2472 rc_rev_int_repository_.
SetLevel(0);
2478 positions_by_decreasing_rc_score_.clear();
2479 for (
int i = 0;
i < num_vars;
i++) {
2484 num_cost_up_[
i] > 0 ? sum_cost_up_[
i] / num_cost_up_[
i] : 0.0;
2485 const double a_down =
2486 num_cost_down_[
i] > 0 ? sum_cost_down_[
i] / num_cost_down_[
i] : 0.0;
2487 if (num_cost_down_[
i] > 0 && num_cost_up_[
i] > 0) {
2488 rc_scores_[
i] = std::min(a_up, a_down);
2490 rc_scores_[
i] = 0.5 * (a_down + a_up);
2495 if (rc_scores_[
i] > 0.0) {
2496 positions_by_decreasing_rc_score_.push_back({-rc_scores_[
i],
i});
2499 std::sort(positions_by_decreasing_rc_score_.begin(),
2500 positions_by_decreasing_rc_score_.end());
2503std::function<IntegerLiteral()>
2505 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2508IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2510 int selected_index = -1;
2511 const int size = positions_by_decreasing_rc_score_.size();
2512 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2513 for (
int i = rev_rc_start_;
i <
size; ++
i) {
2514 const int index = positions_by_decreasing_rc_score_[
i].second;
2515 const IntegerVariable
var = integer_variables_[
index];
2517 selected_index =
index;
2522 if (selected_index == -1)
return IntegerLiteral();
2523 const IntegerVariable
var = integer_variables_[selected_index];
2530 const IntegerValue value_ceil(
2532 if (value_ceil >= ub) {
2539 const IntegerValue value_floor(
2541 if (value_floor <= lb) {
2548 num_cost_up_[selected_index] > 0
2549 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2551 const double a_down =
2552 num_cost_down_[selected_index] > 0
2553 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2555 if (a_down < a_up) {
2562absl::Span<const glop::ColIndex> LinearProgrammingConstraint::IntegerLpRowCols(
2563 glop::RowIndex
row)
const {
2564 const int start = integer_lp_[
row].start_in_buffer;
2565 const size_t num_terms =
static_cast<size_t>(integer_lp_[
row].num_terms);
2566 return {integer_lp_cols_.data() +
start, num_terms};
2569absl::Span<const IntegerValue> LinearProgrammingConstraint::IntegerLpRowCoeffs(
2570 glop::RowIndex
row)
const {
2571 const int start = integer_lp_[
row].start_in_buffer;
2572 const size_t num_terms =
static_cast<size_t>(integer_lp_[
row].num_terms);
2573 return {integer_lp_coeffs_.data() +
start, num_terms};
static int64_t GCD64(int64_t x, int64_t y)
void SetLevel(int level) final
void SaveState(T *object)
std::string GetDimensionString() const
A short string with the problem dimension.
void Clear()
Clears, i.e. reset the object to its initial value.
void NotifyThatColumnsAreClean()
void SetObjectiveOffset(Fractional objective_offset)
void SetObjectiveCoefficient(ColIndex col, Fractional value)
const DenseColumn & constraint_lower_bounds() const
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Defines the coefficient for col / row.
const DenseColumn & constraint_upper_bounds() const
RowIndex CreateNewConstraint()
const SparseColumn & GetSparseColumn(ColIndex col) const
Fractional objective_scaling_factor() const
ColIndex CreateNewVariable()
RowIndex num_constraints() const
Returns the number of constraints.
Fractional UnscaleDualValue(RowIndex row, Fractional value) const
Fractional UnscaleConstraintActivity(RowIndex row, Fractional value) const
void Scale(LinearProgram *lp)
Scale the given LP.
Fractional UnscaleReducedCost(ColIndex col, Fractional value) const
Fractional VariableScalingFactor(ColIndex col) const
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Transforms corresponding value from the scaled domain to the original one.
Fractional GetVariableValue(ColIndex col) const
const DenseColumn & GetDualRay() const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
ColIndex GetBasis(RowIndex row) const
ProblemStatus GetProblemStatus() const
ConstraintStatus GetConstraintStatus(RowIndex row) const
void NotifyThatMatrixIsUnchangedForNextSolve()
Fractional GetObjectiveValue() const
const BasisState & GetState() const
void LoadStateForNextSolve(const BasisState &state)
Uses the given state as a warm-start for the next Solve() call.
Fractional GetDualValue(RowIndex row) const
void ClearIntegralityScales()
Fractional GetReducedCost(ColIndex col) const
ColIndex GetProblemNumCols() const
void ClearStateForNextSolve()
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
Fractional GetConstraintActivity(RowIndex row) const
const DenseRow & GetDualRayRowCombination() const
This is the "dual ray" linear combination of the matrix rows.
RowIndex GetProblemNumRows() const
Getters to retrieve all the information computed by the last Solve().
VariableStatus GetVariableStatus(ColIndex col) const
void SetParameters(const GlopParameters ¶meters)
Sets or gets the algorithm parameters to be used on the next Solve().
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
int64_t GetNumberOfIterations() const
void NotifyThatMatrixIsChangedForNextSolve()
EntryIndex num_entries() const
Note this method can only be used when the vector has no duplicates.
bool TrySimpleSeparation(const CutData &input_ct)
Tries RLT separation of the input constraint. Returns true on success.
void Initialize(const absl::flat_hash_map< IntegerVariable, glop::ColIndex > &lp_vars)
std::string Info() const
Single line of text that we append to the cut log line.
const CutData & cut() const
If successful, this contains the last generated cut.
const CutData & cut() const
If successful, info about the last generated cut.
bool TrySimpleKnapsack(const CutData &input_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
std::string Info() const
Single line of text that we append to the cut log line.
bool TryWithLetchfordSouliLifting(const CutData &input_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
bool TrySingleNodeFlow(const CutData &input_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
int64_t LastValue() const
bool MightBeReachable(int64_t sum) const
void Add(const int64_t positive_value)
void RegisterReversibleInt(int id, int *rev)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
void AlwaysCallAtLevelZero(int id)
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const util_intops::StrongVector< IntegerVariable, double > &lp_values)
void AddData(double new_record)
double CurrentAverage() const
bool ComputeCut(RoundingOptions options, const CutData &base_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
Returns true on success. The cut can be accessed via cut().
const CutData & cut() const
If successful, info about the last generated cut.
std::string Info() const
Single line of text that we append to the cut log line.
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
int64_t num_enqueues() const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
bool IsFixedAtLevelZero(IntegerVariable var) const
Returns true if the variable is fixed at level 0.
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
void RegisterReversibleClass(ReversibleInterface *rev)
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
bool IsFixed(IntegerVariable i) const
Checks if the variable is fixed.
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
int64_t num_constraints() const
Stats.
const std::vector< ConstraintIndex > & LpConstraints() const
bool ChangeLp(glop::BasisState *solution_state, int *num_new_constraints=nullptr)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
bool AddCut(LinearConstraint ct, std::string type_name, std::string extra_info="")
bool UpdateConstraintUb(glop::RowIndex index_in_lp, IntegerValue new_ub)
bool DebugCheckConstraint(const LinearConstraint &cut)
bool UpdateConstraintLb(glop::RowIndex index_in_lp, IntegerValue new_lb)
These must be level zero bounds.
const util_intops::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
All the constraints managed by this class.
void AddAllConstraintsToLp()
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
A class that stores the collection of all LP constraints in a model.
double GetSolutionReducedCost(IntegerVariable variable) const
void RegisterWith(Model *model)
void SetLevel(int level) override
ReversibleInterface API.
void AddCutGenerator(CutGenerator generator)
Register a new cut generator with this constraint.
void AddLinearConstraint(LinearConstraint ct)
Add a new linear constraint to this LP.
bool Propagate() override
PropagatorInterface API.
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
glop::RowIndex ConstraintIndex
std::function< IntegerLiteral()> HeuristicLpReducedCostAverageBranching()
bool IncrementalPropagate(const std::vector< int > &watch_indices) override
double GetSolutionValue(IntegerVariable variable) const
LinearProgrammingConstraint(Model *model, absl::Span< const IntegerVariable > vars)
const std::string & Name() const
bool AddLinearExpressionMultiple(IntegerValue multiplier, absl::Span< const glop::ColIndex > cols, absl::Span< const IntegerValue > coeffs)
bool Add(glop::ColIndex col, IntegerValue value)
Does vector[col] += value and return false in case of overflow.
LinearConstraint ConvertToLinearConstraint(absl::Span< const IntegerVariable > integer_variables, IntegerValue upper_bound, std::optional< std::pair< IntegerVariable, IntegerValue > > extra_term=std::nullopt)
void ConvertToCutData(absl::int128 rhs, absl::Span< const IntegerVariable > integer_variables, absl::Span< const double > lp_solution, IntegerTrail *integer_trail, CutData *result)
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
Similar to ConvertToLinearConstraint().
void ClearAndResize(int size)
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Updates the inner objective bounds.
Simple class to add statistics by name and print them at the end.
void TransferToManager(LinearConstraintManager *manager)
Empty the local pool and add all its content to the manager.
void AddCut(LinearConstraint ct, absl::string_view name, const util_intops::StrongVector< IntegerVariable, double > &lp_solution)
Adds a cut to the local pool.
std::vector< Literal > * MutableConflict()
int CurrentDecisionLevel() const
void AddOneConstraint(glop::RowIndex, absl::Span< const glop::ColIndex > cols, absl::Span< const IntegerValue > coeffs, IntegerValue lb, IntegerValue ub)
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
void ProcessVariables(const std::vector< double > &lp_values, absl::Span< const IntegerValue > lower_bounds, absl::Span< const IntegerValue > upper_bounds)
void assign(size_type n, const value_type &val)
void push_back(const value_type &val)
void resize(size_type new_size)
const std::string name
A name for logging purposes.
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
Computes result += a * b, and return false iff there is an overflow.
void DivideByGCD(LinearConstraint *constraint)
IntType IntTypeAbs(IntType t)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
IntegerVariable PositiveVariable(IntegerVariable i)
int WeightedPick(absl::Span< const double > input, absl::BitGenRef random)
bool VariableIsPositive(IntegerVariable i)
LinearConstraintPropagator< true > IntegerSumLE128
double ToDouble(IntegerValue value)
In SWIG mode, we don't want anything besides these top-level includes.
bool AtMinOrMaxInt64(int64_t x)
Checks if x is equal to the min or the max value of an int64_t.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
Point BinarySearch(Point x_true, Point x_false, std::function< bool(Point)> f)
const std::optional< Range > & range
Our cut are always of the form linear_expression <= rhs.
bool FillFromLinearConstraint(const LinearConstraint &base_ct, const util_intops::StrongVector< IntegerVariable, double > &lp_values, IntegerTrail *integer_trail)
bool AllCoefficientsArePositive() const
These functions transform the cut by complementation.
std::vector< CutTerm > terms
void ComplementForPositiveCoefficients()
bool AppendOneTerm(IntegerVariable var, IntegerValue coeff, double lp_value, IntegerValue lb, IntegerValue ub)
void ComplementForSmallerLpValues()
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::unique_ptr< IntegerValue[]> coeffs
std::unique_ptr< IntegerVariable[]> vars
IntegerVariable objective_var
double ScaleObjective(double value) const