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"
38 #include "ortools/glop/parameters.pb.h"
54 #include "ortools/sat/sat_parameters.pb.h"
73 for (
const glop::ColIndex
col : non_zeros_) {
74 dense_vector_[
col] = IntegerValue(0);
76 dense_vector_.
resize(size, IntegerValue(0));
78 dense_vector_.
assign(size, IntegerValue(0));
80 for (
const glop::ColIndex
col : non_zeros_) {
81 is_zeros_[
col] =
true;
83 is_zeros_.
resize(size,
true);
89 const int64_t add =
CapAdd(
value.value(), dense_vector_[
col].value());
93 dense_vector_[
col] = IntegerValue(add);
94 if (is_sparse_ && is_zeros_[
col]) {
95 is_zeros_[
col] =
false;
96 non_zeros_.push_back(
col);
102 IntegerValue multiplier,
103 const std::vector<std::pair<glop::ColIndex, IntegerValue>>& terms) {
104 const double threshold = 0.1 *
static_cast<double>(dense_vector_.
size());
105 if (is_sparse_ &&
static_cast<double>(terms.size()) < threshold) {
106 for (
const std::pair<glop::ColIndex, IntegerValue>& term : terms) {
107 if (is_zeros_[term.first]) {
108 is_zeros_[term.first] =
false;
109 non_zeros_.push_back(term.first);
111 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
115 if (
static_cast<double>(non_zeros_.size()) > threshold) {
120 for (
const std::pair<glop::ColIndex, IntegerValue>& term : terms) {
121 if (!
AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
130 const std::vector<IntegerVariable>& integer_variables,
132 result->
vars.clear();
135 std::sort(non_zeros_.begin(), non_zeros_.end());
136 for (
const glop::ColIndex
col : non_zeros_) {
137 const IntegerValue coeff = dense_vector_[
col];
138 if (coeff == 0)
continue;
139 result->
vars.push_back(integer_variables[
col.value()]);
140 result->
coeffs.push_back(coeff);
143 const int size = dense_vector_.
size();
144 for (glop::ColIndex
col(0);
col < size; ++
col) {
145 const IntegerValue coeff = dense_vector_[
col];
146 if (coeff == 0)
continue;
147 result->
vars.push_back(integer_variables[
col.value()]);
148 result->
coeffs.push_back(coeff);
155 std::vector<std::pair<glop::ColIndex, IntegerValue>>
157 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
159 std::sort(non_zeros_.begin(), non_zeros_.end());
160 for (
const glop::ColIndex
col : non_zeros_) {
161 const IntegerValue coeff = dense_vector_[
col];
165 const int size = dense_vector_.
size();
166 for (glop::ColIndex
col(0);
col < size; ++
col) {
167 const IntegerValue coeff = dense_vector_[
col];
177 Model*
model, absl::Span<const IntegerVariable> vars)
178 : constraint_manager_(
model),
179 parameters_(*(
model->GetOrCreate<SatParameters>())),
187 implied_bounds_processor_({}, integer_trail_,
190 expanded_lp_solution_(
193 simplex_params_.set_use_dual_simplex(
true);
194 simplex_params_.set_cost_scaling(glop::GlopParameters::MEAN_COST_SCALING);
195 if (parameters_.use_exact_lp_reason()) {
196 simplex_params_.set_change_status_to_imprecise(
false);
197 simplex_params_.set_primal_feasibility_tolerance(1e-7);
198 simplex_params_.set_dual_feasibility_tolerance(1e-7);
200 simplex_.SetParameters(simplex_params_);
201 if (parameters_.use_branching_in_lp() ||
202 parameters_.search_branching() == SatParameters::LP_SEARCH) {
203 compute_reduced_cost_averages_ =
true;
207 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
209 integer_rounding_cut_helper_.SetSharedStatistics(
210 model->GetOrCreate<SharedStatistics>());
211 cover_cut_helper_.SetSharedStatistics(
model->GetOrCreate<SharedStatistics>());
214 CHECK(std::is_sorted(vars.begin(), vars.end()));
216 integer_variables_.assign(vars.begin(), vars.end());
218 for (
const IntegerVariable positive_variable : vars) {
220 implied_bounds_processor_.AddLpVariable(positive_variable);
221 (*dispatcher_)[positive_variable] =
this;
222 mirror_lp_variable_[positive_variable] =
col;
226 lp_solution_.assign(vars.size(), std::numeric_limits<double>::infinity());
227 lp_reduced_cost_.assign(vars.size(), 0.0);
230 const int max_index =
NegationOf(vars.back()).value();
231 if (max_index >= expanded_lp_solution_.size()) {
232 expanded_lp_solution_.assign(max_index + 1, 0.0);
239 DCHECK(!lp_constraint_is_registered_);
240 constraint_manager_.
Add(
ct);
243 glop::ColIndex LinearProgrammingConstraint::GetMirrorVariable(
244 IntegerVariable positive_variable) {
246 return mirror_lp_variable_.at(positive_variable);
250 IntegerValue coeff) {
251 CHECK(!lp_constraint_is_registered_);
252 objective_is_defined_ =
true;
254 if (ivar != pos_var) coeff = -coeff;
257 const glop::ColIndex
col = GetMirrorVariable(pos_var);
258 integer_objective_.push_back({
col, coeff});
259 objective_infinity_norm_ =
276 bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
281 infinity_norms_.
clear();
282 const auto& all_constraints = constraint_manager_.
AllConstraints();
286 integer_lp_.
push_back(LinearConstraintInternal());
287 LinearConstraintInternal& new_ct = integer_lp_.
back();
290 const int size =
ct.vars.size();
291 IntegerValue infinity_norm(0);
293 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
302 new_ct.terms.reserve(size);
303 for (
int i = 0; i < size; ++i) {
305 const IntegerVariable
var =
ct.vars[i];
306 const IntegerValue coeff =
ct.coeffs[i];
308 new_ct.terms.push_back({GetMirrorVariable(
var), coeff});
310 infinity_norms_.
push_back(infinity_norm);
313 DCHECK(std::is_sorted(new_ct.terms.begin(), new_ct.terms.end()));
318 for (
int i = 0; i < integer_variables_.size(); ++i) {
325 objective_infinity_norm_ = 0;
326 for (
const auto& entry : integer_objective_) {
327 const IntegerVariable
var = integer_variables_[entry.first.value()];
329 integer_objective_offset_ +=
333 objective_infinity_norm_ =
335 integer_objective_[new_size++] = entry;
338 objective_infinity_norm_ =
340 integer_objective_.resize(new_size);
343 for (
const LinearConstraintInternal&
ct : integer_lp_) {
346 for (
const auto& term :
ct.terms) {
358 const int num_vars = integer_variables_.size();
359 for (
int i = 0; i < num_vars; i++) {
360 const IntegerVariable cp_var = integer_variables_[i];
368 scaler_.
Scale(simplex_params_, &lp_data_);
369 UpdateBoundsOfLpVariables();
374 if (parameters_.polish_lp_solution()) {
376 for (
int i = 0; i < num_vars; ++i) {
377 const IntegerVariable cp_var = integer_variables_[i];
380 if (lb != 0 || ub != 1)
continue;
390 <<
" Managed constraints.";
394 LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
396 glop::BasisState basis_state = simplex_.
GetState();
398 const glop::Status
status = simplex_.
Solve(lp_data_, time_limit_);
402 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
411 info.new_obj_bound = IntegerValue(
412 static_cast<int64_t
>(std::ceil(info.lp_objective - kCpEpsilon)));
417 void LinearProgrammingConstraint::FillReducedCostReasonIn(
419 std::vector<IntegerLiteral>* integer_reason) {
420 integer_reason->clear();
421 const int num_vars = integer_variables_.size();
422 for (
int i = 0; i < num_vars; i++) {
423 const double rc = reduced_costs[glop::ColIndex(i)];
424 if (rc > kLpEpsilon) {
425 integer_reason->push_back(
427 }
else if (rc < -kLpEpsilon) {
428 integer_reason->push_back(
436 bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
438 DCHECK(lp_solution_is_set_);
440 DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
443 integer_reason_.clear();
445 bool deductions_were_made =
false;
447 UpdateBoundsOfLpVariables();
449 const IntegerValue current_obj_lb = integer_trail_->
LowerBound(objective_cp_);
453 const glop::ColIndex lp_var = GetMirrorVariable(positive_var);
457 if (current_value < current_lb || current_value > current_ub) {
462 const double new_ub = std::floor(current_value);
465 LPSolveInfo lower_branch_info = SolveLpForBranching();
475 positive_var, IntegerValue(std::ceil(current_value)));
476 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
479 deductions_were_made =
true;
480 }
else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
485 const double new_lb = std::ceil(current_value);
488 LPSolveInfo upper_branch_info = SolveLpForBranching();
492 return deductions_were_made;
499 positive_var, IntegerValue(std::floor(current_value)));
500 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
501 return deductions_were_made;
503 deductions_were_made =
true;
505 }
else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
506 return deductions_were_made;
515 approximate_obj_lb = upper_branch_info.new_obj_bound;
517 approximate_obj_lb = lower_branch_info.new_obj_bound;
519 approximate_obj_lb =
std::min(lower_branch_info.new_obj_bound,
520 upper_branch_info.new_obj_bound);
525 if (approximate_obj_lb <= current_obj_lb)
return deductions_were_made;
528 const IntegerLiteral deduction =
530 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
531 return deductions_were_made;
538 DCHECK(!lp_constraint_is_registered_);
539 lp_constraint_is_registered_ =
true;
544 std::sort(integer_objective_.begin(), integer_objective_.end());
547 if (!parameters_.add_lp_constraints_lazily()) {
550 if (!CreateLpFromConstraintManager()) {
556 const int watcher_id = watcher->
Register(
this);
557 const int num_vars = integer_variables_.size();
558 for (
int i = 0; i < num_vars; i++) {
561 if (objective_is_defined_) {
574 optimal_constraints_.resize(rev_optimal_constraints_size_);
575 if (lp_solution_is_set_ && level < lp_solution_level_) {
576 lp_solution_is_set_ =
false;
584 if (level == 0 && !level_zero_lp_solution_.empty()) {
585 lp_solution_is_set_ =
true;
586 lp_solution_ = level_zero_lp_solution_;
587 lp_solution_level_ = 0;
588 for (
int i = 0; i < lp_solution_.size(); i++) {
589 expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
590 expanded_lp_solution_[
NegationOf(integer_variables_[i])] =
597 cut_generators_.push_back(std::move(generator));
601 const std::vector<int>& watch_indices) {
602 if (!lp_solution_is_set_) {
614 for (
const int index : watch_indices) {
620 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
635 glop::ColIndex
var) {
640 IntegerVariable variable)
const {
641 return lp_solution_[mirror_lp_variable_.at(variable).value()];
645 IntegerVariable variable)
const {
646 return lp_reduced_cost_[mirror_lp_variable_.at(variable).value()];
649 void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
650 const int num_vars = integer_variables_.size();
651 for (
int i = 0; i < num_vars; i++) {
652 const IntegerVariable cp_var = integer_variables_[i];
660 bool LinearProgrammingConstraint::SolveLp() {
662 lp_at_level_zero_is_final_ =
false;
665 const auto status = simplex_.
Solve(lp_data_, time_limit_);
668 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
672 average_degeneracy_.
AddData(CalculateDegeneracy());
674 VLOG(2) <<
"High average degeneracy: "
683 if (status_as_int >= num_solves_by_status_.size()) {
684 num_solves_by_status_.resize(status_as_int + 1);
687 num_solves_by_status_[status_as_int]++;
694 lp_solution_is_set_ =
true;
696 const int num_vars = integer_variables_.size();
697 for (
int i = 0; i < num_vars; i++) {
699 GetVariableValueAtCpScale(glop::ColIndex(i));
700 lp_solution_[i] =
value;
701 expanded_lp_solution_[integer_variables_[i]] =
value;
705 if (lp_solution_level_ == 0) {
706 level_zero_lp_solution_ = lp_solution_;
712 bool LinearProgrammingConstraint::AnalyzeLp() {
715 if (parameters_.use_exact_lp_reason()) {
716 if (!FillExactDualRayReason())
return true;
725 UpdateSimplexIterationLimit(10, 1000);
728 if (objective_is_defined_ &&
733 if (parameters_.use_exact_lp_reason()) {
734 if (!ExactLpReasonning())
return false;
739 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
740 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
741 const IntegerValue propagated_lb =
743 if (approximate_new_lb > propagated_lb) {
744 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
746 <<
" ] approx_lb += "
747 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
748 << integer_trail_->
UpperBound(objective_cp_) - propagated_lb;
756 const double objective_cp_ub =
759 ReducedCostStrengtheningDeductions(objective_cp_ub -
760 relaxed_optimal_objective);
761 if (!deductions_.empty()) {
762 deductions_reason_ = integer_reason_;
763 deductions_reason_.push_back(
768 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
769 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
770 if (approximate_new_lb > integer_trail_->
LowerBound(objective_cp_)) {
771 const IntegerLiteral deduction =
773 if (!integer_trail_->
Enqueue(deduction, {}, integer_reason_)) {
779 if (!deductions_.empty()) {
780 const int trail_index_with_same_reason = integer_trail_->
Index();
781 for (
const IntegerLiteral deduction : deductions_) {
782 if (!integer_trail_->
Enqueue(deduction, {}, deductions_reason_,
783 trail_index_with_same_reason)) {
793 CHECK(lp_solution_is_set_);
796 lp_solution_is_integer_ =
true;
797 const int num_vars = integer_variables_.size();
798 for (
int i = 0; i < num_vars; i++) {
801 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
803 lp_solution_is_integer_ =
false;
807 if (compute_reduced_cost_averages_) {
808 UpdateAverageReducedCosts();
816 bool LinearProgrammingConstraint::RemoveFixedTerms(LinearConstraint* cut) {
818 const int num_terms =
static_cast<int>(cut->vars.size());
819 for (
int i = 0; i < num_terms; ++i) {
820 const IntegerVariable
var = cut->vars[i];
821 const IntegerValue coeff = cut->coeffs[i];
828 cut->vars[new_size] =
var;
829 cut->coeffs[new_size] = coeff;
832 cut->vars.resize(new_size);
833 cut->coeffs.resize(new_size);
845 bool LinearProgrammingConstraint::PreprocessCut(LinearConstraint* cut) {
849 bool min_sum_overflow =
false;
850 IntegerValue min_sum(0);
851 IntegerValue max_range(0);
852 bool has_fixed_term =
false;
853 const int num_terms =
static_cast<int>(cut->vars.size());
854 if (num_terms == 0)
return false;
855 for (
int i = 0; i < num_terms; ++i) {
856 const IntegerVariable
var = cut->vars[i];
857 const IntegerValue magnitude = cut->coeffs[i];
858 CHECK_GT(magnitude, 0);
862 has_fixed_term =
true;
868 IntegerValue(
CapProd(magnitude.value(), (ub - lb).value())));
869 if (!
AddProductTo(magnitude, lb, &min_sum)) min_sum_overflow =
true;
872 if (has_fixed_term) {
873 if (!RemoveFixedTerms(cut))
return false;
877 const IntegerValue slack{
CapSub(cut->ub.value(), min_sum.value())};
880 if (slack < 0)
return false;
883 bool newly_fixed =
false;
884 for (
int i = 0; i < num_terms; ++i) {
885 const IntegerVariable
var = cut_.
vars[i];
886 const IntegerValue magnitude = cut_.
coeffs[i];
889 if (
CapProd(magnitude.value(), (ub - lb).value()) > slack) {
891 ++total_num_cut_propagations_;
892 const IntegerValue new_diff = slack / magnitude;
901 if (!RemoveFixedTerms(cut))
return false;
902 if (cut->vars.empty())
return false;
911 bool LinearProgrammingConstraint::AddCutFromConstraints(
912 const std::string&
name,
913 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
924 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
926 VLOG(1) <<
"Issue, overflow!";
945 if (std::abs(activity -
ToDouble(cut_.
ub)) / norm > 1e-4) {
946 VLOG(1) <<
"Cut not tight " << activity <<
" <= " <<
ToDouble(cut_.
ub);
955 if (!PreprocessCut(&cut_))
return false;
956 CHECK(!cut_.
vars.empty());
958 bool at_least_one_added =
false;
965 cut_, expanded_lp_solution_, integer_trail_,
966 &implied_bounds_processor_)) {
967 at_least_one_added |= constraint_manager_.
AddCut(
968 flow_cover_cut_helper_.
cut(), absl::StrCat(
name,
"_F"),
969 expanded_lp_solution_, flow_cover_cut_helper_.
Info());
983 bool some_ints =
false;
984 bool some_relevant_positions =
false;
985 for (
const CutTerm& term : base_ct_.
terms) {
986 if (term.bound_diff > 1) some_ints =
true;
987 if (term.HasRelevantLpValue()) some_relevant_positions =
true;
991 if (!some_relevant_positions)
return false;
993 ImpliedBoundsProcessor* ib_processor =
994 some_ints ? &implied_bounds_processor_ :
nullptr;
997 const IntegerVariable first_slack(expanded_lp_solution_.
size());
998 CHECK_EQ(first_slack.value() % 2, 0);
999 tmp_slack_rows_.clear();
1000 for (
const auto& pair : integer_multipliers) {
1001 const RowIndex
row = pair.first;
1002 const IntegerValue coeff = pair.second;
1008 entry.lp_value = 0.0;
1010 CapSub(integer_lp_[
row].ub.value(), integer_lp_[
row].lb.value());
1011 entry.expr_vars[0] =
1012 first_slack + 2 * IntegerVariable(tmp_slack_rows_.size());
1013 entry.expr_coeffs[1] = 0;
1016 entry.expr_coeffs[0] = IntegerValue(-1);
1017 entry.expr_offset = integer_lp_[
row].ub;
1020 entry.expr_coeffs[0] = IntegerValue(1);
1021 entry.expr_offset = -integer_lp_[
row].lb;
1024 base_ct_.
terms.push_back(entry);
1025 tmp_slack_rows_.push_back(
row);
1029 RoundingOptions options;
1030 options.max_scaling = parameters_.max_integer_rounding_scaling();
1032 options.use_ib_before_heuristic =
false;
1033 if (integer_rounding_cut_helper_.
ComputeCut(options, base_ct_,
1035 at_least_one_added |= PostprocessAndAddCut(
1036 absl::StrCat(
name,
"_R"), integer_rounding_cut_helper_.
Info(),
1037 first_slack, integer_rounding_cut_helper_.
cut());
1040 options.use_ib_before_heuristic =
true;
1041 options.prefer_positive_ib =
false;
1042 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.
ComputeCut(
1043 options, base_ct_, ib_processor)) {
1044 at_least_one_added |= PostprocessAndAddCut(
1045 absl::StrCat(
name,
"_RB"), integer_rounding_cut_helper_.
Info(),
1046 first_slack, integer_rounding_cut_helper_.
cut());
1049 options.use_ib_before_heuristic =
true;
1050 options.prefer_positive_ib =
true;
1051 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.
ComputeCut(
1052 options, base_ct_, ib_processor)) {
1053 at_least_one_added |= PostprocessAndAddCut(
1054 absl::StrCat(
name,
"_RBP"), integer_rounding_cut_helper_.
Info(),
1055 first_slack, integer_rounding_cut_helper_.
cut());
1061 at_least_one_added |= PostprocessAndAddCut(
1062 absl::StrCat(
name,
"_KB"), cover_cut_helper_.
Info(), first_slack,
1063 cover_cut_helper_.
cut());
1067 at_least_one_added |= PostprocessAndAddCut(
1068 absl::StrCat(
name,
"_KL"), cover_cut_helper_.
Info(), first_slack,
1069 cover_cut_helper_.
cut());
1073 return at_least_one_added;
1076 bool LinearProgrammingConstraint::PostprocessAndAddCut(
1077 const std::string&
name,
const std::string& info,
1078 IntegerVariable first_slack,
const LinearConstraint& cut) {
1081 IntegerValue cut_ub = cut.ub;
1082 bool overflow =
false;
1083 for (
int i = 0; i < cut.vars.size(); ++i) {
1084 const IntegerVariable
var = cut.vars[i];
1087 if (
var < first_slack) {
1090 tmp_scattered_vector_.
Add(
col, cut.coeffs[i]);
1092 tmp_scattered_vector_.
Add(
col, -cut.coeffs[i]);
1098 const int slack_index = (
var.value() - first_slack.value()) / 2;
1099 const glop::RowIndex
row = tmp_slack_rows_[slack_index];
1100 const IntegerValue multiplier = cut.coeffs[i];
1102 multiplier, integer_lp_[
row].terms)) {
1109 VLOG(1) <<
"Overflow in slack removal.";
1116 return constraint_manager_.
AddCut(cut_,
name, expanded_lp_solution_, info);
1123 void LinearProgrammingConstraint::AddCGCuts() {
1125 for (RowIndex
row(0);
row < num_rows; ++
row) {
1127 const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
1135 if (std::abs(lp_value - std::round(lp_value)) < 0.01)
continue;
1139 if (basis_col >= integer_variables_.size())
continue;
1144 double magnitude = 0.0;
1145 tmp_lp_multipliers_.clear();
1147 if (lambda.non_zeros.empty()) {
1148 for (RowIndex
row(0);
row < num_rows; ++
row) {
1150 if (std::abs(
value) < kZeroTolerance)
continue;
1156 VLOG(1) <<
"BASIC row not expected! " <<
value;
1161 tmp_lp_multipliers_.push_back({
row,
value});
1164 for (
const ColIndex
col : lambda.non_zeros) {
1166 const double value = lambda.values[
col];
1167 if (std::abs(
value) < kZeroTolerance)
continue;
1171 VLOG(1) <<
"BASIC row not expected! " <<
value;
1176 tmp_lp_multipliers_.push_back({
row,
value});
1179 if (tmp_lp_multipliers_.empty())
continue;
1182 for (
int i = 0; i < 2; ++i) {
1188 for (std::pair<RowIndex, double>& p : tmp_lp_multipliers_) {
1189 p.second = -p.second;
1195 tmp_integer_multipliers_ =
1196 ScaleLpMultiplier(
false,
1197 tmp_lp_multipliers_, &scaling, 52);
1198 AddCutFromConstraints(
"CG", tmp_integer_multipliers_);
1205 template <
class ListOfTerms>
1206 IntegerValue GetCoeff(ColIndex
col,
const ListOfTerms& terms) {
1207 for (
const auto& term : terms) {
1208 if (term.first ==
col)
return term.second;
1210 return IntegerValue(0);
1224 void LinearProgrammingConstraint::AddObjectiveCut() {
1225 if (integer_objective_.size() <= 1)
return;
1231 const IntegerValue obj_lower_bound =
1233 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1236 LinearConstraint objective_ct;
1238 objective_ct.ub = integer_objective_offset_ -
1240 IntegerValue obj_coeff_magnitude(0);
1241 for (
const auto& [
col, coeff] : integer_objective_) {
1242 const IntegerVariable
var = integer_variables_[
col.value()];
1243 objective_ct.vars.push_back(
var);
1244 objective_ct.coeffs.push_back(-coeff);
1251 if (obj_coeff_magnitude < 1e9 &&
1252 constraint_manager_.
AddCut(objective_ct,
"Objective",
1253 expanded_lp_solution_)) {
1264 constraint_manager_.
AddCut(cover_cut_helper_.
cut(),
"Objective_K",
1265 expanded_lp_solution_);
1269 RoundingOptions options;
1270 options.max_scaling = parameters_.max_integer_rounding_scaling();
1271 if (integer_rounding_cut_helper_.
ComputeCut(options, base_ct_,
1272 &implied_bounds_processor_)) {
1273 constraint_manager_.
AddCut(integer_rounding_cut_helper_.
cut(),
1274 "Objective_R", expanded_lp_solution_);
1278 void LinearProgrammingConstraint::AddMirCuts() {
1294 integer_variables_.size(), IntegerValue(0));
1295 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1300 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1304 for (RowIndex
row(0);
row < num_rows; ++
row) {
1315 base_rows.push_back({
row, IntegerValue(1)});
1321 base_rows.push_back({
row, IntegerValue(-1)});
1342 std::vector<double> weights;
1344 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1345 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1355 integer_multipliers = {entry};
1356 if (AddCutFromConstraints(
"MIR_1", integer_multipliers)) {
1361 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1362 dense_cut[
col] = IntegerValue(0);
1364 non_zeros_.SparseClearAll();
1367 const IntegerValue multiplier = entry.second;
1368 for (
const std::pair<ColIndex, IntegerValue>& term :
1369 integer_lp_[entry.first].terms) {
1370 const ColIndex
col = term.first;
1371 const IntegerValue coeff = term.second;
1372 non_zeros_.Set(
col);
1373 dense_cut[
col] += coeff * multiplier;
1376 used_rows.
assign(num_rows,
false);
1377 used_rows[entry.first] =
true;
1382 const int kMaxAggregation = 5;
1383 for (
int i = 0; i < kMaxAggregation; ++i) {
1386 IntegerValue max_magnitude(0);
1388 std::vector<ColIndex> col_candidates;
1389 for (
const ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1390 if (dense_cut[
col] == 0)
continue;
1393 const int col_degree =
1395 if (col_degree <= 1)
continue;
1400 const IntegerVariable
var = integer_variables_[
col.value()];
1401 const double lp_value = expanded_lp_solution_[
var];
1404 const double bound_distance =
std::min(ub - lp_value, lp_value - lb);
1405 if (bound_distance > 1e-2) {
1406 weights.push_back(bound_distance);
1407 col_candidates.push_back(
col);
1410 if (col_candidates.empty())
break;
1412 const ColIndex var_to_eliminate =
1413 col_candidates[std::discrete_distribution<>(weights.begin(),
1414 weights.end())(*random_)];
1417 std::vector<RowIndex> possible_rows;
1420 const RowIndex
row = entry.row();
1425 if (used_rows[
row])
continue;
1426 used_rows[
row] =
true;
1429 bool add_row =
false;
1431 if (entry.coefficient() > 0.0) {
1432 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1434 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1438 if (entry.coefficient() > 0.0) {
1439 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1441 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1446 weights.push_back(row_weights[
row]);
1449 if (possible_rows.empty())
break;
1451 const RowIndex row_to_combine =
1452 possible_rows[std::discrete_distribution<>(weights.begin(),
1453 weights.end())(*random_)];
1454 const IntegerValue to_combine_coeff =
1455 GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1456 CHECK_NE(to_combine_coeff, 0);
1458 IntegerValue mult1 = -to_combine_coeff;
1459 IntegerValue mult2 = dense_cut[var_to_eliminate];
1466 const IntegerValue gcd = IntegerValue(
1476 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1479 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
1481 std::abs(mult2.value()))) ==
1486 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1487 entry.second *= mult1;
1489 integer_multipliers.push_back({row_to_combine, mult2});
1492 if (AddCutFromConstraints(absl::StrCat(
"MIR_", i + 2),
1493 integer_multipliers)) {
1499 if (i + 1 == kMaxAggregation)
break;
1501 for (ColIndex
col : non_zeros_.PositionsSetAtLeastOnce()) {
1502 dense_cut[
col] *= mult1;
1504 for (
const std::pair<ColIndex, IntegerValue>& term :
1505 integer_lp_[row_to_combine].terms) {
1506 const ColIndex
col = term.first;
1507 const IntegerValue coeff = term.second;
1508 non_zeros_.Set(
col);
1509 dense_cut[
col] += coeff * mult2;
1515 void LinearProgrammingConstraint::AddZeroHalfCuts() {
1518 tmp_lp_values_.clear();
1519 tmp_var_lbs_.clear();
1520 tmp_var_ubs_.clear();
1521 for (
const IntegerVariable
var : integer_variables_) {
1522 tmp_lp_values_.push_back(expanded_lp_solution_[
var]);
1530 for (glop::RowIndex
row(0);
row < integer_lp_.size(); ++
row) {
1538 row, integer_lp_[
row].terms, integer_lp_[
row].lb, integer_lp_[
row].ub);
1540 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1548 AddCutFromConstraints(
"ZERO_HALF", multipliers);
1552 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1553 const int64_t min_iter,
const int64_t max_iter) {
1554 if (parameters_.linearization_level() < 2)
return;
1555 const int64_t num_degenerate_columns = CalculateDegeneracy();
1557 if (num_cols <= 0) {
1560 CHECK_GT(num_cols, 0);
1561 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
1566 if (is_degenerate_) {
1567 next_simplex_iter_ /=
std::max(int64_t{1}, decrease_factor);
1569 next_simplex_iter_ *= 2;
1572 if (is_degenerate_) {
1573 next_simplex_iter_ /=
std::max(int64_t{1}, 2 * decrease_factor);
1577 next_simplex_iter_ = num_cols / 40;
1580 next_simplex_iter_ =
1585 UpdateBoundsOfLpVariables();
1589 if ( (
false) && objective_is_defined_) {
1597 simplex_params_.set_objective_upper_limit(
1598 static_cast<double>(integer_trail_->
UpperBound(objective_cp_).value() +
1599 100.0 * kCpEpsilon));
1606 simplex_params_.set_max_number_of_iterations(
1607 parameters_.root_lp_iterations());
1609 simplex_params_.set_max_number_of_iterations(next_simplex_iter_);
1613 if (!SolveLp())
return true;
1614 if (!AnalyzeLp())
return false;
1618 ? parameters_.max_cut_rounds_at_level_zero()
1622 cuts_round < max_cuts_rounds) {
1627 if (parameters_.cut_level() > 0 && num_solves_ > 1) {
1630 expanded_lp_solution_);
1638 if (parameters_.add_objective_cut()) AddObjectiveCut();
1639 if (parameters_.add_mir_cuts()) AddMirCuts();
1640 if (parameters_.add_cg_cuts()) AddCGCuts();
1641 if (parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1645 if (level == 0 || !parameters_.only_add_cuts_at_level_zero()) {
1646 for (
const CutGenerator& generator : cut_generators_) {
1647 if (level > 0 && generator.only_run_at_level_zero)
continue;
1648 if (!generator.generate_cuts(expanded_lp_solution_,
1649 &constraint_manager_)) {
1656 expanded_lp_solution_, &constraint_manager_);
1661 if (constraint_manager_.
ChangeLp(expanded_lp_solution_, &state_,
1664 if (!CreateLpFromConstraintManager()) {
1670 if (num_added == 0) {
1675 if (!SolveLp())
return true;
1676 if (!AnalyzeLp())
return false;
1678 VLOG(3) <<
"Relaxation improvement " << old_obj <<
" -> "
1685 lp_at_level_zero_is_final_ =
true;
1692 if (parameters_.use_branching_in_lp() && objective_is_defined_ &&
1694 lp_solution_is_set_ && !lp_solution_is_integer_ &&
1695 parameters_.linearization_level() >= 2 &&
1696 compute_reduced_cost_averages_ &&
1698 count_since_last_branching_++;
1699 if (count_since_last_branching_ < branching_frequency_) {
1702 count_since_last_branching_ = 0;
1703 bool branching_successful =
false;
1706 const int max_num_branches = 3;
1707 const int num_vars = integer_variables_.size();
1708 std::vector<std::pair<double, IntegerVariable>> branching_vars;
1709 for (
int i = 0; i < num_vars; ++i) {
1710 const IntegerVariable
var = integer_variables_[i];
1715 if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1729 const double cost_i = rc_scores_[i];
1730 std::pair<double, IntegerVariable> branching_var =
1731 std::make_pair(-cost_i, positive_var);
1733 branching_vars.end(), branching_var);
1735 branching_vars.insert(iterator, branching_var);
1736 if (branching_vars.size() > max_num_branches) {
1737 branching_vars.resize(max_num_branches);
1741 for (
const std::pair<double, IntegerVariable>& branching_var :
1743 const IntegerVariable positive_var = branching_var.second;
1744 VLOG(2) <<
"Branching on: " << positive_var;
1745 if (BranchOnVar(positive_var)) {
1746 VLOG(2) <<
"Branching successful.";
1747 branching_successful =
true;
1752 if (!branching_successful) {
1753 branching_frequency_ *= 2;
1763 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1766 const int size = terms.
vars.size();
1767 for (
int i = 0; i < size; ++i) {
1768 const IntegerVariable
var = terms.
vars[i];
1769 const IntegerValue coeff = terms.
coeffs[i];
1781 const int size = constraint.
vars.size();
1782 for (
int i = 0; i < size; ++i) {
1783 const IntegerVariable
var = constraint.
vars[i];
1784 const IntegerValue coeff = constraint.
coeffs[i];
1786 const IntegerValue
bound = coeff > 0
1800 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1801 absl::int128 div128(positive_div.value());
1802 absl::int128 result = x / div128;
1803 if (result * div128 > x)
return result - 1;
1807 absl::int128 CeilRatio128(absl::int128 x, absl::int128 div128) {
1808 absl::int128 result = x / div128;
1809 if (result * div128 < x)
return result + 1;
1816 void DivideConstraint(
const IntegerTrail& integer_trail, IntegerValue divisor,
1817 LinearConstraint* constraint) {
1827 absl::int128 adjust = 0;
1828 const int size = constraint->vars.size();
1829 for (
int i = 0; i < size; ++i) {
1830 const IntegerValue old_coeff = constraint->coeffs[i];
1831 const IntegerValue new_coeff =
FloorRatio(old_coeff, divisor);
1834 const absl::int128 remainder =
1835 absl::int128(old_coeff.value()) -
1836 absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1840 integer_trail.LevelZeroLowerBound(constraint->vars[i]).value());
1842 if (new_coeff == 0)
continue;
1843 constraint->vars[new_size] = constraint->vars[i];
1844 constraint->coeffs[new_size] = new_coeff;
1847 constraint->vars.resize(new_size);
1848 constraint->coeffs.resize(new_size);
1852 constraint->ub = IntegerValue(
static_cast<int64_t
>(
1853 FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1873 absl::int128 max_delta = 0;
1874 const int size = constraint->
vars.size();
1875 for (
int i = 0; i < size; ++i) {
1876 const IntegerVariable
var = constraint->
vars[i];
1879 const absl::int128 coeff(constraint->
coeffs[i].value());
1880 const absl::int128 diff(
1882 max_delta =
std::max(max_delta, coeff * diff);
1884 if (max_delta > threshold) {
1885 const IntegerValue divisor(
1886 static_cast<int64_t
>(CeilRatio128(max_delta, threshold)));
1887 DivideConstraint(integer_trail, divisor, constraint);
1905 absl::int128 sum_min_neg = 0;
1906 absl::int128 sum_min_pos = 0;
1907 absl::int128 sum_max_neg = 0;
1908 absl::int128 sum_max_pos = 0;
1909 const int size = constraint->
vars.size();
1910 for (
int i = 0; i < size; ++i) {
1911 const IntegerVariable
var = constraint->
vars[i];
1912 const absl::int128 coeff(constraint->
coeffs[i].value());
1915 sum_min_pos += coeff * lb;
1917 sum_min_neg += coeff * lb;
1921 sum_max_pos += coeff * ub;
1923 sum_max_neg += coeff * ub;
1926 const absl::int128 min_slack =
1927 static_cast<absl::int128
>(constraint->
ub.value()) -
1928 (sum_min_pos + sum_min_neg);
1929 const absl::int128 max_slack =
1930 static_cast<absl::int128
>(constraint->
ub.value()) -
1931 (sum_max_pos + sum_max_neg);
1932 const absl::int128 max_value =
1933 std::max({-sum_min_neg, sum_min_pos, sum_min_pos + sum_min_neg,
1934 -sum_max_neg, sum_max_pos, sum_max_pos + sum_max_neg,
1935 min_slack, -min_slack, max_slack, -max_slack});
1936 if (max_value > threshold) {
1937 const IntegerValue divisor(
1938 static_cast<int64_t
>(CeilRatio128(max_value, threshold)));
1939 DivideConstraint(integer_trail, divisor, constraint);
1946 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1947 const LinearConstraint& terms, IntegerValue slack) {
1948 integer_reason_.clear();
1949 std::vector<IntegerValue> magnitudes;
1950 const int size = terms.vars.size();
1951 for (
int i = 0; i < size; ++i) {
1952 const IntegerVariable
var = terms.vars[i];
1953 const IntegerValue coeff = terms.coeffs[i];
1956 magnitudes.push_back(coeff);
1959 magnitudes.push_back(-coeff);
1970 std::vector<std::pair<RowIndex, IntegerValue>>
1971 LinearProgrammingConstraint::ScaleLpMultiplier(
1972 bool take_objective_into_account,
1973 const std::vector<std::pair<RowIndex, double>>& lp_multipliers,
1975 double max_sum = 0.0;
1976 tmp_cp_multipliers_.clear();
1977 for (
const std::pair<RowIndex, double>& p : lp_multipliers) {
1978 const RowIndex
row = p.first;
1983 if (std::abs(lp_multi) < kZeroTolerance)
continue;
1999 tmp_cp_multipliers_.push_back({
row, cp_multi});
2000 max_sum +=
ToDouble(infinity_norms_[
row]) * std::abs(cp_multi);
2005 if (take_objective_into_account) {
2006 max_sum +=
ToDouble(objective_infinity_norm_);
2010 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
2011 if (max_sum == 0.0) {
2013 return integer_multipliers;
2018 const double threshold = std::ldexp(1, max_pow) / max_sum;
2019 if (threshold < 1.0) {
2022 return integer_multipliers;
2024 while (2 * *scaling <= threshold) *scaling *= 2;
2029 for (
const auto& entry : tmp_cp_multipliers_) {
2030 const IntegerValue coeff(std::round(entry.second * (*scaling)));
2031 if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
2033 return integer_multipliers;
2036 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
2037 const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
2038 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
2041 scattered_vector->ClearAndResize(integer_variables_.size());
2045 for (
const std::pair<RowIndex, IntegerValue>& term : integer_multipliers) {
2046 const RowIndex
row = term.first;
2047 const IntegerValue multiplier = term.second;
2048 CHECK_LT(
row, integer_lp_.size());
2051 if (!scattered_vector->AddLinearExpressionMultiple(
2052 multiplier, integer_lp_[
row].terms)) {
2057 const IntegerValue
bound =
2058 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
2066 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
2067 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
2068 ScatteredIntegerVector* scattered_vector, IntegerValue*
upper_bound)
const {
2069 const IntegerValue kMaxWantedCoeff(1e18);
2070 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
2071 const RowIndex
row = term.first;
2072 const IntegerValue multiplier = term.second;
2073 if (multiplier == 0)
continue;
2077 IntegerValue negative_limit = kMaxWantedCoeff;
2078 IntegerValue positive_limit = kMaxWantedCoeff;
2082 if (integer_lp_[
row].ub != integer_lp_[
row].lb) {
2083 if (multiplier > 0) {
2084 negative_limit =
std::min(negative_limit, multiplier);
2086 positive_limit =
std::min(positive_limit, -multiplier);
2091 const IntegerValue row_bound =
2092 multiplier > 0 ? integer_lp_[
row].ub : integer_lp_[
row].lb;
2093 if (row_bound != 0) {
2097 const IntegerValue limit2 =
2100 positive_limit =
std::min(positive_limit, limit1);
2101 negative_limit =
std::min(negative_limit, limit2);
2103 negative_limit =
std::min(negative_limit, limit1);
2104 positive_limit =
std::min(positive_limit, limit2);
2116 double positive_diff =
ToDouble(row_bound);
2117 double negative_diff =
ToDouble(row_bound);
2122 for (
const auto& entry : integer_lp_[
row].terms) {
2123 const ColIndex
col = entry.first;
2124 const IntegerValue coeff = entry.second;
2125 const IntegerValue abs_coef =
IntTypeAbs(coeff);
2128 const IntegerVariable
var = integer_variables_[
col.value()];
2135 const IntegerValue current = (*scattered_vector)[
col];
2137 const IntegerValue overflow_limit(
2139 positive_limit =
std::min(positive_limit, overflow_limit);
2140 negative_limit =
std::min(negative_limit, overflow_limit);
2157 const IntegerValue current_magnitude =
IntTypeAbs(current);
2158 const IntegerValue other_direction_limit =
FloorRatio(
2160 ? kMaxWantedCoeff +
std::min(current_magnitude,
2162 : current_magnitude,
2164 const IntegerValue same_direction_limit(
FloorRatio(
2165 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
2167 if ((current > 0) == (coeff > 0)) {
2168 negative_limit =
std::min(negative_limit, other_direction_limit);
2169 positive_limit =
std::min(positive_limit, same_direction_limit);
2171 negative_limit =
std::min(negative_limit, same_direction_limit);
2172 positive_limit =
std::min(positive_limit, other_direction_limit);
2176 const IntegerValue implied = current > 0 ? lb : ub;
2187 IntegerValue to_add(0);
2188 if (positive_diff <= -1.0 && positive_limit > 0) {
2189 to_add = positive_limit;
2191 if (negative_diff >= 1.0 && negative_limit > 0) {
2194 std::abs(
ToDouble(negative_limit) * negative_diff) >
2195 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2196 to_add = -negative_limit;
2200 term.second += to_add;
2205 CHECK(scattered_vector->AddLinearExpressionMultiple(
2206 to_add, integer_lp_[
row].terms));
2225 bool LinearProgrammingConstraint::ExactLpReasonning() {
2227 integer_reason_.clear();
2228 deductions_.clear();
2229 deductions_reason_.clear();
2235 tmp_lp_multipliers_.clear();
2236 for (RowIndex
row(0);
row < num_rows; ++
row) {
2238 if (std::abs(
value) < kZeroTolerance)
continue;
2239 tmp_lp_multipliers_.push_back({
row,
value});
2243 tmp_integer_multipliers_ = ScaleLpMultiplier(
2244 true, tmp_lp_multipliers_, &scaling);
2247 if (!ComputeNewLinearConstraint(tmp_integer_multipliers_,
2248 &tmp_scattered_vector_, &rc_ub)) {
2249 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2255 const IntegerValue obj_scale(std::round(scaling));
2256 if (obj_scale == 0) {
2257 VLOG(1) <<
"Overflow during exact LP reasoning. scaling=" << scaling;
2261 integer_objective_));
2262 CHECK(
AddProductTo(-obj_scale, integer_objective_offset_, &rc_ub));
2263 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2270 tmp_constraint_.
vars.push_back(objective_cp_);
2271 tmp_constraint_.
coeffs.push_back(-obj_scale);
2278 if (tmp_constraint_.
vars.empty()) {
2280 return tmp_constraint_.
ub >= 0;
2283 IntegerSumLE* cp_constraint =
2284 new IntegerSumLE({}, tmp_constraint_.
vars, tmp_constraint_.
coeffs,
2285 tmp_constraint_.
ub, model_);
2289 optimal_constraints_.clear();
2291 optimal_constraints_.emplace_back(cp_constraint);
2292 rev_optimal_constraints_size_ = optimal_constraints_.size();
2293 if (!cp_constraint->PropagateAtLevelZero())
return false;
2294 return cp_constraint->Propagate();
2297 bool LinearProgrammingConstraint::FillExactDualRayReason() {
2300 tmp_lp_multipliers_.clear();
2301 for (RowIndex
row(0);
row < ray.size(); ++
row) {
2303 if (std::abs(
value) < kZeroTolerance)
continue;
2304 tmp_lp_multipliers_.push_back({
row,
value});
2306 tmp_integer_multipliers_ = ScaleLpMultiplier(
2307 false, tmp_lp_multipliers_, &scaling);
2309 IntegerValue new_constraint_ub;
2310 if (!ComputeNewLinearConstraint(tmp_integer_multipliers_,
2311 &tmp_scattered_vector_, &new_constraint_ub)) {
2312 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2316 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2317 &new_constraint_ub);
2320 integer_variables_, new_constraint_ub, &tmp_constraint_);
2326 const IntegerValue implied_lb = GetImpliedLowerBound(tmp_constraint_);
2327 if (implied_lb <= tmp_constraint_.
ub) {
2328 VLOG(1) <<
"LP exact dual ray not infeasible,"
2329 <<
" implied_lb: " << implied_lb.value() / scaling
2330 <<
" ub: " << tmp_constraint_.
ub.value() / scaling;
2333 const IntegerValue slack = (implied_lb - tmp_constraint_.
ub) - 1;
2334 SetImpliedLowerBoundReason(tmp_constraint_, slack);
2338 int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2340 int num_non_basic_with_zero_rc = 0;
2341 for (glop::ColIndex i(0); i < num_vars; ++i) {
2343 if (rc != 0.0)
continue;
2347 num_non_basic_with_zero_rc++;
2350 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2351 return num_non_basic_with_zero_rc;
2354 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2355 double cp_objective_delta) {
2356 deductions_.clear();
2361 const double lp_objective_delta =
2363 const int num_vars = integer_variables_.size();
2364 for (
int i = 0; i < num_vars; i++) {
2365 const IntegerVariable cp_var = integer_variables_[i];
2366 const glop::ColIndex lp_var = glop::ColIndex(i);
2370 if (rc == 0.0)
continue;
2371 const double lp_other_bound =
value + lp_objective_delta / rc;
2372 const double cp_other_bound =
2375 if (rc > kLpEpsilon) {
2377 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2382 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2385 }
else if (rc < -kLpEpsilon) {
2387 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2389 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2390 deductions_.push_back(
2397 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2398 const int num_vars = integer_variables_.size();
2399 if (sum_cost_down_.size() < num_vars) {
2400 sum_cost_down_.resize(num_vars, 0.0);
2401 num_cost_down_.resize(num_vars, 0);
2402 sum_cost_up_.resize(num_vars, 0.0);
2403 num_cost_up_.resize(num_vars, 0);
2404 rc_scores_.resize(num_vars, 0.0);
2408 num_calls_since_reduced_cost_averages_reset_++;
2409 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2410 for (
int i = 0; i < num_vars; i++) {
2411 sum_cost_up_[i] /= 2;
2412 num_cost_up_[i] /= 2;
2413 sum_cost_down_[i] /= 2;
2414 num_cost_down_[i] /= 2;
2416 num_calls_since_reduced_cost_averages_reset_ = 0;
2420 for (
int i = 0; i < num_vars; i++) {
2421 const IntegerVariable
var = integer_variables_[i];
2428 const double rc = lp_reduced_cost_[i];
2429 if (std::abs(rc) < kCpEpsilon)
continue;
2432 sum_cost_down_[i] -= rc;
2433 num_cost_down_[i]++;
2435 sum_cost_up_[i] += rc;
2442 rc_rev_int_repository_.
SetLevel(0);
2448 positions_by_decreasing_rc_score_.clear();
2449 for (
int i = 0; i < num_vars; i++) {
2454 num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2455 const double a_down =
2456 num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2457 if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2458 rc_scores_[i] =
std::min(a_up, a_down);
2460 rc_scores_[i] = 0.5 * (a_down + a_up);
2465 if (rc_scores_[i] > 0.0) {
2466 positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2469 std::sort(positions_by_decreasing_rc_score_.begin(),
2470 positions_by_decreasing_rc_score_.end());
2474 std::function<IntegerLiteral()>
2476 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2479 IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2481 int selected_index = -1;
2482 const int size = positions_by_decreasing_rc_score_.size();
2483 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2484 for (
int i = rev_rc_start_; i < size; ++i) {
2485 const int index = positions_by_decreasing_rc_score_[i].second;
2486 const IntegerVariable
var = integer_variables_[
index];
2489 selected_index =
index;
2494 if (selected_index == -1)
return IntegerLiteral();
2495 const IntegerVariable
var = integer_variables_[selected_index];
2502 const IntegerValue value_ceil(
2504 if (value_ceil >= ub) {
2511 const IntegerValue value_floor(
2513 if (value_floor <= lb) {
2520 num_cost_up_[selected_index] > 0
2521 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2523 const double a_down =
2524 num_cost_down_[selected_index] > 0
2525 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2527 if (a_down < a_up) {
2535 std::string result =
"LP statistics:\n";
2536 absl::StrAppend(&result,
" final dimension: ",
DimensionString(),
"\n");
2537 absl::StrAppend(&result,
" total number of simplex iterations: ",
2539 absl::StrAppend(&result,
" total num cut propagation: ",
2541 absl::StrAppend(&result,
" num solves: \n");
2542 for (
int i = 0; i < num_solves_by_status_.size(); ++i) {
2543 if (num_solves_by_status_[i] == 0)
continue;
2544 absl::StrAppend(&result,
" - #",
2548 absl::StrAppend(&result, constraint_manager_.
Statistics());
2555 std::vector<IntegerVariable> variables;
2556 for (IntegerVariable
var : integer_variables_) {
2559 variables.push_back(
var);
2562 VLOG(1) <<
"HeuristicLPMostInfeasibleBinary has " << variables.size()
2565 return [
this, variables]() {
2569 double fractional_distance_best = -1.0;
2570 for (
const IntegerVariable
var : variables) {
2575 if (lb == ub)
continue;
2579 const double fractional_distance =
2581 lp_value - std::floor(lp_value +
kEpsilon));
2582 if (fractional_distance <
kEpsilon)
continue;
2585 if (fractional_distance > fractional_distance_best) {
2586 fractional_var =
var;
2587 fractional_distance_best = fractional_distance;
2601 std::vector<IntegerVariable> variables;
2602 for (IntegerVariable
var : integer_variables_) {
2605 variables.push_back(
var);
2608 VLOG(1) <<
"HeuristicLpReducedCostBinary has " << variables.size()
2614 const int num_vars = variables.size();
2615 std::vector<double> cost_to_zero(num_vars, 0.0);
2616 std::vector<int> num_cost_to_zero(num_vars);
2619 return [=]()
mutable {
2624 if (num_calls == 10000) {
2625 for (
int i = 0; i < num_vars; i++) {
2626 cost_to_zero[i] /= 2;
2627 num_cost_to_zero[i] /= 2;
2633 for (
int i = 0; i < num_vars; i++) {
2634 const IntegerVariable
var = variables[i];
2639 if (lb == ub)
continue;
2643 if (std::abs(rc) <
kEpsilon)
continue;
2646 if (
value == 1.0 && rc < 0.0) {
2647 cost_to_zero[i] -= rc;
2648 num_cost_to_zero[i]++;
2653 int selected_index = -1;
2654 double best_cost = 0.0;
2655 for (
int i = 0; i < num_vars; i++) {
2656 const IntegerVariable
var = variables[i];
2661 if (num_cost_to_zero[i] > 0 &&
2662 best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2663 best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2668 if (selected_index >= 0) {
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)
const DenseColumn & constraint_lower_bounds() const
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
ColIndex CreateNewVariable()
const DenseColumn & constraint_upper_bounds() const
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 DenseRow & GetDualRayRowCombination() const
Fractional GetVariableValue(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
Fractional GetConstraintActivity(RowIndex row) const
VariableStatus GetVariableStatus(ColIndex col) const
Fractional GetReducedCost(ColIndex col) const
const DenseColumn & GetDualRay() const
void NotifyThatMatrixIsChangedForNextSolve()
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
Fractional GetObjectiveValue() const
Fractional GetDualValue(RowIndex row) const
void ClearIntegralityScales()
void NotifyThatMatrixIsUnchangedForNextSolve()
ConstraintStatus GetConstraintStatus(RowIndex row) const
ColIndex GetProblemNumCols() const
void LoadStateForNextSolve(const BasisState &state)
RowIndex GetProblemNumRows() const
void ClearStateForNextSolve()
int64_t GetNumberOfIterations() const
const BasisState & GetState() const
ColIndex GetBasis(RowIndex row) const
void SetParameters(const GlopParameters ¶meters)
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
EntryIndex num_entries() const
const LinearConstraint & cut() const
bool TrySimpleKnapsack(const CutData &input, ImpliedBoundsProcessor *ib_processor=nullptr)
bool TryWithLetchfordSouliLifting(const CutData &input, ImpliedBoundsProcessor *ib_processor=nullptr)
bool MakeAllTermsPositive(CutData *cut)
const LinearConstraint & cut() const
bool ComputeFlowCoverRelaxationAndGenerateCut(const LinearConstraint &base_ct, const absl::StrongVector< IntegerVariable, double > &lp_values, IntegerTrail *integer_trail, ImpliedBoundsProcessor *ib_helper)
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 RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
void AddData(double new_record)
double CurrentAverage() const
const LinearConstraint & cut() const
bool ComputeCut(RoundingOptions options, const CutData &base_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
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 DebugCheckConstraint(const LinearConstraint &cut)
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
const absl::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
const std::vector< ConstraintIndex > & LpConstraints() const
std::string Statistics() const
void AddAllConstraintsToLp()
bool ChangeLp(const absl::StrongVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state, int *num_new_constraints=nullptr)
bool AddCut(const LinearConstraint &ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
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, absl::Span< const IntegerVariable > vars)
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 NotifyThatModelIsUnsat()
void ConvertToLinearConstraint(const std::vector< IntegerVariable > &integer_variables, IntegerValue upper_bound, LinearConstraint *result)
bool Add(glop::ColIndex col, IntegerValue value)
void ClearAndResize(int size)
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
bool AddLinearExpressionMultiple(IntegerValue multiplier, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
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)
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
void AddOneConstraint(glop::RowIndex, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms, IntegerValue lb, IntegerValue ub)
constexpr double kEpsilon
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)
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)
bool PossibleOverflow(const IntegerTrail &integer_trail, const LinearConstraint &constraint)
std::string FormatCounter(int64_t num)
void PreventOverflow(const IntegerTrail &integer_trail, LinearConstraint *constraint)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
bool VariableIsPositive(IntegerVariable i)
void DivideByGCD(LinearConstraint *constraint)
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.
bool AtMinOrMaxInt64(int64_t x)
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
std::vector< CutTerm > terms
bool FillFromLinearConstraint(const LinearConstraint &base_ct, const absl::StrongVector< IntegerVariable, double > &lp_values, IntegerTrail *integer_trail)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)