30#include "absl/algorithm/container.h"
31#include "absl/container/flat_hash_map.h"
32#include "absl/log/check.h"
33#include "absl/log/log.h"
34#include "absl/log/vlog_is_on.h"
35#include "absl/numeric/int128.h"
36#include "absl/strings/str_cat.h"
37#include "absl/strings/str_format.h"
38#include "absl/strings/string_view.h"
39#include "absl/types/span.h"
83 for (
const glop::ColIndex col : non_zeros_) {
84 dense_vector_[col] = IntegerValue(0);
86 dense_vector_.resize(size, IntegerValue(0));
88 dense_vector_.assign(size, IntegerValue(0));
90 for (
const glop::ColIndex col : non_zeros_) {
91 is_zeros_[col] =
true;
93 is_zeros_.resize(size,
true);
99 const int64_t add =
CapAdd(value.value(), dense_vector_[col].value());
100 if (add == std::numeric_limits<int64_t>::min() ||
101 add == std::numeric_limits<int64_t>::max())
103 dense_vector_[col] = IntegerValue(add);
104 if (is_sparse_ && is_zeros_[col]) {
105 is_zeros_[col] =
false;
106 non_zeros_.push_back(col);
111template <
bool check_overflow>
113 const IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
114 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude) {
116 if (check_overflow) {
117 const IntegerValue prod =
CapProdI(max_coeff_magnitude, multiplier);
121 IntegerValue* data = dense_vector_.data();
122 const double threshold = 0.1 *
static_cast<double>(dense_vector_.size());
123 const int num_terms = cols.size();
124 if (is_sparse_ &&
static_cast<double>(num_terms) < threshold) {
125 for (
int i = 0;
i < num_terms; ++
i) {
126 const glop::ColIndex col = cols[
i];
127 if (is_zeros_[col]) {
128 is_zeros_[col] =
false;
129 non_zeros_.push_back(col);
131 const IntegerValue product = multiplier * coeffs[
i];
132 if (check_overflow) {
134 data[col.value()].mutable_value())) {
138 data[col.value()] += product;
141 if (
static_cast<double>(non_zeros_.size()) > threshold) {
146 for (
int i = 0;
i < num_terms; ++
i) {
147 const glop::ColIndex col = cols[
i];
148 const IntegerValue product = multiplier * coeffs[
i];
149 if (check_overflow) {
151 data[col.value()].mutable_value())) {
155 data[col.value()] += product;
164 IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
165 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude);
168 IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
169 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude);
172 absl::Span<const IntegerVariable> integer_variables,
173 IntegerValue upper_bound,
174 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term) {
178 for (
const glop::ColIndex col : non_zeros_) {
179 const IntegerValue coeff = dense_vector_[col];
180 if (coeff == 0)
continue;
184 for (
const IntegerValue coeff : dense_vector_) {
185 if (coeff != 0) ++final_size;
188 if (extra_term != std::nullopt) ++final_size;
192 result.
resize(final_size);
197 std::sort(non_zeros_.begin(), non_zeros_.end());
198 for (
const glop::ColIndex col : non_zeros_) {
199 const IntegerValue coeff = dense_vector_[col];
200 if (coeff == 0)
continue;
201 result.
vars[new_size] = integer_variables[col.value()];
202 result.
coeffs[new_size] = coeff;
206 const int size = dense_vector_.size();
207 for (glop::ColIndex col(0); col < size; ++col) {
208 const IntegerValue coeff = dense_vector_[col];
209 if (coeff == 0)
continue;
210 result.
vars[new_size] = integer_variables[col.value()];
211 result.
coeffs[new_size] = coeff;
217 result.
ub = upper_bound;
219 if (extra_term != std::nullopt) {
220 result.
vars[new_size] += extra_term->first;
221 result.
coeffs[new_size] += extra_term->second;
225 CHECK_EQ(new_size, final_size);
231 absl::int128 rhs, absl::Span<const IntegerVariable> integer_variables,
232 absl::Span<const double> lp_solution,
IntegerTrail* integer_trail,
234 result->
terms.clear();
236 absl::Span<const IntegerValue> dense_vector = dense_vector_;
238 std::sort(non_zeros_.begin(), non_zeros_.end());
239 for (
const glop::ColIndex col : non_zeros_) {
240 const IntegerValue coeff = dense_vector[col.value()];
241 if (coeff == 0)
continue;
242 const IntegerVariable var = integer_variables[col.value()];
243 CHECK(result->
AppendOneTerm(var, coeff, lp_solution[col.value()],
248 for (
int col(0); col < dense_vector.size(); ++col) {
249 const IntegerValue coeff = dense_vector[col];
250 if (coeff == 0)
continue;
251 const IntegerVariable var = integer_variables[col];
259std::vector<std::pair<glop::ColIndex, IntegerValue>>
261 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
263 std::sort(non_zeros_.begin(), non_zeros_.end());
264 for (
const glop::ColIndex col : non_zeros_) {
265 const IntegerValue coeff = dense_vector_[col];
266 if (coeff != 0) result.push_back({col, coeff});
269 const int size = dense_vector_.size();
270 for (glop::ColIndex col(0); col < size; ++col) {
271 const IntegerValue coeff = dense_vector_[col];
272 if (coeff != 0) result.push_back({col, coeff});
281 Model* model, absl::Span<const IntegerVariable> vars)
282 : constraint_manager_(model),
285 time_limit_(model->GetOrCreate<
TimeLimit>()),
287 trail_(model->GetOrCreate<
Trail>()),
297 rlt_cut_helper_(model),
298 implied_bounds_processor_({}, integer_trail_,
305 simplex_params_.set_use_dual_simplex(
true);
306 simplex_params_.set_primal_feasibility_tolerance(
307 parameters_.lp_primal_tolerance());
308 simplex_params_.set_dual_feasibility_tolerance(
309 parameters_.lp_dual_tolerance());
310 if (parameters_.use_exact_lp_reason()) {
311 simplex_params_.set_change_status_to_imprecise(
false);
313 simplex_.SetParameters(simplex_params_);
315 simplex_.SetRandom(*random_);
317 compute_reduced_cost_averages_ =
true;
321 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
322 mirror_lp_variable_.resize(integer_trail_->NumIntegerVariables(),
326 auto* stats = model->GetOrCreate<SharedStatistics>();
327 integer_rounding_cut_helper_.SetSharedStatistics(stats);
328 cover_cut_helper_.SetSharedStatistics(stats);
331 CHECK(std::is_sorted(vars.begin(), vars.end()));
337 for (
const IntegerVariable positive_variable : vars) {
339 implied_bounds_processor_.AddLpVariable(positive_variable);
340 (*dispatcher_)[positive_variable] =
this;
342 if (!symmetrizer_->AppearInFoldedProblem(positive_variable))
continue;
344 integer_variables_.push_back(positive_variable);
345 extended_integer_variables_.push_back(positive_variable);
347 mirror_lp_variable_[positive_variable] = col;
352 if (symmetrizer_->HasSymmetry()) {
353 for (
const IntegerVariable var : integer_variables_) {
354 if (!symmetrizer_->IsOrbitSumVar(var))
continue;
355 const int orbit_index = symmetrizer_->OrbitIndex(var);
356 for (
const IntegerVariable var : symmetrizer_->Orbit(orbit_index)) {
357 extended_integer_variables_.push_back(var);
359 mirror_lp_variable_[var] = col;
366 CHECK_EQ(vars.size(), extended_integer_variables_.size());
367 lp_solution_.assign(vars.size(), std::numeric_limits<double>::infinity());
368 lp_reduced_cost_.assign(vars.size(), 0.0);
371 const IntegerVariable max_index = std::max(
372 NegationOf(vars.back()), integer_trail_->NumIntegerVariables() - 1);
373 if (max_index >= expanded_lp_solution_.size()) {
374 expanded_lp_solution_.assign(max_index.value() + 1, 0.0);
376 if (max_index >= expanded_reduced_costs_.size()) {
377 expanded_reduced_costs_.assign(max_index.value() + 1, 0.0);
383 DCHECK(!lp_constraint_is_registered_);
386 const auto index = constraint_manager_.Add(std::move(ct), &added, &folded);
390 if (added && folded) {
391 const auto& info = constraint_manager_.AllConstraints()[index];
393 const absl::Span<const IntegerVariable> vars = new_ct.
VarsAsSpan();
394 if (!info.ub_is_trivial) {
395 if (!linear_propagator_->AddConstraint({}, vars, new_ct.
CoeffsAsSpan(),
400 if (!info.lb_is_trivial) {
401 tmp_vars_.assign(vars.begin(), vars.end());
402 for (IntegerVariable& var : tmp_vars_) var =
NegationOf(var);
403 if (!linear_propagator_->AddConstraint(
413 DCHECK(!lp_constraint_is_registered_);
414 lp_constraint_is_registered_ =
true;
421 std::sort(integer_objective_.begin(), integer_objective_.end());
422 objective_infinity_norm_ = 0;
423 for (
const auto [col, coeff] : integer_objective_) {
424 constraint_manager_.SetObjectiveCoefficient(integer_variables_[col.value()],
426 objective_infinity_norm_ =
427 std::max(objective_infinity_norm_,
IntTypeAbs(coeff));
435 if (!parameters_.add_lp_constraints_lazily() &&
436 constraint_manager_.num_constraints() < 1e6) {
437 constraint_manager_.AddAllConstraintsToLp();
439 if (!CreateLpFromConstraintManager()) {
444 watcher_id_ = watcher_->Register(
this);
445 const int num_vars = integer_variables_.size();
446 orbit_indices_.clear();
447 for (
int i = 0;
i < num_vars;
i++) {
448 const IntegerVariable pos_var = integer_variables_[
i];
449 DCHECK(symmetrizer_->AppearInFoldedProblem(pos_var));
450 watcher_->WatchIntegerVariable(pos_var, watcher_id_,
i);
451 if (symmetrizer_->IsOrbitSumVar(pos_var)) {
452 orbit_indices_.push_back(symmetrizer_->OrbitIndex(pos_var));
455 if (objective_is_defined_) {
456 watcher_->WatchUpperBound(objective_cp_, watcher_id_);
458 watcher_->SetPropagatorPriority(watcher_id_, 2);
459 watcher_->AlwaysCallAtLevelZero(watcher_id_);
463 integer_trail_->RegisterReversibleClass(
this);
464 watcher_->RegisterReversibleInt(watcher_id_, &rev_optimal_constraints_size_);
467glop::ColIndex LinearProgrammingConstraint::GetMirrorVariable(
468 IntegerVariable positive_variable) {
470 return mirror_lp_variable_[positive_variable];
474 IntegerValue coeff) {
475 CHECK(!lp_constraint_is_registered_);
476 objective_is_defined_ =
true;
478 if (ivar != pos_var) coeff = -coeff;
479 integer_objective_.push_back({GetMirrorVariable(pos_var), coeff});
495bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
498 integer_lp_cols_.clear();
499 integer_lp_coeffs_.clear();
501 infinity_norms_.clear();
502 const auto& all_constraints = constraint_manager_.
AllConstraints();
503 for (
const auto index : constraint_manager_.
LpConstraints()) {
506 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
510 integer_lp_.
push_back(LinearConstraintInternal());
511 LinearConstraintInternal& new_ct = integer_lp_.back();
514 new_ct.lb_is_trivial = all_constraints[index].lb_is_trivial;
515 new_ct.ub_is_trivial = all_constraints[index].ub_is_trivial;
517 IntegerValue infinity_norm = 0;
518 infinity_norm = std::max(infinity_norm,
IntTypeAbs(ct.
lb));
519 infinity_norm = std::max(infinity_norm,
IntTypeAbs(ct.
ub));
520 new_ct.start_in_buffer = integer_lp_cols_.size();
525 new_ct.num_terms = size;
526 for (
int i = 0;
i < size; ++
i) {
528 const IntegerVariable var = ct.
vars[
i];
529 const IntegerValue coeff = ct.
coeffs[
i];
530 infinity_norm = std::max(infinity_norm,
IntTypeAbs(coeff));
531 integer_lp_cols_.push_back(GetMirrorVariable(var));
532 integer_lp_coeffs_.push_back(coeff);
534 infinity_norms_.
push_back(infinity_norm);
537 DCHECK(std::is_sorted(
538 integer_lp_cols_.data() + new_ct.start_in_buffer,
539 integer_lp_cols_.data() + new_ct.start_in_buffer + new_ct.num_terms));
545 objective_infinity_norm_ = 0;
546 for (
const auto& entry : integer_objective_) {
547 const IntegerVariable var = integer_variables_[entry.first.value()];
548 if (integer_trail_->IsFixedAtLevelZero(var)) {
549 integer_objective_offset_ +=
550 entry.second * integer_trail_->LevelZeroLowerBound(var);
553 objective_infinity_norm_ =
554 std::max(objective_infinity_norm_,
IntTypeAbs(entry.second));
555 integer_objective_[new_size++] = entry;
557 integer_objective_.resize(new_size);
558 objective_infinity_norm_ =
559 std::max(objective_infinity_norm_,
IntTypeAbs(integer_objective_offset_));
564 ComputeIntegerLpScalingFactors();
570 scaler_.ConfigureFromFactors(row_factors_, col_factors_);
571 scaler_.AverageCostScaling(&obj_with_slack_);
572 scaler_.ContainOneBoundScaling(simplex_.MutableLowerBounds(),
573 simplex_.MutableUpperBounds());
576 UpdateBoundsOfLpVariables();
581 if (parameters_.polish_lp_solution()) {
582 simplex_.ClearIntegralityScales();
583 const int num_vars = integer_variables_.size();
584 for (
int i = 0;
i < num_vars; ++
i) {
585 const IntegerVariable cp_var = integer_variables_[
i];
586 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(cp_var);
587 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(cp_var);
588 if (lb != 0 || ub != 1)
continue;
589 simplex_.SetIntegralityScale(
591 1.0 / scaler_.VariableScalingFactor(glop::ColIndex(
i)));
595 VLOG(3) <<
"LP relaxation: " << integer_lp_.size() <<
" x "
596 << integer_variables_.size() <<
". "
597 << constraint_manager_.AllConstraints().size()
598 <<
" Managed constraints.";
604void LinearProgrammingConstraint::ComputeIntegerLpScalingFactors() {
605 const int num_rows = integer_lp_.size();
606 const int num_cols = integer_variables_.size();
609 const double infinity = std::numeric_limits<double>::infinity();
610 row_factors_.assign(num_rows, 1.0);
611 col_factors_.assign(num_cols, 1.0);
614 IntegerValue* coeffs = integer_lp_coeffs_.data();
615 glop::ColIndex* cols = integer_lp_cols_.data();
616 double* row_factors = row_factors_.data();
617 double* col_factors = col_factors_.data();
619 col_min_.assign(num_cols, infinity);
620 col_max_.assign(num_cols, 0.0);
621 double* col_min = col_min_.data();
622 double* col_max = col_max_.data();
624 for (
int i = 0;
i < 4; ++
i) {
626 for (
int row = 0; row < num_rows; ++row) {
627 double min_scaled = +infinity;
628 double max_scaled = 0.0;
629 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
630 for (
int i = 0;
i < ct.num_terms; ++
i) {
631 const int index = ct.start_in_buffer +
i;
632 const int col = cols[index].value();
633 const double coeff =
static_cast<double>(coeffs[index].value());
634 const double scaled_magnitude = col_factors[col] * std::abs(coeff);
635 min_scaled = std::min(min_scaled, scaled_magnitude);
636 max_scaled = std::max(max_scaled, scaled_magnitude);
639 if (ct.num_terms == 0)
continue;
640 const Fractional factor(std::sqrt(max_scaled * min_scaled));
641 row_factors[row] = 1.0 / factor;
645 for (
int row = 0; row < num_rows; ++row) {
646 const double row_factor = row_factors[row];
647 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
648 for (
int i = 0;
i < ct.num_terms; ++
i) {
649 const int index = ct.start_in_buffer +
i;
650 const int col = cols[index].value();
651 const double coeff =
static_cast<double>(coeffs[index].value());
652 const double scaled_magnitude = row_factor * std::abs(coeff);
653 col_min[col] = std::min(col_min[col], scaled_magnitude);
654 col_max[col] = std::max(col_max[col], scaled_magnitude);
657 for (
int col = 0; col < num_cols; ++col) {
658 if (col_min[col] == infinity)
continue;
659 col_factors[col] = 1.0 / std::sqrt(col_min[col] * col_max[col]);
662 col_min[col] = infinity;
668 for (
int row = 0; row < num_rows; ++row) {
669 double max_scaled = 0.0;
670 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
671 for (
int i = 0;
i < ct.num_terms; ++
i) {
672 const int index = ct.start_in_buffer +
i;
673 const int col = cols[index].value();
674 const double coeff =
static_cast<double>(coeffs[index].value());
675 const double scaled_magnitude = col_factors[col] * std::abs(coeff);
676 max_scaled = std::max(max_scaled, scaled_magnitude);
678 if (ct.num_terms == 0)
continue;
679 row_factors[row] = 1.0 / max_scaled;
683 for (
int row = 0; row < num_rows; ++row) {
684 const double row_factor = row_factors[row];
685 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
686 for (
int i = 0;
i < ct.num_terms; ++
i) {
687 const int index = ct.start_in_buffer +
i;
688 const int col = cols[index].value();
689 const double coeff =
static_cast<double>(coeffs[index].value());
690 const double scaled_magnitude = row_factor * std::abs(coeff);
691 col_max[col] = std::max(col_max[col], scaled_magnitude);
694 for (
int col = 0; col < num_cols; ++col) {
695 if (col_max[col] == 0)
continue;
696 col_factors[col] = 1.0 / col_max[col];
700void LinearProgrammingConstraint::FillLpData() {
701 const int num_rows = integer_lp_.size();
702 const int num_cols = integer_variables_.size();
703 IntegerValue* coeffs = integer_lp_coeffs_.data();
704 glop::ColIndex* cols = integer_lp_cols_.data();
705 double* row_factors = row_factors_.data();
706 double* col_factors = col_factors_.data();
709 glop::CompactSparseMatrix* data = simplex_.MutableTransposedMatrixWithSlack();
710 data->Reset(glop::RowIndex(num_cols + num_rows));
711 for (
int row = 0; row < num_rows; ++row) {
712 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
713 const double row_factor = row_factors[row];
714 for (
int i = 0;
i < ct.num_terms; ++
i) {
715 const int index = ct.start_in_buffer +
i;
716 const int col = cols[index].value();
717 const double coeff =
static_cast<double>(coeffs[index].value());
718 const double scaled_coeff = row_factor * col_factors[col] * coeff;
719 data->AddEntryToCurrentColumn(RowIndex(col), scaled_coeff);
723 data->AddEntryToCurrentColumn(RowIndex(num_cols + row), 1.0);
726 data->CloseCurrentColumn();
730 const glop::ColIndex num_cols_with_slacks(num_rows + num_cols);
731 obj_with_slack_.assign(num_cols_with_slacks, 0.0);
732 for (
const auto [col, value] : integer_objective_) {
733 obj_with_slack_[col] =
ToDouble(value) * col_factors[col.value()];
737 simplex_.MutableLowerBounds()->resize(num_cols_with_slacks);
738 simplex_.MutableUpperBounds()->resize(num_cols_with_slacks);
739 Fractional* lb_with_slack = simplex_.MutableLowerBounds()->data();
740 Fractional* ub_with_slack = simplex_.MutableUpperBounds()->data();
741 const double infinity = std::numeric_limits<double>::infinity();
742 for (
int row = 0; row < integer_lp_.size(); ++row) {
743 const LinearConstraintInternal& ct = integer_lp_[glop::RowIndex(row)];
749 const double factor = row_factors[row];
750 lb_with_slack[num_cols + row] =
751 ct.ub_is_trivial ? -infinity :
ToDouble(-ct.ub) * factor;
752 ub_with_slack[num_cols + row] =
753 ct.lb_is_trivial ? +infinity :
ToDouble(-ct.lb) * factor;
762 const int num_vars = integer_variables_.size();
763 for (
int i = 0;
i < num_vars;
i++) {
764 const IntegerVariable cp_var = integer_variables_[
i];
765 const double factor = col_factors[
i];
767 ToDouble(integer_trail_->LevelZeroLowerBound(cp_var)) * factor;
769 ToDouble(integer_trail_->LevelZeroUpperBound(cp_var)) * factor;
773void LinearProgrammingConstraint::FillReducedCostReasonIn(
775 std::vector<IntegerLiteral>* integer_reason) {
776 integer_reason->clear();
777 const int num_vars = integer_variables_.size();
778 for (
int i = 0;
i < num_vars;
i++) {
779 const double rc = reduced_costs[glop::ColIndex(
i)];
780 if (rc > kLpEpsilon) {
781 integer_reason->push_back(
782 integer_trail_->LowerBoundAsLiteral(integer_variables_[
i]));
783 }
else if (rc < -kLpEpsilon) {
784 integer_reason->push_back(
785 integer_trail_->UpperBoundAsLiteral(integer_variables_[
i]));
789 integer_trail_->RemoveLevelZeroBounds(integer_reason);
794 if (level == 0) rev_optimal_constraints_size_ = 0;
795 optimal_constraints_.resize(rev_optimal_constraints_size_);
796 cumulative_optimal_constraint_sizes_.resize(rev_optimal_constraints_size_);
797 if (lp_solution_is_set_ && level < lp_solution_level_) {
798 lp_solution_is_set_ =
false;
801 if (level < previous_level_) {
802 lp_at_optimal_ =
false;
803 lp_objective_lower_bound_ = -std::numeric_limits<double>::infinity();
805 previous_level_ = level;
814 if (level == 0 && !lp_solution_is_set_ && !level_zero_lp_solution_.empty()) {
815 lp_solution_is_set_ =
true;
816 lp_solution_ = level_zero_lp_solution_;
817 lp_solution_level_ = 0;
821 if (expanded_lp_solution_.size() < integer_trail_->NumIntegerVariables()) {
822 expanded_lp_solution_.resize(integer_trail_->NumIntegerVariables());
824 for (IntegerVariable
i(0);
i < integer_trail_->NumIntegerVariables(); ++
i) {
825 if (integer_trail_->IsFixed(
i)) {
826 expanded_lp_solution_[
i] =
ToDouble(integer_trail_->LowerBound(
i));
829 for (
int i = 0;
i < lp_solution_.size();
i++) {
830 const IntegerVariable var = extended_integer_variables_[
i];
831 expanded_lp_solution_[var] = lp_solution_[
i];
832 expanded_lp_solution_[
NegationOf(var)] = -lp_solution_[
i];
838 cut_generators_.push_back(std::move(generator));
842 const std::vector<int>& watch_indices) {
843 if (!enabled_)
return true;
853 if (!cumulative_optimal_constraint_sizes_.empty()) {
854 const double current_size =
855 static_cast<double>(cumulative_optimal_constraint_sizes_.back());
856 const double low_limit = 1e7;
857 if (current_size > low_limit) {
860 const double num_enqueues =
static_cast<double>(integer_trail_->Index());
861 if ((current_size - low_limit) > 100 * num_enqueues)
return true;
865 if (!lp_solution_is_set_ || num_force_lp_call_on_next_propagate_ > 0) {
871 if (trail_->CurrentDecisionLevel() == 0 && !lp_at_level_zero_is_final_) {
877 for (
const int index : watch_indices) {
879 ToDouble(integer_trail_->LowerBound(integer_variables_[index]));
881 ToDouble(integer_trail_->UpperBound(integer_variables_[index]));
882 const double value = lp_solution_[index];
883 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
904 glop::ColIndex var) {
909 IntegerVariable variable)
const {
910 return lp_solution_[mirror_lp_variable_[variable].value()];
913void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
914 const int num_vars = integer_variables_.size();
917 for (
int i = 0;
i < num_vars;
i++) {
918 const IntegerVariable cp_var = integer_variables_[
i];
920 static_cast<double>(integer_trail_->
LowerBound(cp_var).value());
922 static_cast<double>(integer_trail_->
UpperBound(cp_var).value());
924 lb_with_slack[
i] = lb * factor;
925 ub_with_slack[
i] = ub * factor;
929bool LinearProgrammingConstraint::SolveLp() {
932 lp_at_level_zero_is_final_ =
false;
935 const double unscaling_factor = 1.0 / scaler_.ObjectiveScalingFactor();
936 const double offset_before_unscaling =
937 ToDouble(integer_objective_offset_) * scaler_.ObjectiveScalingFactor();
938 auto status = simplex_.MinimizeFromTransposedMatrixWithSlack(
939 obj_with_slack_, unscaling_factor, offset_before_unscaling, time_limit_);
943 VLOG(2) <<
"The LP solver returned abnormal, resolving from scratch";
944 simplex_.ClearStateForNextSolve();
945 status = simplex_.MinimizeFromTransposedMatrixWithSlack(
946 obj_with_slack_, unscaling_factor, offset_before_unscaling,
950 state_ = simplex_.GetState();
951 total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
953 VLOG(2) <<
"The LP solver encountered an error: " << status.error_message();
954 simplex_.ClearStateForNextSolve();
957 average_degeneracy_.AddData(CalculateDegeneracy());
958 if (average_degeneracy_.CurrentAverage() >= 1000.0) {
959 VLOG(2) <<
"High average degeneracy: "
960 << average_degeneracy_.CurrentAverage();
963 const int status_as_int =
static_cast<int>(simplex_.GetProblemStatus());
964 if (status_as_int >= num_solves_by_status_.size()) {
965 num_solves_by_status_.resize(status_as_int + 1);
968 num_solves_by_status_[status_as_int]++;
969 VLOG(2) <<
DimensionString() <<
" lvl:" << trail_->CurrentDecisionLevel()
970 <<
" " << simplex_.GetProblemStatus()
971 <<
" iter:" << simplex_.GetNumberOfIterations()
972 <<
" obj:" << simplex_.GetObjectiveValue() <<
" scaled:"
973 << objective_definition_->ScaleObjective(
974 simplex_.GetObjectiveValue());
978 lp_objective_lower_bound_ = simplex_.GetObjectiveValue();
985 parameters_.stop_after_root_propagation()) {
986 lp_solution_is_set_ =
true;
987 lp_solution_level_ = trail_->CurrentDecisionLevel();
988 const int num_vars = integer_variables_.size();
989 const auto reduced_costs = simplex_.GetReducedCosts().const_view();
990 for (
int i = 0;
i < num_vars;
i++) {
991 const glop::ColIndex col(
i);
992 const IntegerVariable var = integer_variables_[
i];
995 lp_solution_[
i] = value;
996 expanded_lp_solution_[var] = value;
997 expanded_lp_solution_[
NegationOf(var)] = -value;
1000 scaler_.UnscaleReducedCost(col, reduced_costs[col]);
1001 lp_reduced_cost_[
i] = rc;
1002 expanded_reduced_costs_[var] = rc;
1003 expanded_reduced_costs_[
NegationOf(var)] = -rc;
1008 for (
const int orbit_index : orbit_indices_) {
1009 const IntegerVariable sum_var = symmetrizer_->OrbitSumVar(orbit_index);
1010 const double lp_value = expanded_lp_solution_[sum_var];
1011 const absl::Span<const IntegerVariable> orbit =
1012 symmetrizer_->Orbit(orbit_index);
1020 bool to_lower =
false;
1021 bool to_upper =
false;
1022 if (trail_->CurrentDecisionLevel() > 0) {
1024 static_cast<double>(integer_trail_->LowerBound(sum_var).value());
1026 static_cast<double>(integer_trail_->UpperBound(sum_var).value());
1027 if (std::abs(lp_value - lb) < 1e-2) {
1029 }
else if (std::abs(lp_value - ub) < 1e-2) {
1047 const double even_split = lp_value /
static_cast<double>(orbit.size());
1051 const double new_rc = expanded_reduced_costs_[sum_var];
1053 for (
const IntegerVariable var : orbit) {
1054 const glop::ColIndex col = GetMirrorVariable(var);
1056 double new_value = even_split;
1059 static_cast<double>(integer_trail_->LowerBound(var).value());
1060 }
else if (to_upper) {
1062 static_cast<double>(integer_trail_->UpperBound(var).value());
1065 lp_solution_[col.value()] = new_value;
1066 expanded_lp_solution_[var] = new_value;
1067 expanded_lp_solution_[
NegationOf(var)] = -new_value;
1069 lp_reduced_cost_[col.value()] = new_rc;
1070 expanded_reduced_costs_[var] = new_rc;
1071 expanded_reduced_costs_[
NegationOf(var)] = -new_rc;
1076 lp_solution_is_integer_ =
true;
1077 for (
const double value : lp_solution_) {
1078 if (std::abs(value - std::round(value)) > kCpEpsilon) {
1079 lp_solution_is_integer_ =
false;
1084 if (lp_solution_level_ == 0) {
1085 level_zero_lp_solution_ = lp_solution_;
1092bool LinearProgrammingConstraint::AnalyzeLp() {
1095 if (parameters_.use_exact_lp_reason()) {
1096 return PropagateExactDualRay();
1098 FillReducedCostReasonIn(simplex_.GetDualRayRowCombination(),
1100 return integer_trail_->ReportConflict(integer_reason_);
1104 UpdateSimplexIterationLimit(10, 1000);
1108 if (objective_is_defined_ &&
1113 if (parameters_.use_exact_lp_reason()) {
1114 if (!PropagateExactLpReason())
return false;
1117 if (VLOG_IS_ON(2)) {
1118 const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1119 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1120 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1121 const IntegerValue propagated_lb =
1122 integer_trail_->LowerBound(objective_cp_);
1123 if (approximate_new_lb > propagated_lb) {
1124 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1125 <<
ToDouble(integer_trail_->UpperBound(objective_cp_))
1126 <<
" ] approx_lb += "
1127 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1128 << integer_trail_->UpperBound(objective_cp_) - propagated_lb;
1135 FillReducedCostReasonIn(simplex_.GetReducedCosts(), &integer_reason_);
1136 const double objective_cp_ub =
1137 ToDouble(integer_trail_->UpperBound(objective_cp_));
1138 const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1139 ReducedCostStrengtheningDeductions(objective_cp_ub -
1140 relaxed_optimal_objective);
1141 if (!deductions_.empty()) {
1142 deductions_reason_ = integer_reason_;
1143 deductions_reason_.push_back(
1144 integer_trail_->UpperBoundAsLiteral(objective_cp_));
1148 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1149 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1150 if (approximate_new_lb > integer_trail_->LowerBound(objective_cp_)) {
1151 const IntegerLiteral deduction =
1153 if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
1159 if (!deductions_.empty()) {
1160 const int trail_index_with_same_reason = integer_trail_->Index();
1161 for (
const IntegerLiteral deduction : deductions_) {
1162 if (!integer_trail_->Enqueue(deduction, {}, deductions_reason_,
1163 trail_index_with_same_reason)) {
1172 if (compute_reduced_cost_averages_ &&
1174 CHECK(lp_solution_is_set_);
1175 UpdateAverageReducedCosts();
1184 if (objective_is_defined_ &&
1185 objective_definition_->objective_var == objective_cp_ &&
1186 trail_->CurrentDecisionLevel() == 0) {
1187 shared_response_manager_->UpdateInnerObjectiveBounds(
1188 model_->Name(), integer_trail_->LowerBound(objective_cp_),
1189 integer_trail_->UpperBound(objective_cp_));
1201bool LinearProgrammingConstraint::PreprocessCut(IntegerVariable first_slack,
1204 cut->ComplementForPositiveCoefficients();
1206 problem_proven_infeasible_by_cuts_ =
true;
1213 for (
const CutTerm& term : cut->terms) {
1214 reachable_.Add(term.coeff.value());
1218 if (cut->rhs < absl::int128(reachable_.LastValue()) &&
1219 !reachable_.MightBeReachable(
static_cast<int64_t
>(cut->rhs))) {
1220 problem_proven_infeasible_by_cuts_ =
true;
1224 bool some_fixed_terms =
false;
1225 bool some_fractional_positions =
false;
1226 for (CutTerm& term : cut->terms) {
1227 const absl::int128 magnitude128 = term.coeff.value();
1228 const absl::int128 range =
1229 absl::int128(term.bound_diff.value()) * magnitude128;
1231 IntegerValue new_diff = term.bound_diff;
1232 if (range > cut->rhs) {
1233 new_diff =
static_cast<int64_t
>(cut->rhs / magnitude128);
1237 absl::int128 rest128 =
1238 cut->rhs - absl::int128(new_diff.value()) * magnitude128;
1239 while (rest128 < absl::int128(reachable_.LastValue()) &&
1240 !reachable_.MightBeReachable(
static_cast<int64_t
>(rest128))) {
1241 ++total_num_eq_propagations_;
1242 CHECK_GT(new_diff, 0);
1244 rest128 += magnitude128;
1248 if (new_diff < term.bound_diff) {
1249 term.bound_diff = new_diff;
1251 const IntegerVariable var = term.expr_vars[0];
1252 if (var < first_slack) {
1254 ++total_num_cut_propagations_;
1257 if (term.expr_coeffs[0] == 1) {
1259 if (!integer_trail_->Enqueue(
1261 var, term.bound_diff - term.expr_offset),
1263 problem_proven_infeasible_by_cuts_ =
true;
1267 CHECK_EQ(term.expr_coeffs[0], -1);
1269 if (!integer_trail_->Enqueue(
1271 var, term.expr_offset - term.bound_diff),
1273 problem_proven_infeasible_by_cuts_ =
true;
1281 const int slack_index = (var.value() - first_slack.value()) / 2;
1282 const glop::RowIndex row = tmp_slack_rows_[slack_index];
1283 if (term.expr_coeffs[0] == 1) {
1285 const IntegerValue new_ub = term.bound_diff - term.expr_offset;
1286 if (constraint_manager_.UpdateConstraintUb(row, new_ub)) {
1287 integer_lp_[row].ub = new_ub;
1291 CHECK_EQ(term.expr_coeffs[0], -1);
1292 const IntegerValue new_lb = term.expr_offset - term.bound_diff;
1293 if (constraint_manager_.UpdateConstraintLb(row, new_lb)) {
1294 integer_lp_[row].lb = new_lb;
1300 if (term.bound_diff == 0) {
1301 some_fixed_terms =
true;
1303 if (term.IsFractional()) {
1304 some_fractional_positions =
true;
1310 if (some_fixed_terms) {
1312 for (
const CutTerm& term : cut->terms) {
1313 if (term.bound_diff == 0)
continue;
1314 cut->terms[new_size++] = term;
1316 cut->terms.resize(new_size);
1318 return some_fractional_positions;
1321bool LinearProgrammingConstraint::AddCutFromConstraints(
1322 absl::string_view name,
1323 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers) {
1333 IntegerValue cut_ub;
1334 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
1336 ++num_cut_overflows_;
1337 VLOG(1) <<
"Issue, overflow!";
1348 tmp_scattered_vector_.ConvertToCutData(
1349 cut_ub.value(), extended_integer_variables_, lp_solution_, integer_trail_,
1354 ImpliedBoundsProcessor* ib_processor =
nullptr;
1356 bool some_ints =
false;
1357 bool some_fractional_positions =
false;
1358 for (
const CutTerm& term : base_ct_.terms) {
1359 if (term.bound_diff > 1) some_ints =
true;
1360 if (term.IsFractional()) {
1361 some_fractional_positions =
true;
1366 if (!some_fractional_positions)
return false;
1367 if (some_ints) ib_processor = &implied_bounds_processor_;
1372 const IntegerVariable first_slack(expanded_lp_solution_.size());
1373 CHECK_EQ(first_slack.value() % 2, 0);
1374 tmp_slack_rows_.clear();
1375 for (
const auto [row, coeff] : integer_multipliers) {
1376 if (integer_lp_[row].lb == integer_lp_[row].ub)
continue;
1379 entry.coeff = coeff > 0 ? coeff : -coeff;
1380 entry.lp_value = 0.0;
1381 entry.bound_diff = integer_lp_[row].ub - integer_lp_[row].lb;
1382 entry.expr_vars[0] =
1383 first_slack + 2 * IntegerVariable(tmp_slack_rows_.size());
1384 entry.expr_coeffs[1] = 0;
1385 const double activity = scaler_.UnscaleConstraintActivity(
1386 row, simplex_.GetConstraintActivity(row));
1389 entry.lp_value =
ToDouble(integer_lp_[row].ub) - activity;
1390 entry.expr_coeffs[0] = IntegerValue(-1);
1391 entry.expr_offset = integer_lp_[row].ub;
1394 entry.lp_value = activity -
ToDouble(integer_lp_[row].lb);
1395 entry.expr_coeffs[0] = IntegerValue(1);
1396 entry.expr_offset = -integer_lp_[row].lb;
1399 base_ct_.terms.push_back(entry);
1400 tmp_slack_rows_.push_back(row);
1404 if (!PreprocessCut(first_slack, &base_ct_))
return false;
1407 if (base_ct_.rhs == 1)
return false;
1411 double activity = 0.0;
1413 for (
const CutTerm& term : base_ct_.terms) {
1414 const double coeff =
ToDouble(term.coeff);
1415 activity += term.lp_value * coeff;
1416 norm += coeff * coeff;
1418 if (std::abs(activity -
static_cast<double>(base_ct_.rhs)) / norm > 1e-4) {
1419 VLOG(1) <<
"Cut not tight " << activity
1420 <<
" <= " <<
static_cast<double>(base_ct_.rhs);
1425 bool at_least_one_added =
false;
1426 DCHECK(base_ct_.AllCoefficientsArePositive());
1431 if (integer_multipliers.size() == 1 && parameters_.add_rlt_cuts()) {
1432 if (rlt_cut_helper_.TrySimpleSeparation(base_ct_)) {
1433 at_least_one_added |= PostprocessAndAddCut(
1434 absl::StrCat(name,
"_RLT"), rlt_cut_helper_.Info(), first_slack,
1435 rlt_cut_helper_.cut());
1440 if (ib_processor !=
nullptr) {
1441 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1442 ib_processor =
nullptr;
1449 cover_cut_helper_.ClearCache();
1451 if (cover_cut_helper_.TrySingleNodeFlow(base_ct_, ib_processor)) {
1452 at_least_one_added |= PostprocessAndAddCut(
1453 absl::StrCat(name,
"_FF"), cover_cut_helper_.Info(), first_slack,
1454 cover_cut_helper_.cut());
1456 if (cover_cut_helper_.TrySimpleKnapsack(base_ct_, ib_processor)) {
1457 at_least_one_added |= PostprocessAndAddCut(
1458 absl::StrCat(name,
"_K"), cover_cut_helper_.Info(), first_slack,
1459 cover_cut_helper_.cut());
1464 if (cover_cut_helper_.TryWithLetchfordSouliLifting(base_ct_,
1466 at_least_one_added |= PostprocessAndAddCut(
1467 absl::StrCat(name,
"_KL"), cover_cut_helper_.Info(), first_slack,
1468 cover_cut_helper_.cut());
1474 base_ct_.ComplementForSmallerLpValues();
1476 RoundingOptions options;
1477 options.max_scaling = parameters_.max_integer_rounding_scaling();
1478 options.use_ib_before_heuristic =
false;
1479 if (integer_rounding_cut_helper_.ComputeCut(options, base_ct_,
1481 at_least_one_added |= PostprocessAndAddCut(
1482 absl::StrCat(name,
"_R"), integer_rounding_cut_helper_.Info(),
1483 first_slack, integer_rounding_cut_helper_.cut());
1486 options.use_ib_before_heuristic =
true;
1487 options.prefer_positive_ib =
false;
1488 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.ComputeCut(
1489 options, base_ct_, ib_processor)) {
1490 at_least_one_added |= PostprocessAndAddCut(
1491 absl::StrCat(name,
"_RB"), integer_rounding_cut_helper_.Info(),
1492 first_slack, integer_rounding_cut_helper_.cut());
1495 options.use_ib_before_heuristic =
true;
1496 options.prefer_positive_ib =
true;
1497 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.ComputeCut(
1498 options, base_ct_, ib_processor)) {
1499 at_least_one_added |= PostprocessAndAddCut(
1500 absl::StrCat(name,
"_RBP"), integer_rounding_cut_helper_.Info(),
1501 first_slack, integer_rounding_cut_helper_.cut());
1505 return at_least_one_added;
1508bool LinearProgrammingConstraint::PostprocessAndAddCut(
1509 const std::string& name,
const std::string& info,
1510 IntegerVariable first_slack,
const CutData& cut) {
1511 if (cut.rhs > absl::int128(std::numeric_limits<int64_t>::max()) ||
1512 cut.rhs < absl::int128(std::numeric_limits<int64_t>::min())) {
1513 VLOG(2) <<
"RHS overflow " << name <<
" " << info;
1514 ++num_cut_overflows_;
1522 if (cut.ComputeViolation() < 1e-4) {
1523 VLOG(3) <<
"Bad cut " << name <<
" " << info;
1529 tmp_scattered_vector_.ClearAndResize(extended_integer_variables_.size());
1530 IntegerValue cut_ub =
static_cast<int64_t
>(cut.rhs);
1531 for (
const CutTerm& term : cut.terms) {
1532 if (term.coeff == 0)
continue;
1533 if (!
AddProductTo(-term.coeff, term.expr_offset, &cut_ub)) {
1534 VLOG(2) <<
"Overflow in conversion";
1535 ++num_cut_overflows_;
1538 for (
int i = 0;
i < 2; ++
i) {
1539 if (term.expr_coeffs[
i] == 0)
continue;
1540 const IntegerVariable var = term.expr_vars[
i];
1541 const IntegerValue coeff =
1542 CapProd(term.coeff.value(), term.expr_coeffs[
i].value());
1544 VLOG(2) <<
"Overflow in conversion";
1545 ++num_cut_overflows_;
1550 if (var < first_slack) {
1553 tmp_scattered_vector_.Add(col, coeff);
1555 tmp_scattered_vector_.Add(col, -coeff);
1560 const int slack_index = (var.value() - first_slack.value()) / 2;
1561 const glop::RowIndex row = tmp_slack_rows_[slack_index];
1562 if (!tmp_scattered_vector_.AddLinearExpressionMultiple(
1563 coeff, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
1564 infinity_norms_[row])) {
1565 VLOG(2) <<
"Overflow in slack removal";
1566 ++num_cut_overflows_;
1576 LinearConstraint converted_cut =
1577 tmp_scattered_vector_.ConvertToLinearConstraint(
1578 extended_integer_variables_, cut_ub);
1583 top_n_cuts_.AddCut(std::move(converted_cut), name, expanded_lp_solution_);
1586 return constraint_manager_.AddCut(std::move(converted_cut), name, info);
1593void LinearProgrammingConstraint::AddCGCuts() {
1594 std::vector<std::pair<RowIndex, double>> sorted_columns;
1595 const RowIndex num_rows(integer_lp_.size());
1597 for (RowIndex index(0); index < num_rows; ++index) {
1598 const ColIndex basis_col = simplex_.GetBasis(index);
1607 if (basis_col >= integer_variables_.size())
continue;
1612 simplex_.GetVariableValue(basis_col) /
1613 scaler_.VariableScalingFactorWithSlack(basis_col);
1621 const double fractionality = std::abs(lp_value - std::round(lp_value));
1622 if (fractionality < 0.01)
continue;
1624 const double score = fractionality * (1.0 - fractionality) / norms[index];
1625 sorted_columns.push_back({index, score});
1627 absl::c_sort(sorted_columns, [](
const std::pair<RowIndex, double>& a,
1628 const std::pair<RowIndex, double>&
b) {
1629 return a.second >
b.second;
1633 for (
const auto [index, _] : sorted_columns) {
1634 if (time_limit_->LimitReached())
return;
1639 tmp_lp_multipliers_.clear();
1640 const glop::ScatteredRow& lambda = simplex_.GetUnitRowLeftInverse(index);
1641 if (lambda.non_zeros.empty()) {
1642 for (RowIndex row(0); row < num_rows; ++row) {
1644 if (std::abs(value) < kZeroTolerance)
continue;
1645 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
1648 for (
const ColIndex col : lambda.non_zeros) {
1650 const double value = lambda.values[col];
1651 if (std::abs(value) < kZeroTolerance)
continue;
1652 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
1657 if (tmp_lp_multipliers_.size() <= 1)
continue;
1659 IntegerValue scaling;
1660 for (
int i = 0;
i < 2; ++
i) {
1661 tmp_cg_multipliers_ = tmp_lp_multipliers_;
1667 for (std::pair<RowIndex, double>& p : tmp_cg_multipliers_) {
1668 p.second = -p.second;
1679 IgnoreTrivialConstraintMultipliers(&tmp_cg_multipliers_);
1680 if (tmp_cg_multipliers_.size() <= 1)
continue;
1682 ScaleMultipliers(tmp_cg_multipliers_,
1684 &tmp_integer_multipliers_);
1686 if (AddCutFromConstraints(
"CG", tmp_integer_multipliers_)) {
1693 if (num_added > 10)
break;
1706void LinearProgrammingConstraint::AddObjectiveCut() {
1707 if (integer_objective_.size() <= 1)
return;
1712 const double obj_lp_value = simplex_.GetObjectiveValue();
1713 const IntegerValue obj_lower_bound =
1714 integer_trail_->LevelZeroLowerBound(objective_cp_);
1715 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1718 LinearConstraint objective_ct;
1720 objective_ct.ub = integer_objective_offset_ -
1721 integer_trail_->LevelZeroLowerBound(objective_cp_);
1722 IntegerValue obj_coeff_magnitude(0);
1723 objective_ct.resize(integer_objective_.size());
1725 for (
const auto& [col, coeff] : integer_objective_) {
1726 const IntegerVariable var = integer_variables_[col.value()];
1727 objective_ct.vars[
i] = var;
1728 objective_ct.coeffs[
i] = -coeff;
1729 obj_coeff_magnitude = std::max(obj_coeff_magnitude,
IntTypeAbs(coeff));
1733 if (!base_ct_.FillFromLinearConstraint(objective_ct, expanded_lp_solution_,
1741 if (obj_coeff_magnitude < 1e9 &&
1742 constraint_manager_.AddCut(std::move(objective_ct),
"Objective")) {
1748 ImpliedBoundsProcessor* ib_processor =
nullptr;
1750 bool some_ints =
false;
1751 bool some_relevant_positions =
false;
1752 for (
const CutTerm& term : base_ct_.terms) {
1753 if (term.bound_diff > 1) some_ints =
true;
1754 if (term.HasRelevantLpValue()) some_relevant_positions =
true;
1758 if (!some_relevant_positions)
return;
1759 if (some_ints) ib_processor = &implied_bounds_processor_;
1763 const IntegerVariable first_slack(
1764 std::numeric_limits<IntegerVariable::ValueType>::max());
1765 if (ib_processor !=
nullptr) {
1766 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1767 ib_processor =
nullptr;
1772 base_ct_.ComplementForPositiveCoefficients();
1773 cover_cut_helper_.ClearCache();
1774 if (cover_cut_helper_.TrySimpleKnapsack(base_ct_, ib_processor)) {
1775 PostprocessAndAddCut(
"Objective_K", cover_cut_helper_.Info(), first_slack,
1776 cover_cut_helper_.cut());
1780 RoundingOptions options;
1781 options.max_scaling = parameters_.max_integer_rounding_scaling();
1782 base_ct_.ComplementForSmallerLpValues();
1783 if (integer_rounding_cut_helper_.ComputeCut(options, base_ct_,
1785 PostprocessAndAddCut(
"Objective_R", integer_rounding_cut_helper_.Info(),
1786 first_slack, integer_rounding_cut_helper_.cut());
1790void LinearProgrammingConstraint::AddMirCuts() {
1805 util_intops::StrongVector<ColIndex, IntegerValue> dense_cut(
1806 integer_variables_.size(), IntegerValue(0));
1807 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1811 const int num_cols = integer_variables_.size();
1812 const int num_rows = integer_lp_.size();
1813 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1814 util_intops::StrongVector<RowIndex, double> row_weights(num_rows, 0.0);
1815 Fractional* lb_with_slack = simplex_.MutableLowerBounds()->data();
1816 Fractional* ub_with_slack = simplex_.MutableUpperBounds()->data();
1817 for (RowIndex row(0); row < num_rows; ++row) {
1824 const auto status = simplex_.GetConstraintStatus(row);
1825 const double activity = simplex_.GetConstraintActivity(row);
1826 const double ct_lb = -ub_with_slack[num_cols + row.value()];
1827 const double ct_ub = -lb_with_slack[num_cols + row.value()];
1828 if (activity > ct_ub - 1e-4 ||
1831 base_rows.push_back({row, IntegerValue(1)});
1833 if (activity < ct_lb + 1e-4 ||
1836 base_rows.push_back({row, IntegerValue(-1)});
1855 std::max(
Fractional(1e-8), std::abs(simplex_.GetDualValue(row)));
1861 int64_t dtime_num_entries = 0;
1862 std::shuffle(base_rows.begin(), base_rows.end(), *random_);
1864 std::vector<double> weights;
1865 util_intops::StrongVector<RowIndex, bool> used_rows;
1866 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1867 const auto matrix = simplex_.MatrixWithSlack().view();
1868 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1869 if (time_limit_->LimitReached())
break;
1870 if (dtime_num_entries > 1e7)
break;
1872 const glop::RowIndex base_row = entry.first;
1873 const IntegerValue multiplier = entry.second;
1882 integer_multipliers = {entry};
1883 if ((multiplier > 0 && !integer_lp_[base_row].ub_is_trivial) ||
1884 (multiplier < 0 && !integer_lp_[base_row].lb_is_trivial)) {
1885 if (AddCutFromConstraints(
"MIR_1", integer_multipliers))
continue;
1889 for (
const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1890 dense_cut[col] = IntegerValue(0);
1892 non_zeros_.ResetAllToFalse();
1895 const LinearConstraintInternal& ct = integer_lp_[entry.first];
1896 for (
int i = 0;
i < ct.num_terms; ++
i) {
1897 const int index = ct.start_in_buffer +
i;
1898 const ColIndex col = integer_lp_cols_[index];
1899 const IntegerValue coeff = integer_lp_coeffs_[index];
1900 non_zeros_.Set(col);
1901 dense_cut[col] += coeff * multiplier;
1904 used_rows.
assign(num_rows,
false);
1905 used_rows[entry.first] =
true;
1910 const int kMaxAggregation = 5;
1911 for (
int i = 0;
i < kMaxAggregation; ++
i) {
1914 IntegerValue max_magnitude(0);
1916 std::vector<ColIndex> col_candidates;
1917 for (
const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1918 if (dense_cut[col] == 0)
continue;
1920 max_magnitude = std::max(max_magnitude,
IntTypeAbs(dense_cut[col]));
1921 const int col_degree = matrix.ColumnNumEntries(col).value();
1922 if (col_degree <= 1)
continue;
1927 const IntegerVariable var = integer_variables_[col.value()];
1928 const double lp_value = expanded_lp_solution_[var];
1929 const double lb =
ToDouble(integer_trail_->LevelZeroLowerBound(var));
1930 const double ub =
ToDouble(integer_trail_->LevelZeroUpperBound(var));
1931 const double bound_distance = std::min(ub - lp_value, lp_value - lb);
1932 if (bound_distance > 1e-2) {
1933 weights.push_back(bound_distance);
1934 col_candidates.push_back(col);
1937 if (col_candidates.empty())
break;
1939 const ColIndex var_to_eliminate =
1943 std::vector<RowIndex> possible_rows;
1945 for (
const auto entry_index : matrix.Column(var_to_eliminate)) {
1946 const RowIndex row = matrix.EntryRow(entry_index);
1952 if (used_rows[row])
continue;
1953 used_rows[row] =
true;
1960 bool add_row =
false;
1961 if (!integer_lp_[row].ub_is_trivial) {
1963 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1965 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1968 if (!integer_lp_[row].lb_is_trivial) {
1970 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1972 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1976 possible_rows.push_back(row);
1977 weights.push_back(row_weights[row]);
1980 if (possible_rows.empty())
break;
1982 const RowIndex row_to_combine =
1986 IntegerValue to_combine_coeff = 0;
1987 const LinearConstraintInternal& ct_to_combine =
1988 integer_lp_[row_to_combine];
1989 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
1990 const int index = ct_to_combine.start_in_buffer +
i;
1991 if (integer_lp_cols_[index] == var_to_eliminate) {
1992 to_combine_coeff = integer_lp_coeffs_[index];
1996 CHECK_NE(to_combine_coeff, 0);
1998 IntegerValue mult1 = -to_combine_coeff;
1999 IntegerValue mult2 = dense_cut[var_to_eliminate];
2006 const IntegerValue gcd = IntegerValue(
2016 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
2017 max_magnitude = std::max(max_magnitude,
IntTypeAbs(entry.second));
2019 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
2020 CapProd(infinity_norms_[row_to_combine].value(),
2021 std::abs(mult2.value()))) ==
2022 std::numeric_limits<int64_t>::max()) {
2026 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
2027 dtime_num_entries += integer_lp_[entry.first].num_terms;
2028 entry.second *= mult1;
2030 dtime_num_entries += integer_lp_[row_to_combine].num_terms;
2031 integer_multipliers.push_back({row_to_combine, mult2});
2034 if (AddCutFromConstraints(absl::StrCat(
"MIR_",
i + 2),
2035 integer_multipliers)) {
2041 if (
i + 1 == kMaxAggregation)
break;
2043 for (ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
2044 dense_cut[col] *= mult1;
2046 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
2047 const int index = ct_to_combine.start_in_buffer +
i;
2048 const ColIndex col = integer_lp_cols_[index];
2049 const IntegerValue coeff = integer_lp_coeffs_[index];
2050 non_zeros_.Set(col);
2051 dense_cut[col] += coeff * mult2;
2057void LinearProgrammingConstraint::AddZeroHalfCuts() {
2058 if (time_limit_->LimitReached())
return;
2060 tmp_lp_values_.clear();
2061 tmp_var_lbs_.clear();
2062 tmp_var_ubs_.clear();
2063 for (
const IntegerVariable var : integer_variables_) {
2064 tmp_lp_values_.push_back(expanded_lp_solution_[var]);
2065 tmp_var_lbs_.push_back(integer_trail_->LevelZeroLowerBound(var));
2066 tmp_var_ubs_.push_back(integer_trail_->LevelZeroUpperBound(var));
2070 zero_half_cut_helper_.ProcessVariables(tmp_lp_values_, tmp_var_lbs_,
2072 for (glop::RowIndex row(0); row < integer_lp_.size(); ++row) {
2075 const auto status = simplex_.GetConstraintStatus(row);
2079 zero_half_cut_helper_.AddOneConstraint(
2080 row, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2081 integer_lp_[row].lb, integer_lp_[row].ub);
2083 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
2084 zero_half_cut_helper_.InterestingCandidates(random_)) {
2085 if (time_limit_->LimitReached())
break;
2091 AddCutFromConstraints(
"ZERO_HALF", multipliers);
2095void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
2096 const int64_t min_iter,
const int64_t max_iter) {
2097 if (parameters_.linearization_level() < 2)
return;
2098 const int64_t num_degenerate_columns = CalculateDegeneracy();
2099 const int64_t num_cols = simplex_.GetProblemNumCols().value();
2100 if (num_cols <= 0) {
2103 CHECK_GT(num_cols, 0);
2104 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
2109 if (is_degenerate_) {
2110 next_simplex_iter_ /= std::max(int64_t{1}, decrease_factor);
2112 next_simplex_iter_ *= 2;
2115 if (is_degenerate_) {
2116 next_simplex_iter_ /= std::max(int64_t{1}, 2 * decrease_factor);
2120 next_simplex_iter_ = num_cols / 40;
2123 next_simplex_iter_ =
2124 std::max(min_iter, std::min(max_iter, next_simplex_iter_));
2128 const int old_num_force = num_force_lp_call_on_next_propagate_;
2129 num_force_lp_call_on_next_propagate_ = 0;
2130 if (!enabled_)
return true;
2131 if (time_limit_->LimitReached())
return true;
2132 const int64_t timestamp_at_function_start = integer_trail_->num_enqueues();
2134 UpdateBoundsOfLpVariables();
2138 if ( (
false) && objective_is_defined_) {
2146 simplex_params_.set_objective_upper_limit(
2147 static_cast<double>(integer_trail_->UpperBound(objective_cp_).value() +
2148 100.0 * kCpEpsilon));
2154 if (trail_->CurrentDecisionLevel() == 0) {
2155 simplex_params_.set_max_number_of_iterations(
2156 parameters_.root_lp_iterations());
2158 simplex_params_.set_max_number_of_iterations(next_simplex_iter_);
2161 simplex_.SetParameters(simplex_params_);
2162 simplex_.SetRandom(*random_);
2163 if (!SolveLp())
return true;
2164 if (!AnalyzeLp())
return false;
2167 const int max_cuts_rounds = trail_->CurrentDecisionLevel() == 0
2168 ? parameters_.max_cut_rounds_at_level_zero()
2172 cuts_round < max_cuts_rounds) {
2180 if (integer_trail_->num_enqueues() > timestamp_at_function_start &&
2181 old_num_force < 3) {
2182 num_force_lp_call_on_next_propagate_ = old_num_force + 1;
2183 watcher_->CallAgainDuringThisPropagation();
2191 if (parameters_.cut_level() > 0 &&
2192 (num_solves_ > 1 || !parameters_.add_lp_constraints_lazily())) {
2194 implied_bounds_processor_.RecomputeCacheAndSeparateSomeImpliedBoundCuts(
2195 expanded_lp_solution_);
2196 if (parameters_.add_rlt_cuts()) {
2197 rlt_cut_helper_.Initialize(extended_integer_variables_);
2204 const int level = trail_->CurrentDecisionLevel();
2205 if (trail_->CurrentDecisionLevel() == 0) {
2206 problem_proven_infeasible_by_cuts_ =
false;
2207 if (parameters_.add_objective_cut()) AddObjectiveCut();
2208 if (parameters_.add_mir_cuts()) AddMirCuts();
2209 if (parameters_.add_cg_cuts()) AddCGCuts();
2210 if (parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
2211 if (problem_proven_infeasible_by_cuts_) {
2212 return integer_trail_->ReportConflict({});
2214 top_n_cuts_.TransferToManager(&constraint_manager_);
2218 if (level == 0 || !parameters_.only_add_cuts_at_level_zero()) {
2220 if (level > 0 && generator.only_run_at_level_zero)
continue;
2221 if (!generator.generate_cuts(&constraint_manager_)) {
2227 implied_bounds_processor_.IbCutPool().TransferToManager(
2228 &constraint_manager_);
2232 if (constraint_manager_.ChangeLp(&state_, &num_added)) {
2234 simplex_.LoadStateForNextSolve(state_);
2235 if (!CreateLpFromConstraintManager()) {
2236 return integer_trail_->ReportConflict({});
2241 if (num_added == 0) {
2245 const double old_obj = simplex_.GetObjectiveValue();
2246 if (!SolveLp())
return true;
2247 if (!AnalyzeLp())
return false;
2249 VLOG(3) <<
"Relaxation improvement " << old_obj <<
" -> "
2250 << simplex_.GetObjectiveValue()
2251 <<
" diff: " << simplex_.GetObjectiveValue() - old_obj
2252 <<
" level: " << trail_->CurrentDecisionLevel();
2255 if (trail_->CurrentDecisionLevel() == 0) {
2256 lp_at_level_zero_is_final_ =
true;
2265absl::int128 LinearProgrammingConstraint::GetImpliedLowerBound(
2267 absl::int128 lower_bound(0);
2269 for (
int i = 0;
i < size; ++
i) {
2270 const IntegerVariable var = terms.
vars[
i];
2271 const IntegerValue coeff = terms.
coeffs[
i];
2272 const IntegerValue bound = coeff > 0 ? integer_trail_->
LowerBound(var)
2274 lower_bound += absl::int128(bound.value()) * absl::int128(coeff.value());
2279bool LinearProgrammingConstraint::ScalingCanOverflow(
2280 int power,
bool take_objective_into_account,
2281 absl::Span<
const std::pair<glop::RowIndex, double>> multipliers,
2282 int64_t overflow_cap)
const {
2284 const int64_t factor = int64_t{1} << power;
2285 const double factor_as_double =
static_cast<double>(factor);
2286 if (take_objective_into_account) {
2288 if (bound >= overflow_cap)
return true;
2290 for (
const auto [row, double_coeff] : multipliers) {
2291 const double magnitude =
2292 std::abs(std::round(double_coeff * factor_as_double));
2293 if (std::isnan(magnitude))
return true;
2294 if (magnitude >=
static_cast<double>(std::numeric_limits<int64_t>::max())) {
2298 infinity_norms_[row].value()));
2299 if (bound >= overflow_cap)
return true;
2301 return bound >= overflow_cap;
2304void LinearProgrammingConstraint::IgnoreTrivialConstraintMultipliers(
2305 std::vector<std::pair<RowIndex, double>>* lp_multipliers) {
2307 for (
const std::pair<RowIndex, double>& p : *lp_multipliers) {
2308 const RowIndex row = p.first;
2310 if (lp_multi > 0.0 && integer_lp_[row].ub_is_trivial)
continue;
2311 if (lp_multi < 0.0 && integer_lp_[row].lb_is_trivial)
continue;
2312 (*lp_multipliers)[new_size++] = p;
2314 lp_multipliers->resize(new_size);
2317void LinearProgrammingConstraint::ScaleMultipliers(
2318 absl::Span<
const std::pair<RowIndex, double>> lp_multipliers,
2319 bool take_objective_into_account, IntegerValue* scaling,
2320 std::vector<std::pair<RowIndex, IntegerValue>>* output)
const {
2324 if (lp_multipliers.empty()) {
2331 const int64_t overflow_cap = std::numeric_limits<int64_t>::max();
2332 if (ScalingCanOverflow(0, take_objective_into_account,
2333 lp_multipliers, overflow_cap)) {
2334 ++num_scaling_issues_;
2348 if (candidate >= 63)
return false;
2350 return !ScalingCanOverflow(candidate, take_objective_into_account,
2351 lp_multipliers, overflow_cap);
2353 *scaling = int64_t{1} << power;
2357 int64_t gcd = scaling->value();
2358 const double scaling_as_double =
static_cast<double>(scaling->value());
2359 for (
const auto [row, double_coeff] : lp_multipliers) {
2360 const IntegerValue coeff(std::round(double_coeff * scaling_as_double));
2362 gcd = std::gcd(gcd, std::abs(coeff.value()));
2363 output->push_back({row, coeff});
2368 for (
auto& entry : *output) {
2369 entry.second /= gcd;
2374template <
bool check_overflow>
2375bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
2376 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers,
2380 scattered_vector->
ClearAndResize(extended_integer_variables_.size());
2384 for (
const std::pair<RowIndex, IntegerValue>& term : integer_multipliers) {
2385 const RowIndex row = term.first;
2386 const IntegerValue multiplier = term.second;
2387 DCHECK_LT(row, integer_lp_.size());
2391 multiplier, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2392 infinity_norms_[row])) {
2397 const IntegerValue bound =
2398 multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
2399 if (!
AddProductTo(multiplier, bound, upper_bound))
return false;
2406void LinearProgrammingConstraint::AdjustNewLinearConstraint(
2407 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
2409 const IntegerValue kMaxWantedCoeff(1e18);
2410 bool adjusted =
false;
2411 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
2412 const RowIndex row = term.first;
2413 const IntegerValue multiplier = term.second;
2414 if (multiplier == 0)
continue;
2420 IntegerValue negative_limit =
2421 FloorRatio(kMaxWantedCoeff, infinity_norms_[row]);
2422 IntegerValue positive_limit = negative_limit;
2426 if (integer_lp_[row].ub != integer_lp_[row].lb) {
2427 if (multiplier > 0) {
2428 negative_limit = std::min(negative_limit, multiplier);
2430 positive_limit = std::min(positive_limit, -multiplier);
2435 const IntegerValue row_bound =
2436 multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
2437 if (row_bound != 0) {
2439 std::max(IntegerValue(0), kMaxWantedCoeff -
IntTypeAbs(*upper_bound)),
2441 const IntegerValue limit2 =
2443 if ((*upper_bound > 0) == (row_bound > 0)) {
2444 positive_limit = std::min(positive_limit, limit1);
2445 negative_limit = std::min(negative_limit, limit2);
2447 negative_limit = std::min(negative_limit, limit1);
2448 positive_limit = std::min(positive_limit, limit2);
2460 double common_diff =
ToDouble(row_bound);
2461 double positive_diff = 0.0;
2462 double negative_diff = 0.0;
2467 const LinearConstraintInternal& ct = integer_lp_[row];
2468 for (
int i = 0;
i < ct.num_terms; ++
i) {
2469 const int index = ct.start_in_buffer +
i;
2470 const ColIndex col = integer_lp_cols_[index];
2471 const IntegerValue coeff = integer_lp_coeffs_[index];
2472 DCHECK_NE(coeff, 0);
2477 const IntegerValue current = (*scattered_vector)[col];
2479 const IntegerVariable var = integer_variables_[col.value()];
2480 const IntegerValue lb = integer_trail_->LowerBound(var);
2481 const IntegerValue ub = integer_trail_->UpperBound(var);
2502 const IntegerValue abs_coeff =
IntTypeAbs(coeff);
2503 const IntegerValue current_magnitude =
IntTypeAbs(current);
2504 const IntegerValue overflow_threshold =
2505 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude);
2506 if ((current > 0) == (coeff > 0)) {
2507 if (negative_limit * abs_coeff > current_magnitude) {
2508 negative_limit = current_magnitude / abs_coeff;
2509 if (positive_limit == 0 && negative_limit == 0)
break;
2511 if (positive_limit * abs_coeff > overflow_threshold) {
2512 positive_limit = overflow_threshold / abs_coeff;
2513 if (positive_limit == 0 && negative_limit == 0)
break;
2516 if (negative_limit * abs_coeff > overflow_threshold) {
2517 negative_limit = overflow_threshold / abs_coeff;
2518 if (positive_limit == 0 && negative_limit == 0)
break;
2520 if (positive_limit * abs_coeff > current_magnitude) {
2521 positive_limit = current_magnitude / abs_coeff;
2522 if (positive_limit == 0 && negative_limit == 0)
break;
2527 const IntegerVariable var = integer_variables_[col.value()];
2528 const IntegerValue implied = current > 0
2529 ? integer_trail_->LowerBound(var)
2530 : integer_trail_->UpperBound(var);
2536 positive_diff += common_diff;
2537 negative_diff += common_diff;
2543 IntegerValue to_add(0);
2544 if (positive_diff <= -1.0 && positive_limit > 0) {
2545 to_add = positive_limit;
2547 if (negative_diff >= 1.0 && negative_limit > 0) {
2550 std::abs(
ToDouble(negative_limit) * negative_diff) >
2551 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2552 to_add = -negative_limit;
2556 term.second += to_add;
2557 *upper_bound += to_add * row_bound;
2559 CHECK(scattered_vector
2560 ->AddLinearExpressionMultiple</*check_overflow=*/false>(
2561 to_add, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2562 infinity_norms_[row]));
2565 if (adjusted) ++num_adjusts_;
2568void LinearProgrammingConstraint::SetReducedCostsInConstraintManager(
2570 constraint_manager_.ClearReducedCostsInfo();
2571 absl::int128 ub = ct.ub.value();
2572 absl::int128 level_zero_lb = 0;
2573 for (
int i = 0;
i < ct.num_terms; ++
i) {
2574 IntegerVariable var = ct.vars[
i];
2575 IntegerValue coeff = ct.coeffs[
i];
2581 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(var);
2582 level_zero_lb += absl::int128(coeff.value()) * absl::int128(lb.value());
2584 if (lb == integer_trail_->LevelZeroUpperBound(var))
continue;
2585 const LiteralIndex lit = integer_encoder_->GetAssociatedLiteral(
2588 constraint_manager_.SetLiteralReducedCost(Literal(lit), coeff);
2591 const absl::int128 gap = absl::int128(ct.ub.value()) - level_zero_lb;
2593 constraint_manager_.SetReducedCostsGap(gap);
2595 constraint_manager_.ClearReducedCostsInfo();
2599bool LinearProgrammingConstraint::PropagateLpConstraint(
LinearConstraint ct) {
2600 DCHECK(constraint_manager_.DebugCheckConstraint(ct));
2603 const int num_terms = ct.num_terms;
2604 if (num_terms == 0) {
2605 if (ct.ub >= 0)
return true;
2606 return integer_trail_->ReportConflict({});
2609 std::unique_ptr<IntegerSumLE128> cp_constraint(
2614 if (!cp_constraint->PropagateAtLevelZero())
return false;
2615 if (trail_->CurrentDecisionLevel() == 0)
return true;
2619 const int64_t stamp = integer_trail_->num_enqueues();
2620 const bool no_conflict = cp_constraint->Propagate();
2621 if (no_conflict && integer_trail_->num_enqueues() == stamp)
return true;
2623 const int64_t current_size =
2624 cumulative_optimal_constraint_sizes_.empty()
2626 : cumulative_optimal_constraint_sizes_.back();
2627 optimal_constraints_.push_back(std::move(cp_constraint));
2628 cumulative_optimal_constraint_sizes_.push_back(current_size + num_terms);
2629 rev_optimal_constraints_size_ = optimal_constraints_.size();
2647bool LinearProgrammingConstraint::PropagateExactLpReason() {
2649 integer_reason_.clear();
2650 deductions_.clear();
2651 deductions_reason_.clear();
2656 const RowIndex num_rows = simplex_.GetProblemNumRows();
2657 tmp_lp_multipliers_.clear();
2658 for (RowIndex row(0); row < num_rows; ++row) {
2659 const double value = -simplex_.GetDualValue(row);
2660 if (std::abs(value) < kZeroTolerance)
continue;
2661 tmp_lp_multipliers_.push_back({row, scaler_.UnscaleDualValue(row, value)});
2667 if (tmp_lp_multipliers_.empty())
return true;
2671 bool take_objective_into_account =
true;
2672 if (objective_cp_is_part_of_lp_) {
2675 CHECK_EQ(integer_objective_.size(), 1);
2676 CHECK_EQ(integer_objective_[0].first, mirror_lp_variable_[objective_cp_]);
2677 CHECK_EQ(integer_objective_[0].second, IntegerValue(1));
2679 take_objective_into_account =
false;
2682 IntegerValue scaling = 0;
2683 IgnoreTrivialConstraintMultipliers(&tmp_lp_multipliers_);
2684 ScaleMultipliers(tmp_lp_multipliers_, take_objective_into_account, &scaling,
2685 &tmp_integer_multipliers_);
2687 VLOG(1) << simplex_.GetProblemStatus();
2688 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2693 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2694 tmp_integer_multipliers_, &tmp_scattered_vector_, &rc_ub));
2696 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term =
2698 if (take_objective_into_account) {
2701 const IntegerValue obj_scale = scaling;
2705 tmp_coeffs_.clear();
2706 for (
const auto [col, coeff] : integer_objective_) {
2707 tmp_cols_.push_back(col);
2708 tmp_coeffs_.push_back(coeff);
2710 CHECK(tmp_scattered_vector_
2711 .AddLinearExpressionMultiple</*check_overflow=*/false>(
2712 obj_scale, tmp_cols_, tmp_coeffs_, objective_infinity_norm_));
2713 CHECK(
AddProductTo(-obj_scale, integer_objective_offset_, &rc_ub));
2715 extra_term = {objective_cp_, -obj_scale};
2721 if (take_objective_into_account) {
2722 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2728 LinearConstraint explanation =
2729 tmp_scattered_vector_.ConvertToLinearConstraint(
2730 extended_integer_variables_, rc_ub, extra_term);
2733 if (explanation.num_terms == 0) {
2734 trail_->MutableConflict()->clear();
2735 return explanation.ub >= 0;
2738 SetReducedCostsInConstraintManager(explanation);
2739 return PropagateLpConstraint(std::move(explanation));
2742bool LinearProgrammingConstraint::PropagateExactDualRay() {
2743 IntegerValue scaling;
2745 tmp_lp_multipliers_.clear();
2746 for (RowIndex row(0); row < ray.size(); ++row) {
2747 const double value = ray[row];
2748 if (std::abs(value) < kZeroTolerance)
continue;
2752 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
2754 IgnoreTrivialConstraintMultipliers(&tmp_lp_multipliers_);
2755 ScaleMultipliers(tmp_lp_multipliers_,
false,
2756 &scaling, &tmp_integer_multipliers_);
2758 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2762 IntegerValue new_constraint_ub;
2763 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2764 tmp_integer_multipliers_, &tmp_scattered_vector_, &new_constraint_ub))
2767 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2768 &new_constraint_ub);
2770 LinearConstraint explanation =
2771 tmp_scattered_vector_.ConvertToLinearConstraint(
2772 extended_integer_variables_, new_constraint_ub);
2774 std::string message;
2775 if (VLOG_IS_ON(1)) {
2777 message = absl::StrCat(
"LP exact dual ray not infeasible,",
2778 " implied_lb: ", GetImpliedLowerBound(explanation),
2779 " ub: ", explanation.ub.value());
2783 if (!PropagateLpConstraint(std::move(explanation)))
return false;
2789int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2790 int num_non_basic_with_zero_rc = 0;
2791 const auto reduced_costs = simplex_.GetReducedCosts().const_view();
2792 for (
const glop::ColIndex
i : simplex_.GetNotBasicBitRow()) {
2793 if (reduced_costs[
i] == 0.0) {
2794 num_non_basic_with_zero_rc++;
2797 const int64_t num_cols = simplex_.GetProblemNumCols().value();
2798 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2799 return num_non_basic_with_zero_rc;
2802void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2803 double cp_objective_delta) {
2804 deductions_.clear();
2806 const double lp_objective_delta =
2807 cp_objective_delta / scaler_.ObjectiveScalingFactor();
2808 const int num_vars = integer_variables_.size();
2809 for (
int i = 0;
i < num_vars;
i++) {
2810 const IntegerVariable cp_var = integer_variables_[
i];
2811 const glop::ColIndex lp_var = glop::ColIndex(
i);
2812 const double rc = simplex_.GetReducedCost(lp_var);
2813 const double value = simplex_.GetVariableValue(lp_var);
2815 if (rc == 0.0)
continue;
2816 const double lp_other_bound = value + lp_objective_delta / rc;
2817 const double cp_other_bound =
2818 scaler_.UnscaleVariableValue(lp_var, lp_other_bound);
2820 if (rc > kLpEpsilon) {
2821 const double ub =
ToDouble(integer_trail_->UpperBound(cp_var));
2822 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2827 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2830 }
else if (rc < -kLpEpsilon) {
2831 const double lb =
ToDouble(integer_trail_->LowerBound(cp_var));
2832 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2834 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2835 deductions_.push_back(
2842void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2843 const int num_vars = integer_variables_.size();
2844 if (sum_cost_down_.size() < num_vars) {
2845 sum_cost_down_.resize(num_vars, 0.0);
2846 num_cost_down_.resize(num_vars, 0);
2847 sum_cost_up_.resize(num_vars, 0.0);
2848 num_cost_up_.resize(num_vars, 0);
2849 rc_scores_.resize(num_vars, 0.0);
2853 num_calls_since_reduced_cost_averages_reset_++;
2854 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2855 for (
int i = 0;
i < num_vars;
i++) {
2856 sum_cost_up_[
i] /= 2;
2857 num_cost_up_[
i] /= 2;
2858 sum_cost_down_[
i] /= 2;
2859 num_cost_down_[
i] /= 2;
2861 num_calls_since_reduced_cost_averages_reset_ = 0;
2865 for (
int i = 0;
i < num_vars;
i++) {
2866 const IntegerVariable var = integer_variables_[
i];
2869 if (integer_trail_->IsFixed(var))
continue;
2872 const double rc = lp_reduced_cost_[
i];
2873 if (std::abs(rc) < kCpEpsilon)
continue;
2876 sum_cost_down_[
i] -= rc;
2877 num_cost_down_[
i]++;
2879 sum_cost_up_[
i] += rc;
2886 rc_rev_int_repository_.SetLevel(0);
2887 rc_rev_int_repository_.SetLevel(trail_->CurrentDecisionLevel());
2892 positions_by_decreasing_rc_score_.clear();
2893 for (
int i = 0;
i < num_vars;
i++) {
2898 num_cost_up_[
i] > 0 ? sum_cost_up_[
i] / num_cost_up_[
i] : 0.0;
2899 const double a_down =
2900 num_cost_down_[
i] > 0 ? sum_cost_down_[
i] / num_cost_down_[
i] : 0.0;
2901 if (num_cost_down_[
i] > 0 && num_cost_up_[
i] > 0) {
2902 rc_scores_[
i] = std::min(a_up, a_down);
2904 rc_scores_[
i] = 0.5 * (a_down + a_up);
2909 if (rc_scores_[
i] > 0.0) {
2910 positions_by_decreasing_rc_score_.push_back({-rc_scores_[
i],
i});
2913 std::sort(positions_by_decreasing_rc_score_.begin(),
2914 positions_by_decreasing_rc_score_.end());
2919 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2922IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2924 int selected_index = -1;
2925 const int size = positions_by_decreasing_rc_score_.size();
2926 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2927 for (
int i = rev_rc_start_;
i < size; ++
i) {
2928 const int index = positions_by_decreasing_rc_score_[
i].second;
2929 const IntegerVariable var = integer_variables_[index];
2930 if (integer_trail_->
IsFixed(var))
continue;
2931 selected_index = index;
2936 if (selected_index == -1)
return IntegerLiteral();
2937 const IntegerVariable var = integer_variables_[selected_index];
2943 const IntegerValue ub = integer_trail_->
UpperBound(var);
2944 const IntegerValue value_ceil(
2946 if (value_ceil >= ub) {
2952 const IntegerValue lb = integer_trail_->
LowerBound(var);
2953 const IntegerValue value_floor(
2955 if (value_floor <= lb) {
2962 num_cost_up_[selected_index] > 0
2963 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2965 const double a_down =
2966 num_cost_down_[selected_index] > 0
2967 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2969 if (a_down < a_up) {
2976absl::Span<const glop::ColIndex> LinearProgrammingConstraint::IntegerLpRowCols(
2977 glop::RowIndex row)
const {
2978 const int start = integer_lp_[row].start_in_buffer;
2979 const size_t num_terms =
static_cast<size_t>(integer_lp_[row].num_terms);
2980 return {integer_lp_cols_.data() + start, num_terms};
2983absl::Span<const IntegerValue> LinearProgrammingConstraint::IntegerLpRowCoeffs(
2984 glop::RowIndex row)
const {
2985 const int start = integer_lp_[row].start_in_buffer;
2986 const size_t num_terms =
static_cast<size_t>(integer_lp_[row].num_terms);
2987 return {integer_lp_coeffs_.data() + start, num_terms};
2991 return absl::StrFormat(
"%d rows, %d columns, %d entries", integer_lp_.size(),
2992 integer_variables_.size(), integer_lp_coeffs_.size());
static int64_t GCD64(int64_t x, int64_t y)
void SaveState(T *object)
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
DenseRow * MutableLowerBounds()
DenseRow * MutableUpperBounds()
StrictITISpan< RowIndex, const Fractional > ConstView
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerValue UpperBound(IntegerVariable i) const
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.
const std::vector< ConstraintIndex > & LpConstraints() const
const util_intops::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
All the constraints managed by this class.
A class that stores the collection of all LP constraints in a model.
std::string DimensionString() const
void RegisterWith(Model *model)
void SetLevel(int level) override
ReversibleInterface API.
void AddCutGenerator(CutGenerator generator)
Register a new cut generator with this constraint.
bool AddLinearConstraint(LinearConstraint ct)
bool Propagate() override
PropagatorInterface API.
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
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)
static constexpr SearchBranching LP_SEARCH
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().
bool AddLinearExpressionMultiple(IntegerValue multiplier, absl::Span< const glop::ColIndex > cols, absl::Span< const IntegerValue > coeffs, IntegerValue max_coeff_magnitude)
Force instantations for tests.
void ClearAndResize(int size)
Simple class to add statistics by name and print them at the end.
int CurrentDecisionLevel() const
void assign(size_type n, const value_type &val)
value_type * data()
– Pass-through methods to STL vector ----------------------------------—
void push_back(const value_type &val)
constexpr ColIndex kInvalidCol(-1)
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.
@ ABNORMAL
An error occurred during the solving process.
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)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const 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
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
Overflows and saturated arithmetic.
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)
bool AddIntoOverflow(int64_t x, int64_t *y)
Point BinarySearch(Point x_true, Point x_false, std::function< bool(Point)> f)
int64_t CapProd(int64_t x, int64_t y)
Our cut are always of the form linear_expression <= rhs.
std::vector< CutTerm > terms
bool AppendOneTerm(IntegerVariable var, IntegerValue coeff, double lp_value, IntegerValue lb, IntegerValue ub)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::unique_ptr< IntegerValue[]> coeffs
std::unique_ptr< IntegerVariable[]> vars
absl::Span< const IntegerValue > CoeffsAsSpan() const
absl::Span< const IntegerVariable > VarsAsSpan() const
Same as ModelLpValues for reduced costs.