30#include "absl/algorithm/container.h"
31#include "absl/cleanup/cleanup.h"
32#include "absl/container/flat_hash_map.h"
33#include "absl/log/check.h"
34#include "absl/log/log.h"
35#include "absl/log/vlog_is_on.h"
36#include "absl/numeric/int128.h"
37#include "absl/strings/str_cat.h"
38#include "absl/strings/str_format.h"
39#include "absl/strings/string_view.h"
40#include "absl/types/span.h"
84 for (
const glop::ColIndex col : non_zeros_) {
85 dense_vector_[col] = IntegerValue(0);
87 dense_vector_.resize(size, IntegerValue(0));
89 dense_vector_.assign(size, IntegerValue(0));
91 for (
const glop::ColIndex col : non_zeros_) {
92 is_zeros_[col] =
true;
94 is_zeros_.resize(size,
true);
100 const int64_t add =
CapAdd(value.value(), dense_vector_[col].value());
101 if (add == std::numeric_limits<int64_t>::min() ||
102 add == std::numeric_limits<int64_t>::max())
104 dense_vector_[col] = IntegerValue(add);
105 if (is_sparse_ && is_zeros_[col]) {
106 is_zeros_[col] =
false;
107 non_zeros_.push_back(col);
112template <
bool check_overflow>
114 const IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
115 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude) {
117 if (check_overflow) {
118 const IntegerValue prod =
CapProdI(max_coeff_magnitude, multiplier);
122 IntegerValue* data = dense_vector_.data();
123 const double threshold = 0.1 *
static_cast<double>(dense_vector_.size());
124 const int num_terms = cols.size();
125 if (is_sparse_ &&
static_cast<double>(num_terms) < threshold) {
126 for (
int i = 0;
i < num_terms; ++
i) {
127 const glop::ColIndex col = cols[
i];
128 if (is_zeros_[col]) {
129 is_zeros_[col] =
false;
130 non_zeros_.push_back(col);
132 const IntegerValue product = multiplier * coeffs[
i];
133 if (check_overflow) {
135 data[col.value()].mutable_value())) {
139 data[col.value()] += product;
142 if (
static_cast<double>(non_zeros_.size()) > threshold) {
147 for (
int i = 0;
i < num_terms; ++
i) {
148 const glop::ColIndex col = cols[
i];
149 const IntegerValue product = multiplier * coeffs[
i];
150 if (check_overflow) {
152 data[col.value()].mutable_value())) {
156 data[col.value()] += product;
165 IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
166 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude);
169 IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
170 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude);
173 absl::Span<const IntegerVariable> integer_variables,
174 IntegerValue upper_bound,
175 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term) {
179 for (
const glop::ColIndex col : non_zeros_) {
180 const IntegerValue coeff = dense_vector_[col];
181 if (coeff == 0)
continue;
185 for (
const IntegerValue coeff : dense_vector_) {
186 if (coeff != 0) ++final_size;
189 if (extra_term != std::nullopt) ++final_size;
193 result.
resize(final_size);
198 std::sort(non_zeros_.begin(), non_zeros_.end());
199 for (
const glop::ColIndex col : non_zeros_) {
200 const IntegerValue coeff = dense_vector_[col];
201 if (coeff == 0)
continue;
202 result.
vars[new_size] = integer_variables[col.value()];
203 result.
coeffs[new_size] = coeff;
207 const int size = dense_vector_.size();
208 for (glop::ColIndex col(0); col < size; ++col) {
209 const IntegerValue coeff = dense_vector_[col];
210 if (coeff == 0)
continue;
211 result.
vars[new_size] = integer_variables[col.value()];
212 result.
coeffs[new_size] = coeff;
218 result.
ub = upper_bound;
220 if (extra_term != std::nullopt) {
221 result.
vars[new_size] = extra_term->first;
222 result.
coeffs[new_size] = extra_term->second;
226 CHECK_EQ(new_size, final_size);
232 absl::int128 rhs, absl::Span<const IntegerVariable> integer_variables,
233 absl::Span<const double> lp_solution,
IntegerTrail* integer_trail,
235 result->
terms.clear();
237 absl::Span<const IntegerValue> dense_vector = dense_vector_;
239 std::sort(non_zeros_.begin(), non_zeros_.end());
240 for (
const glop::ColIndex col : non_zeros_) {
241 const IntegerValue coeff = dense_vector[col.value()];
242 if (coeff == 0)
continue;
243 const IntegerVariable var = integer_variables[col.value()];
244 CHECK(result->
AppendOneTerm(var, coeff, lp_solution[col.value()],
249 for (
int col(0); col < dense_vector.size(); ++col) {
250 const IntegerValue coeff = dense_vector[col];
251 if (coeff == 0)
continue;
252 const IntegerVariable var = integer_variables[col];
260std::vector<std::pair<glop::ColIndex, IntegerValue>>
262 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
264 std::sort(non_zeros_.begin(), non_zeros_.end());
265 for (
const glop::ColIndex col : non_zeros_) {
266 const IntegerValue coeff = dense_vector_[col];
267 if (coeff != 0) result.push_back({col, coeff});
270 const int size = dense_vector_.size();
271 for (glop::ColIndex col(0); col < size; ++col) {
272 const IntegerValue coeff = dense_vector_[col];
273 if (coeff != 0) result.push_back({col, coeff});
282 Model* model, absl::Span<const IntegerVariable> vars)
283 : constraint_manager_(model),
286 time_limit_(model->GetOrCreate<
TimeLimit>()),
288 trail_(model->GetOrCreate<
Trail>()),
289 sat_solver_(model->GetOrCreate<
SatSolver>()),
298 cover_cut_helper_(model),
299 integer_rounding_cut_helper_(model),
300 rlt_cut_helper_(model),
301 implied_bounds_processor_({}, integer_trail_,
308 simplex_params_.set_use_dual_simplex(
true);
309 simplex_params_.set_primal_feasibility_tolerance(
310 parameters_.lp_primal_tolerance());
311 simplex_params_.set_dual_feasibility_tolerance(
312 parameters_.lp_dual_tolerance());
313 if (parameters_.use_exact_lp_reason()) {
314 simplex_params_.set_change_status_to_imprecise(
false);
316 simplex_.SetParameters(simplex_params_);
318 simplex_.SetRandom(*random_);
320 compute_reduced_cost_averages_ =
true;
324 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
325 mirror_lp_variable_.resize(integer_trail_->NumIntegerVariables(),
329 CHECK(std::is_sorted(vars.begin(), vars.end()));
335 for (
const IntegerVariable positive_variable : vars) {
337 implied_bounds_processor_.AddLpVariable(positive_variable);
338 (*dispatcher_)[positive_variable] =
this;
340 if (!symmetrizer_->AppearInFoldedProblem(positive_variable))
continue;
342 integer_variables_.push_back(positive_variable);
343 extended_integer_variables_.push_back(positive_variable);
345 mirror_lp_variable_[positive_variable] = col;
350 if (symmetrizer_->HasSymmetry()) {
351 for (
const IntegerVariable var : integer_variables_) {
352 if (!symmetrizer_->IsOrbitSumVar(var))
continue;
353 const int orbit_index = symmetrizer_->OrbitIndex(var);
354 for (
const IntegerVariable var : symmetrizer_->Orbit(orbit_index)) {
355 extended_integer_variables_.push_back(var);
357 mirror_lp_variable_[var] = col;
364 CHECK_EQ(vars.size(), extended_integer_variables_.size());
365 lp_solution_.assign(vars.size(), std::numeric_limits<double>::infinity());
366 lp_reduced_cost_.assign(vars.size(), 0.0);
369 const IntegerVariable max_index = std::max(
370 NegationOf(vars.back()), integer_trail_->NumIntegerVariables() - 1);
371 if (max_index >= expanded_lp_solution_.size()) {
372 expanded_lp_solution_.assign(max_index.value() + 1, 0.0);
374 if (max_index >= expanded_reduced_costs_.size()) {
375 expanded_reduced_costs_.assign(max_index.value() + 1, 0.0);
381 if (!VLOG_IS_ON(1))
return;
382 std::vector<std::pair<std::string, int64_t>> stats;
383 stats.push_back({
"LinearProgrammingConstraint/num_root_level_skips",
384 num_root_level_skips_});
385 stats.push_back({
"LinearProgrammingConstraint/num_root_level_solves",
386 num_root_level_solves_});
387 shared_stats_->AddStats(stats);
391 DCHECK(!lp_constraint_is_registered_);
394 const auto index = constraint_manager_.Add(std::move(ct), &added, &folded);
398 if (added && folded) {
399 const auto& info = constraint_manager_.AllConstraints()[index];
401 const absl::Span<const IntegerVariable> vars = new_ct.
VarsAsSpan();
402 if (!info.ub_is_trivial) {
403 if (!linear_propagator_->AddConstraint({}, vars, new_ct.
CoeffsAsSpan(),
408 if (!info.lb_is_trivial) {
409 tmp_vars_.assign(vars.begin(), vars.end());
410 for (IntegerVariable& var : tmp_vars_) var =
NegationOf(var);
411 if (!linear_propagator_->AddConstraint(
421 DCHECK(!lp_constraint_is_registered_);
422 lp_constraint_is_registered_ =
true;
429 std::sort(integer_objective_.begin(), integer_objective_.end());
430 objective_infinity_norm_ = 0;
431 for (
const auto [col, coeff] : integer_objective_) {
432 constraint_manager_.SetObjectiveCoefficient(integer_variables_[col.value()],
434 objective_infinity_norm_ =
435 std::max(objective_infinity_norm_,
IntTypeAbs(coeff));
443 if (!parameters_.add_lp_constraints_lazily() &&
444 constraint_manager_.num_constraints() < 1e6) {
445 constraint_manager_.AddAllConstraintsToLp();
447 if (!CreateLpFromConstraintManager()) {
452 watcher_id_ = watcher_->Register(
this);
453 const int num_vars = integer_variables_.size();
454 orbit_indices_.clear();
455 for (
int i = 0;
i < num_vars;
i++) {
456 const IntegerVariable pos_var = integer_variables_[
i];
457 DCHECK(symmetrizer_->AppearInFoldedProblem(pos_var));
458 watcher_->WatchIntegerVariable(pos_var, watcher_id_,
i);
459 if (symmetrizer_->IsOrbitSumVar(pos_var)) {
460 orbit_indices_.push_back(symmetrizer_->OrbitIndex(pos_var));
463 if (objective_is_defined_) {
464 watcher_->WatchUpperBound(objective_cp_, watcher_id_);
466 watcher_->SetPropagatorPriority(watcher_id_, 2);
467 watcher_->AlwaysCallAtLevelZero(watcher_id_);
471 integer_trail_->RegisterReversibleClass(
this);
472 watcher_->RegisterReversibleInt(watcher_id_, &rev_optimal_constraints_size_);
475glop::ColIndex LinearProgrammingConstraint::GetMirrorVariable(
476 IntegerVariable positive_variable) {
478 return mirror_lp_variable_[positive_variable];
482 IntegerValue coeff) {
483 CHECK(!lp_constraint_is_registered_);
484 objective_is_defined_ =
true;
486 if (ivar != pos_var) coeff = -coeff;
487 integer_objective_.push_back({GetMirrorVariable(pos_var), coeff});
503bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
506 integer_lp_cols_.clear();
507 integer_lp_coeffs_.clear();
509 infinity_norms_.clear();
510 const auto& all_constraints = constraint_manager_.
AllConstraints();
511 for (
const auto index : constraint_manager_.
LpConstraints()) {
514 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
518 integer_lp_.
push_back(LinearConstraintInternal());
519 LinearConstraintInternal& new_ct = integer_lp_.back();
522 new_ct.lb_is_trivial = all_constraints[index].lb_is_trivial;
523 new_ct.ub_is_trivial = all_constraints[index].ub_is_trivial;
525 IntegerValue infinity_norm = 0;
526 infinity_norm = std::max(infinity_norm,
IntTypeAbs(ct.
lb));
527 infinity_norm = std::max(infinity_norm,
IntTypeAbs(ct.
ub));
528 new_ct.start_in_buffer = integer_lp_cols_.size();
533 new_ct.num_terms = size;
534 for (
int i = 0;
i < size; ++
i) {
536 const IntegerVariable var = ct.
vars[
i];
537 const IntegerValue coeff = ct.
coeffs[
i];
538 infinity_norm = std::max(infinity_norm,
IntTypeAbs(coeff));
539 integer_lp_cols_.push_back(GetMirrorVariable(var));
540 integer_lp_coeffs_.push_back(coeff);
542 infinity_norms_.
push_back(infinity_norm);
545 DCHECK(std::is_sorted(
546 integer_lp_cols_.data() + new_ct.start_in_buffer,
547 integer_lp_cols_.data() + new_ct.start_in_buffer + new_ct.num_terms));
553 objective_infinity_norm_ = 0;
554 for (
const auto& entry : integer_objective_) {
555 const IntegerVariable var = integer_variables_[entry.first.value()];
556 if (integer_trail_->IsFixedAtLevelZero(var)) {
557 integer_objective_offset_ +=
558 entry.second * integer_trail_->LevelZeroLowerBound(var);
561 objective_infinity_norm_ =
562 std::max(objective_infinity_norm_,
IntTypeAbs(entry.second));
563 integer_objective_[new_size++] = entry;
565 integer_objective_.resize(new_size);
566 objective_infinity_norm_ =
567 std::max(objective_infinity_norm_,
IntTypeAbs(integer_objective_offset_));
572 ComputeIntegerLpScalingFactors();
578 scaler_.ConfigureFromFactors(row_factors_, col_factors_);
579 scaler_.AverageCostScaling(&obj_with_slack_);
580 scaler_.ContainOneBoundScaling(simplex_.MutableLowerBounds(),
581 simplex_.MutableUpperBounds());
584 UpdateBoundsOfLpVariables();
589 if (parameters_.polish_lp_solution()) {
590 simplex_.ClearIntegralityScales();
591 const int num_vars = integer_variables_.size();
592 for (
int i = 0;
i < num_vars; ++
i) {
593 const IntegerVariable cp_var = integer_variables_[
i];
594 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(cp_var);
595 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(cp_var);
596 if (lb != 0 || ub != 1)
continue;
597 simplex_.SetIntegralityScale(
599 1.0 / scaler_.VariableScalingFactor(glop::ColIndex(
i)));
603 VLOG(3) <<
"LP relaxation: " << integer_lp_.size() <<
" x "
604 << integer_variables_.size() <<
". "
605 << constraint_manager_.AllConstraints().size()
606 <<
" Managed constraints.";
612void LinearProgrammingConstraint::ComputeIntegerLpScalingFactors() {
613 const int num_rows = integer_lp_.size();
614 const int num_cols = integer_variables_.size();
617 const double infinity = std::numeric_limits<double>::infinity();
618 row_factors_.assign(num_rows, 1.0);
619 col_factors_.assign(num_cols, 1.0);
622 IntegerValue* coeffs = integer_lp_coeffs_.data();
623 glop::ColIndex* cols = integer_lp_cols_.data();
624 double* row_factors = row_factors_.data();
625 double* col_factors = col_factors_.data();
627 col_min_.assign(num_cols, infinity);
628 col_max_.assign(num_cols, 0.0);
629 double* col_min = col_min_.data();
630 double* col_max = col_max_.data();
632 for (
int i = 0;
i < 4; ++
i) {
634 for (
int row = 0; row < num_rows; ++row) {
635 double min_scaled = +infinity;
636 double max_scaled = 0.0;
637 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
638 for (
int i = 0;
i < ct.num_terms; ++
i) {
639 const int index = ct.start_in_buffer +
i;
640 const int col = cols[index].value();
641 const double coeff =
static_cast<double>(coeffs[index].value());
642 const double scaled_magnitude = col_factors[col] * std::abs(coeff);
643 min_scaled = std::min(min_scaled, scaled_magnitude);
644 max_scaled = std::max(max_scaled, scaled_magnitude);
647 if (ct.num_terms == 0)
continue;
648 const Fractional factor(std::sqrt(max_scaled * min_scaled));
649 row_factors[row] = 1.0 / factor;
653 for (
int row = 0; row < num_rows; ++row) {
654 const double row_factor = row_factors[row];
655 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
656 for (
int i = 0;
i < ct.num_terms; ++
i) {
657 const int index = ct.start_in_buffer +
i;
658 const int col = cols[index].value();
659 const double coeff =
static_cast<double>(coeffs[index].value());
660 const double scaled_magnitude = row_factor * std::abs(coeff);
661 col_min[col] = std::min(col_min[col], scaled_magnitude);
662 col_max[col] = std::max(col_max[col], scaled_magnitude);
665 for (
int col = 0; col < num_cols; ++col) {
666 if (col_min[col] == infinity)
continue;
667 col_factors[col] = 1.0 / std::sqrt(col_min[col] * col_max[col]);
670 col_min[col] = infinity;
676 for (
int row = 0; row < num_rows; ++row) {
677 double max_scaled = 0.0;
678 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
679 for (
int i = 0;
i < ct.num_terms; ++
i) {
680 const int index = ct.start_in_buffer +
i;
681 const int col = cols[index].value();
682 const double coeff =
static_cast<double>(coeffs[index].value());
683 const double scaled_magnitude = col_factors[col] * std::abs(coeff);
684 max_scaled = std::max(max_scaled, scaled_magnitude);
686 if (ct.num_terms == 0)
continue;
687 row_factors[row] = 1.0 / max_scaled;
691 for (
int row = 0; row < num_rows; ++row) {
692 const double row_factor = row_factors[row];
693 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
694 for (
int i = 0;
i < ct.num_terms; ++
i) {
695 const int index = ct.start_in_buffer +
i;
696 const int col = cols[index].value();
697 const double coeff =
static_cast<double>(coeffs[index].value());
698 const double scaled_magnitude = row_factor * std::abs(coeff);
699 col_max[col] = std::max(col_max[col], scaled_magnitude);
702 for (
int col = 0; col < num_cols; ++col) {
703 if (col_max[col] == 0)
continue;
704 col_factors[col] = 1.0 / col_max[col];
708void LinearProgrammingConstraint::FillLpData() {
709 const int num_rows = integer_lp_.size();
710 const int num_cols = integer_variables_.size();
711 IntegerValue* coeffs = integer_lp_coeffs_.data();
712 glop::ColIndex* cols = integer_lp_cols_.data();
713 double* row_factors = row_factors_.data();
714 double* col_factors = col_factors_.data();
717 glop::CompactSparseMatrix* data = simplex_.MutableTransposedMatrixWithSlack();
718 data->Reset(glop::RowIndex(num_cols + num_rows));
719 for (
int row = 0; row < num_rows; ++row) {
720 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
721 const double row_factor = row_factors[row];
722 for (
int i = 0;
i < ct.num_terms; ++
i) {
723 const int index = ct.start_in_buffer +
i;
724 const int col = cols[index].value();
725 const double coeff =
static_cast<double>(coeffs[index].value());
726 const double scaled_coeff = row_factor * col_factors[col] * coeff;
727 data->AddEntryToCurrentColumn(RowIndex(col), scaled_coeff);
731 data->AddEntryToCurrentColumn(RowIndex(num_cols + row), 1.0);
734 data->CloseCurrentColumn();
736 DCHECK_EQ(data->num_cols(), integer_lp_.size());
739 const glop::ColIndex num_cols_with_slacks(num_rows + num_cols);
740 obj_with_slack_.assign(num_cols_with_slacks, 0.0);
741 for (
const auto [col, value] : integer_objective_) {
742 obj_with_slack_[col] =
ToDouble(value) * col_factors[col.value()];
746 simplex_.MutableLowerBounds()->resize(num_cols_with_slacks);
747 simplex_.MutableUpperBounds()->resize(num_cols_with_slacks);
748 Fractional* lb_with_slack = simplex_.MutableLowerBounds()->data();
749 Fractional* ub_with_slack = simplex_.MutableUpperBounds()->data();
750 const double infinity = std::numeric_limits<double>::infinity();
751 for (
int row = 0; row < integer_lp_.size(); ++row) {
752 const LinearConstraintInternal& ct = integer_lp_[glop::RowIndex(row)];
758 const double factor = row_factors[row];
759 lb_with_slack[num_cols + row] =
760 ct.ub_is_trivial ? -infinity :
ToDouble(-ct.ub) * factor;
761 ub_with_slack[num_cols + row] =
762 ct.lb_is_trivial ? +infinity :
ToDouble(-ct.lb) * factor;
771 const int num_vars = integer_variables_.size();
772 for (
int i = 0;
i < num_vars;
i++) {
773 const IntegerVariable cp_var = integer_variables_[
i];
774 const double factor = col_factors[
i];
776 ToDouble(integer_trail_->LevelZeroLowerBound(cp_var)) * factor;
778 ToDouble(integer_trail_->LevelZeroUpperBound(cp_var)) * factor;
782void LinearProgrammingConstraint::FillReducedCostReasonIn(
784 std::vector<IntegerLiteral>* integer_reason) {
785 integer_reason->clear();
786 const int num_vars = integer_variables_.size();
787 for (
int i = 0;
i < num_vars;
i++) {
788 const double rc = reduced_costs[glop::ColIndex(
i)];
789 if (rc > kLpEpsilon) {
790 integer_reason->push_back(
791 integer_trail_->LowerBoundAsLiteral(integer_variables_[
i]));
792 }
else if (rc < -kLpEpsilon) {
793 integer_reason->push_back(
794 integer_trail_->UpperBoundAsLiteral(integer_variables_[
i]));
798 integer_trail_->RemoveLevelZeroBounds(integer_reason);
803 if (level == 0) rev_optimal_constraints_size_ = 0;
804 optimal_constraints_.resize(rev_optimal_constraints_size_);
805 cumulative_optimal_constraint_sizes_.resize(rev_optimal_constraints_size_);
806 if (lp_solution_is_set_ && level < lp_solution_level_) {
807 lp_solution_is_set_ =
false;
810 if (level < previous_level_) {
811 lp_at_optimal_ =
false;
812 lp_objective_lower_bound_ = -std::numeric_limits<double>::infinity();
814 previous_level_ = level;
823 if (level == 0 && !lp_solution_is_set_ && !level_zero_lp_solution_.empty()) {
824 lp_solution_is_set_ =
true;
825 lp_solution_ = level_zero_lp_solution_;
826 lp_solution_level_ = 0;
830 if (expanded_lp_solution_.size() < integer_trail_->NumIntegerVariables()) {
831 expanded_lp_solution_.resize(integer_trail_->NumIntegerVariables());
833 for (IntegerVariable
i(0);
i < integer_trail_->NumIntegerVariables(); ++
i) {
834 if (integer_trail_->IsFixed(
i)) {
835 expanded_lp_solution_[
i] =
ToDouble(integer_trail_->LowerBound(
i));
838 for (
int i = 0;
i < lp_solution_.size();
i++) {
839 const IntegerVariable var = extended_integer_variables_[
i];
840 expanded_lp_solution_[var] = lp_solution_[
i];
841 expanded_lp_solution_[
NegationOf(var)] = -lp_solution_[
i];
847 cut_generators_.push_back(std::move(generator));
851 const std::vector<int>& watch_indices) {
852 if (!enabled_)
return true;
862 if (!cumulative_optimal_constraint_sizes_.empty()) {
863 const double current_size =
864 static_cast<double>(cumulative_optimal_constraint_sizes_.back());
865 const double low_limit = 1e7;
866 if (current_size > low_limit) {
869 const double num_enqueues =
static_cast<double>(integer_trail_->Index());
870 if ((current_size - low_limit) > 100 * num_enqueues)
return true;
874 if (!lp_solution_is_set_ || num_force_lp_call_on_next_propagate_ > 0) {
884 if (trail_->CurrentDecisionLevel() == 0 && !lp_at_level_zero_is_final_) {
890 for (
const int index : watch_indices) {
892 ToDouble(integer_trail_->LowerBound(integer_variables_[index]));
894 ToDouble(integer_trail_->UpperBound(integer_variables_[index]));
895 const double value = lp_solution_[index];
896 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
917 glop::ColIndex var) {
922 IntegerVariable variable)
const {
923 return lp_solution_[mirror_lp_variable_[variable].value()];
926void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
927 const int num_vars = integer_variables_.size();
930 for (
int i = 0;
i < num_vars;
i++) {
931 const IntegerVariable cp_var = integer_variables_[
i];
933 static_cast<double>(integer_trail_->
LowerBound(cp_var).value());
935 static_cast<double>(integer_trail_->
UpperBound(cp_var).value());
937 lb_with_slack[
i] = lb * factor;
938 ub_with_slack[
i] = ub * factor;
942bool LinearProgrammingConstraint::SolveLp() {
945 lp_at_level_zero_is_final_ =
false;
948 const double unscaling_factor = 1.0 / scaler_.ObjectiveScalingFactor();
949 const double offset_before_unscaling =
950 ToDouble(integer_objective_offset_) * scaler_.ObjectiveScalingFactor();
951 auto status = simplex_.MinimizeFromTransposedMatrixWithSlack(
952 obj_with_slack_, unscaling_factor, offset_before_unscaling, time_limit_);
953 DCHECK_EQ(simplex_.GetProblemNumRows(), integer_lp_.size());
957 VLOG(2) <<
"The LP solver returned abnormal, resolving from scratch";
958 simplex_.ClearStateForNextSolve();
959 status = simplex_.MinimizeFromTransposedMatrixWithSlack(
960 obj_with_slack_, unscaling_factor, offset_before_unscaling,
964 state_ = simplex_.GetState();
965 total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
967 VLOG(2) <<
"The LP solver encountered an error: " << status.error_message();
968 simplex_.ClearStateForNextSolve();
971 average_degeneracy_.AddData(CalculateDegeneracy());
972 if (average_degeneracy_.CurrentAverage() >= 1000.0) {
973 VLOG(2) <<
"High average degeneracy: "
974 << average_degeneracy_.CurrentAverage();
977 const int status_as_int =
static_cast<int>(simplex_.GetProblemStatus());
978 if (status_as_int >= num_solves_by_status_.size()) {
979 num_solves_by_status_.resize(status_as_int + 1);
982 num_solves_by_status_[status_as_int]++;
983 VLOG(2) <<
DimensionString() <<
" lvl:" << trail_->CurrentDecisionLevel()
984 <<
" " << simplex_.GetProblemStatus()
985 <<
" iter:" << simplex_.GetNumberOfIterations()
986 <<
" obj:" << simplex_.GetObjectiveValue() <<
" scaled:"
987 << objective_definition_->ScaleObjective(
988 simplex_.GetObjectiveValue());
992 lp_objective_lower_bound_ = simplex_.GetObjectiveValue();
999 parameters_.stop_after_root_propagation()) {
1000 lp_solution_is_set_ =
true;
1001 lp_solution_level_ = trail_->CurrentDecisionLevel();
1002 const int num_vars = integer_variables_.size();
1003 const auto reduced_costs = simplex_.GetReducedCosts().const_view();
1004 for (
int i = 0;
i < num_vars;
i++) {
1005 const glop::ColIndex col(
i);
1006 const IntegerVariable var = integer_variables_[
i];
1009 lp_solution_[
i] = value;
1010 expanded_lp_solution_[var] = value;
1011 expanded_lp_solution_[
NegationOf(var)] = -value;
1014 scaler_.UnscaleReducedCost(col, reduced_costs[col]);
1015 lp_reduced_cost_[
i] = rc;
1016 expanded_reduced_costs_[var] = rc;
1017 expanded_reduced_costs_[
NegationOf(var)] = -rc;
1022 for (
const int orbit_index : orbit_indices_) {
1023 const IntegerVariable sum_var = symmetrizer_->OrbitSumVar(orbit_index);
1024 const double lp_value = expanded_lp_solution_[sum_var];
1025 const absl::Span<const IntegerVariable> orbit =
1026 symmetrizer_->Orbit(orbit_index);
1034 bool to_lower =
false;
1035 bool to_upper =
false;
1036 if (trail_->CurrentDecisionLevel() > 0) {
1038 static_cast<double>(integer_trail_->LowerBound(sum_var).value());
1040 static_cast<double>(integer_trail_->UpperBound(sum_var).value());
1041 if (std::abs(lp_value - lb) < 1e-2) {
1043 }
else if (std::abs(lp_value - ub) < 1e-2) {
1061 const double even_split = lp_value /
static_cast<double>(orbit.size());
1065 const double new_rc = expanded_reduced_costs_[sum_var];
1067 for (
const IntegerVariable var : orbit) {
1068 const glop::ColIndex col = GetMirrorVariable(var);
1070 double new_value = even_split;
1073 static_cast<double>(integer_trail_->LowerBound(var).value());
1074 }
else if (to_upper) {
1076 static_cast<double>(integer_trail_->UpperBound(var).value());
1079 lp_solution_[col.value()] = new_value;
1080 expanded_lp_solution_[var] = new_value;
1081 expanded_lp_solution_[
NegationOf(var)] = -new_value;
1083 lp_reduced_cost_[col.value()] = new_rc;
1084 expanded_reduced_costs_[var] = new_rc;
1085 expanded_reduced_costs_[
NegationOf(var)] = -new_rc;
1090 lp_solution_is_integer_ =
true;
1091 for (
const double value : lp_solution_) {
1092 if (std::abs(value - std::round(value)) > kCpEpsilon) {
1093 lp_solution_is_integer_ =
false;
1098 if (lp_solution_level_ == 0) {
1099 level_zero_lp_solution_ = lp_solution_;
1106bool LinearProgrammingConstraint::AnalyzeLp() {
1109 if (parameters_.use_exact_lp_reason()) {
1110 return PropagateExactDualRay();
1112 FillReducedCostReasonIn(simplex_.GetDualRayRowCombination(),
1114 return integer_trail_->ReportConflict(integer_reason_);
1118 UpdateSimplexIterationLimit(10, 1000);
1122 if (objective_is_defined_ &&
1127 if (parameters_.use_exact_lp_reason()) {
1128 if (!PropagateExactLpReason())
return false;
1131 if (VLOG_IS_ON(2)) {
1132 const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1133 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1134 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1135 const IntegerValue propagated_lb =
1136 integer_trail_->LowerBound(objective_cp_);
1137 if (approximate_new_lb > propagated_lb) {
1138 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1139 <<
ToDouble(integer_trail_->UpperBound(objective_cp_))
1140 <<
" ] approx_lb += "
1141 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1142 << integer_trail_->UpperBound(objective_cp_) - propagated_lb;
1149 FillReducedCostReasonIn(simplex_.GetReducedCosts(), &integer_reason_);
1150 const double objective_cp_ub =
1151 ToDouble(integer_trail_->UpperBound(objective_cp_));
1152 const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1153 ReducedCostStrengtheningDeductions(objective_cp_ub -
1154 relaxed_optimal_objective);
1155 if (!deductions_.empty()) {
1156 deductions_reason_ = integer_reason_;
1157 deductions_reason_.push_back(
1158 integer_trail_->UpperBoundAsLiteral(objective_cp_));
1162 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1163 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1164 if (approximate_new_lb > integer_trail_->LowerBound(objective_cp_)) {
1165 const IntegerLiteral deduction =
1167 if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
1173 if (!deductions_.empty()) {
1174 const int trail_index_with_same_reason = integer_trail_->Index();
1175 for (
const IntegerLiteral deduction : deductions_) {
1176 if (!integer_trail_->Enqueue(deduction, {}, deductions_reason_,
1177 trail_index_with_same_reason)) {
1186 if (compute_reduced_cost_averages_ &&
1188 CHECK(lp_solution_is_set_);
1189 UpdateAverageReducedCosts();
1198 if (objective_is_defined_ &&
1199 objective_definition_->objective_var == objective_cp_ &&
1200 trail_->CurrentDecisionLevel() == 0) {
1201 shared_response_manager_->UpdateInnerObjectiveBounds(
1202 model_->Name(), integer_trail_->LowerBound(objective_cp_),
1203 integer_trail_->UpperBound(objective_cp_));
1215bool LinearProgrammingConstraint::PreprocessCut(IntegerVariable first_slack,
1218 cut->ComplementForPositiveCoefficients();
1220 problem_proven_infeasible_by_cuts_ =
true;
1227 for (
const CutTerm& term : cut->terms) {
1228 reachable_.Add(term.coeff.value());
1232 if (cut->rhs < absl::int128(reachable_.LastValue()) &&
1233 !reachable_.MightBeReachable(
static_cast<int64_t
>(cut->rhs))) {
1234 problem_proven_infeasible_by_cuts_ =
true;
1238 bool some_fixed_terms =
false;
1239 bool some_fractional_positions =
false;
1240 for (CutTerm& term : cut->terms) {
1241 const absl::int128 magnitude128 = term.coeff.value();
1242 const absl::int128 range =
1243 absl::int128(term.bound_diff.value()) * magnitude128;
1245 IntegerValue new_diff = term.bound_diff;
1246 if (range > cut->rhs) {
1247 new_diff =
static_cast<int64_t
>(cut->rhs / magnitude128);
1251 absl::int128 rest128 =
1252 cut->rhs - absl::int128(new_diff.value()) * magnitude128;
1253 while (rest128 < absl::int128(reachable_.LastValue()) &&
1254 !reachable_.MightBeReachable(
static_cast<int64_t
>(rest128))) {
1255 ++total_num_eq_propagations_;
1256 CHECK_GT(new_diff, 0);
1258 rest128 += magnitude128;
1262 if (new_diff < term.bound_diff) {
1263 term.bound_diff = new_diff;
1265 const IntegerVariable var = term.expr_vars[0];
1266 if (var < first_slack) {
1268 ++total_num_cut_propagations_;
1271 if (term.expr_coeffs[0] == 1) {
1273 if (!integer_trail_->Enqueue(
1275 var, term.bound_diff - term.expr_offset),
1277 problem_proven_infeasible_by_cuts_ =
true;
1281 CHECK_EQ(term.expr_coeffs[0], -1);
1283 if (!integer_trail_->Enqueue(
1285 var, term.expr_offset - term.bound_diff),
1287 problem_proven_infeasible_by_cuts_ =
true;
1295 const int slack_index = (var.value() - first_slack.value()) / 2;
1296 const glop::RowIndex row = tmp_slack_rows_[slack_index];
1297 if (term.expr_coeffs[0] == 1) {
1299 const IntegerValue new_ub = term.bound_diff - term.expr_offset;
1300 if (constraint_manager_.UpdateConstraintUb(row, new_ub)) {
1301 integer_lp_[row].ub = new_ub;
1305 CHECK_EQ(term.expr_coeffs[0], -1);
1306 const IntegerValue new_lb = term.expr_offset - term.bound_diff;
1307 if (constraint_manager_.UpdateConstraintLb(row, new_lb)) {
1308 integer_lp_[row].lb = new_lb;
1314 if (term.bound_diff == 0) {
1315 some_fixed_terms =
true;
1317 if (term.IsFractional()) {
1318 some_fractional_positions =
true;
1324 if (some_fixed_terms) {
1326 for (
const CutTerm& term : cut->terms) {
1327 if (term.bound_diff == 0)
continue;
1328 cut->terms[new_size++] = term;
1330 cut->terms.resize(new_size);
1332 return some_fractional_positions;
1335bool LinearProgrammingConstraint::AddCutFromConstraints(
1336 absl::string_view name,
1337 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers) {
1347 IntegerValue cut_ub;
1348 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
1350 ++num_cut_overflows_;
1351 VLOG(1) <<
"Issue, overflow!";
1362 tmp_scattered_vector_.ConvertToCutData(
1363 cut_ub.value(), extended_integer_variables_, lp_solution_, integer_trail_,
1368 ImpliedBoundsProcessor* ib_processor =
nullptr;
1370 bool some_ints =
false;
1371 bool some_fractional_positions =
false;
1372 for (
const CutTerm& term : base_ct_.terms) {
1373 if (term.bound_diff > 1) some_ints =
true;
1374 if (term.IsFractional()) {
1375 some_fractional_positions =
true;
1380 if (!some_fractional_positions)
return false;
1381 if (some_ints) ib_processor = &implied_bounds_processor_;
1386 const IntegerVariable first_slack(expanded_lp_solution_.size());
1387 CHECK_EQ(first_slack.value() % 2, 0);
1388 tmp_slack_rows_.clear();
1389 for (
const auto [row, coeff] : integer_multipliers) {
1390 if (integer_lp_[row].lb == integer_lp_[row].ub)
continue;
1393 entry.coeff = coeff > 0 ? coeff : -coeff;
1394 entry.lp_value = 0.0;
1395 entry.bound_diff = integer_lp_[row].ub - integer_lp_[row].lb;
1396 entry.expr_vars[0] =
1397 first_slack + 2 * IntegerVariable(tmp_slack_rows_.size());
1398 entry.expr_coeffs[1] = 0;
1399 const double activity = scaler_.UnscaleConstraintActivity(
1400 row, simplex_.GetConstraintActivity(row));
1403 entry.lp_value =
ToDouble(integer_lp_[row].ub) - activity;
1404 entry.expr_coeffs[0] = IntegerValue(-1);
1405 entry.expr_offset = integer_lp_[row].ub;
1408 entry.lp_value = activity -
ToDouble(integer_lp_[row].lb);
1409 entry.expr_coeffs[0] = IntegerValue(1);
1410 entry.expr_offset = -integer_lp_[row].lb;
1413 base_ct_.terms.push_back(entry);
1414 tmp_slack_rows_.push_back(row);
1418 if (!PreprocessCut(first_slack, &base_ct_))
return false;
1421 if (base_ct_.rhs == 1)
return false;
1425 double activity = 0.0;
1427 for (
const CutTerm& term : base_ct_.terms) {
1428 const double coeff =
ToDouble(term.coeff);
1429 activity += term.lp_value * coeff;
1430 norm += coeff * coeff;
1432 if (std::abs(activity -
static_cast<double>(base_ct_.rhs)) / norm > 1e-4) {
1433 VLOG(1) <<
"Cut not tight " << activity
1434 <<
" <= " <<
static_cast<double>(base_ct_.rhs);
1439 bool at_least_one_added =
false;
1440 DCHECK(base_ct_.AllCoefficientsArePositive());
1445 if (integer_multipliers.size() == 1 && parameters_.add_rlt_cuts()) {
1446 if (rlt_cut_helper_.TrySimpleSeparation(base_ct_)) {
1447 at_least_one_added |= PostprocessAndAddCut(
1448 absl::StrCat(name,
"_RLT"), rlt_cut_helper_.Info(), first_slack,
1449 rlt_cut_helper_.cut());
1454 if (ib_processor !=
nullptr) {
1455 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1456 ib_processor =
nullptr;
1463 cover_cut_helper_.ClearCache();
1465 if (cover_cut_helper_.TrySingleNodeFlow(base_ct_, ib_processor)) {
1466 at_least_one_added |= PostprocessAndAddCut(
1467 absl::StrCat(name,
"_FF"), cover_cut_helper_.Info(), first_slack,
1468 cover_cut_helper_.cut());
1470 if (cover_cut_helper_.TrySimpleKnapsack(base_ct_, ib_processor)) {
1471 at_least_one_added |= PostprocessAndAddCut(
1472 absl::StrCat(name,
"_K"), cover_cut_helper_.Info(), first_slack,
1473 cover_cut_helper_.cut());
1478 if (cover_cut_helper_.TryWithLetchfordSouliLifting(base_ct_,
1480 at_least_one_added |= PostprocessAndAddCut(
1481 absl::StrCat(name,
"_KL"), cover_cut_helper_.Info(), first_slack,
1482 cover_cut_helper_.cut());
1488 base_ct_.ComplementForSmallerLpValues();
1490 RoundingOptions options;
1491 options.max_scaling = parameters_.max_integer_rounding_scaling();
1492 options.use_ib_before_heuristic =
false;
1493 if (integer_rounding_cut_helper_.ComputeCut(options, base_ct_,
1495 at_least_one_added |= PostprocessAndAddCut(
1496 absl::StrCat(name,
"_R"), integer_rounding_cut_helper_.Info(),
1497 first_slack, integer_rounding_cut_helper_.cut());
1500 options.use_ib_before_heuristic =
true;
1501 options.prefer_positive_ib =
false;
1502 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.ComputeCut(
1503 options, base_ct_, ib_processor)) {
1504 at_least_one_added |= PostprocessAndAddCut(
1505 absl::StrCat(name,
"_RB"), integer_rounding_cut_helper_.Info(),
1506 first_slack, integer_rounding_cut_helper_.cut());
1509 options.use_ib_before_heuristic =
true;
1510 options.prefer_positive_ib =
true;
1511 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.ComputeCut(
1512 options, base_ct_, ib_processor)) {
1513 at_least_one_added |= PostprocessAndAddCut(
1514 absl::StrCat(name,
"_RBP"), integer_rounding_cut_helper_.Info(),
1515 first_slack, integer_rounding_cut_helper_.cut());
1519 return at_least_one_added;
1522bool LinearProgrammingConstraint::PostprocessAndAddCut(
1523 const std::string& name,
const std::string& info,
1524 IntegerVariable first_slack,
const CutData& cut) {
1525 if (cut.rhs > absl::int128(std::numeric_limits<int64_t>::max()) ||
1526 cut.rhs < absl::int128(std::numeric_limits<int64_t>::min())) {
1527 VLOG(2) <<
"RHS overflow " << name <<
" " << info;
1528 ++num_cut_overflows_;
1536 if (cut.ComputeViolation() < 1e-4) {
1537 VLOG(3) <<
"Bad cut " << name <<
" " << info;
1543 tmp_scattered_vector_.ClearAndResize(extended_integer_variables_.size());
1544 IntegerValue cut_ub =
static_cast<int64_t
>(cut.rhs);
1545 for (
const CutTerm& term : cut.terms) {
1546 if (term.coeff == 0)
continue;
1547 if (!
AddProductTo(-term.coeff, term.expr_offset, &cut_ub)) {
1548 VLOG(2) <<
"Overflow in conversion";
1549 ++num_cut_overflows_;
1552 for (
int i = 0;
i < 2; ++
i) {
1553 if (term.expr_coeffs[
i] == 0)
continue;
1554 const IntegerVariable var = term.expr_vars[
i];
1555 const IntegerValue coeff =
1556 CapProd(term.coeff.value(), term.expr_coeffs[
i].value());
1558 VLOG(2) <<
"Overflow in conversion";
1559 ++num_cut_overflows_;
1564 if (var < first_slack) {
1567 tmp_scattered_vector_.Add(col, coeff);
1569 tmp_scattered_vector_.Add(col, -coeff);
1574 const int slack_index = (var.value() - first_slack.value()) / 2;
1575 const glop::RowIndex row = tmp_slack_rows_[slack_index];
1576 if (!tmp_scattered_vector_.AddLinearExpressionMultiple(
1577 coeff, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
1578 infinity_norms_[row])) {
1579 VLOG(2) <<
"Overflow in slack removal";
1580 ++num_cut_overflows_;
1590 LinearConstraint converted_cut =
1591 tmp_scattered_vector_.ConvertToLinearConstraint(
1592 extended_integer_variables_, cut_ub);
1597 top_n_cuts_.AddCut(std::move(converted_cut), name, expanded_lp_solution_);
1600 return constraint_manager_.AddCut(std::move(converted_cut), name, info);
1607void LinearProgrammingConstraint::AddCGCuts() {
1608 std::vector<std::pair<RowIndex, double>> sorted_columns;
1609 const RowIndex num_rows(integer_lp_.size());
1611 for (RowIndex index(0); index < num_rows; ++index) {
1612 const ColIndex basis_col = simplex_.GetBasis(index);
1621 if (basis_col >= integer_variables_.size())
continue;
1626 simplex_.GetVariableValue(basis_col) /
1627 scaler_.VariableScalingFactorWithSlack(basis_col);
1635 const double fractionality = std::abs(lp_value - std::round(lp_value));
1636 if (fractionality < 0.01)
continue;
1638 const double score = fractionality * (1.0 - fractionality) / norms[index];
1639 sorted_columns.push_back({index, score});
1641 absl::c_sort(sorted_columns, [](
const std::pair<RowIndex, double>& a,
1642 const std::pair<RowIndex, double>&
b) {
1643 return a.second >
b.second;
1647 for (
const auto [index, _] : sorted_columns) {
1648 if (time_limit_->LimitReached())
return;
1653 tmp_lp_multipliers_.clear();
1654 const glop::ScatteredRow& lambda = simplex_.GetUnitRowLeftInverse(index);
1655 if (lambda.non_zeros.empty()) {
1656 for (RowIndex row(0); row < num_rows; ++row) {
1658 if (std::abs(value) < kZeroTolerance)
continue;
1659 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
1662 for (
const ColIndex col : lambda.non_zeros) {
1664 const double value = lambda.values[col];
1665 if (std::abs(value) < kZeroTolerance)
continue;
1666 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
1671 if (tmp_lp_multipliers_.size() <= 1)
continue;
1673 IntegerValue scaling;
1674 for (
int i = 0;
i < 2; ++
i) {
1675 tmp_cg_multipliers_ = tmp_lp_multipliers_;
1681 for (std::pair<RowIndex, double>& p : tmp_cg_multipliers_) {
1682 p.second = -p.second;
1693 IgnoreTrivialConstraintMultipliers(&tmp_cg_multipliers_);
1694 if (tmp_cg_multipliers_.size() <= 1)
continue;
1696 ScaleMultipliers(tmp_cg_multipliers_,
1698 &tmp_integer_multipliers_);
1700 if (AddCutFromConstraints(
"CG", tmp_integer_multipliers_)) {
1707 if (num_added > 10)
break;
1720void LinearProgrammingConstraint::AddObjectiveCut() {
1721 if (integer_objective_.size() <= 1)
return;
1726 const double obj_lp_value = simplex_.GetObjectiveValue();
1727 const IntegerValue obj_lower_bound =
1728 integer_trail_->LevelZeroLowerBound(objective_cp_);
1729 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1732 LinearConstraint objective_ct;
1734 objective_ct.ub = integer_objective_offset_ -
1735 integer_trail_->LevelZeroLowerBound(objective_cp_);
1736 IntegerValue obj_coeff_magnitude(0);
1737 objective_ct.resize(integer_objective_.size());
1739 for (
const auto& [col, coeff] : integer_objective_) {
1740 const IntegerVariable var = integer_variables_[col.value()];
1741 objective_ct.vars[
i] = var;
1742 objective_ct.coeffs[
i] = -coeff;
1743 obj_coeff_magnitude = std::max(obj_coeff_magnitude,
IntTypeAbs(coeff));
1747 if (!base_ct_.FillFromLinearConstraint(objective_ct, expanded_lp_solution_,
1755 if (obj_coeff_magnitude < 1e9 &&
1756 constraint_manager_.AddCut(std::move(objective_ct),
"Objective")) {
1762 ImpliedBoundsProcessor* ib_processor =
nullptr;
1764 bool some_ints =
false;
1765 bool some_relevant_positions =
false;
1766 for (
const CutTerm& term : base_ct_.terms) {
1767 if (term.bound_diff > 1) some_ints =
true;
1768 if (term.HasRelevantLpValue()) some_relevant_positions =
true;
1772 if (!some_relevant_positions)
return;
1773 if (some_ints) ib_processor = &implied_bounds_processor_;
1777 const IntegerVariable first_slack(
1778 std::numeric_limits<IntegerVariable::ValueType>::max());
1779 if (ib_processor !=
nullptr) {
1780 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1781 ib_processor =
nullptr;
1786 base_ct_.ComplementForPositiveCoefficients();
1787 cover_cut_helper_.ClearCache();
1788 if (cover_cut_helper_.TrySimpleKnapsack(base_ct_, ib_processor)) {
1789 PostprocessAndAddCut(
"Objective_K", cover_cut_helper_.Info(), first_slack,
1790 cover_cut_helper_.cut());
1794 RoundingOptions options;
1795 options.max_scaling = parameters_.max_integer_rounding_scaling();
1796 base_ct_.ComplementForSmallerLpValues();
1797 if (integer_rounding_cut_helper_.ComputeCut(options, base_ct_,
1799 PostprocessAndAddCut(
"Objective_R", integer_rounding_cut_helper_.Info(),
1800 first_slack, integer_rounding_cut_helper_.cut());
1804void LinearProgrammingConstraint::AddMirCuts() {
1819 util_intops::StrongVector<ColIndex, IntegerValue> dense_cut(
1820 integer_variables_.size(), IntegerValue(0));
1821 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1825 const int num_cols = integer_variables_.size();
1826 const int num_rows = integer_lp_.size();
1827 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1828 util_intops::StrongVector<RowIndex, double> row_weights(num_rows, 0.0);
1829 Fractional* lb_with_slack = simplex_.MutableLowerBounds()->data();
1830 Fractional* ub_with_slack = simplex_.MutableUpperBounds()->data();
1831 for (RowIndex row(0); row < num_rows; ++row) {
1838 const auto status = simplex_.GetConstraintStatus(row);
1839 const double activity = simplex_.GetConstraintActivity(row);
1840 const double ct_lb = -ub_with_slack[num_cols + row.value()];
1841 const double ct_ub = -lb_with_slack[num_cols + row.value()];
1842 if (activity > ct_ub - 1e-4 ||
1845 base_rows.push_back({row, IntegerValue(1)});
1847 if (activity < ct_lb + 1e-4 ||
1850 base_rows.push_back({row, IntegerValue(-1)});
1869 std::max(
Fractional(1e-8), std::abs(simplex_.GetDualValue(row)));
1875 int64_t dtime_num_entries = 0;
1876 std::shuffle(base_rows.begin(), base_rows.end(), *random_);
1878 std::vector<double> weights;
1879 util_intops::StrongVector<RowIndex, bool> used_rows;
1880 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1881 const auto matrix = simplex_.MatrixWithSlack().view();
1882 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1883 if (time_limit_->LimitReached())
break;
1884 if (dtime_num_entries > 1e7)
break;
1886 const glop::RowIndex base_row = entry.first;
1887 const IntegerValue multiplier = entry.second;
1896 integer_multipliers = {entry};
1897 if ((multiplier > 0 && !integer_lp_[base_row].ub_is_trivial) ||
1898 (multiplier < 0 && !integer_lp_[base_row].lb_is_trivial)) {
1899 if (AddCutFromConstraints(
"MIR_1", integer_multipliers))
continue;
1903 for (
const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1904 dense_cut[col] = IntegerValue(0);
1906 non_zeros_.ResetAllToFalse();
1909 const LinearConstraintInternal& ct = integer_lp_[entry.first];
1910 for (
int i = 0;
i < ct.num_terms; ++
i) {
1911 const int index = ct.start_in_buffer +
i;
1912 const ColIndex col = integer_lp_cols_[index];
1913 const IntegerValue coeff = integer_lp_coeffs_[index];
1914 non_zeros_.Set(col);
1915 dense_cut[col] += coeff * multiplier;
1918 used_rows.
assign(num_rows,
false);
1919 used_rows[entry.first] =
true;
1924 const int kMaxAggregation = 5;
1925 for (
int i = 0;
i < kMaxAggregation; ++
i) {
1928 IntegerValue max_magnitude(0);
1930 std::vector<ColIndex> col_candidates;
1931 for (
const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1932 if (dense_cut[col] == 0)
continue;
1934 max_magnitude = std::max(max_magnitude,
IntTypeAbs(dense_cut[col]));
1935 const int col_degree = matrix.ColumnNumEntries(col).value();
1936 if (col_degree <= 1)
continue;
1941 const IntegerVariable var = integer_variables_[col.value()];
1942 const double lp_value = expanded_lp_solution_[var];
1943 const double lb =
ToDouble(integer_trail_->LevelZeroLowerBound(var));
1944 const double ub =
ToDouble(integer_trail_->LevelZeroUpperBound(var));
1945 const double bound_distance = std::min(ub - lp_value, lp_value - lb);
1946 if (bound_distance > 1e-2) {
1947 weights.push_back(bound_distance);
1948 col_candidates.push_back(col);
1951 if (col_candidates.empty())
break;
1953 const ColIndex var_to_eliminate =
1957 std::vector<RowIndex> possible_rows;
1959 for (
const auto entry_index : matrix.Column(var_to_eliminate)) {
1960 const RowIndex row = matrix.EntryRow(entry_index);
1966 if (used_rows[row])
continue;
1967 used_rows[row] =
true;
1974 bool add_row =
false;
1975 if (!integer_lp_[row].ub_is_trivial) {
1977 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1979 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1982 if (!integer_lp_[row].lb_is_trivial) {
1984 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1986 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1990 possible_rows.push_back(row);
1991 weights.push_back(row_weights[row]);
1994 if (possible_rows.empty())
break;
1996 const RowIndex row_to_combine =
2000 IntegerValue to_combine_coeff = 0;
2001 const LinearConstraintInternal& ct_to_combine =
2002 integer_lp_[row_to_combine];
2003 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
2004 const int index = ct_to_combine.start_in_buffer +
i;
2005 if (integer_lp_cols_[index] == var_to_eliminate) {
2006 to_combine_coeff = integer_lp_coeffs_[index];
2010 CHECK_NE(to_combine_coeff, 0);
2012 IntegerValue mult1 = -to_combine_coeff;
2013 IntegerValue mult2 = dense_cut[var_to_eliminate];
2020 const IntegerValue gcd = IntegerValue(
2030 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
2031 max_magnitude = std::max(max_magnitude,
IntTypeAbs(entry.second));
2033 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
2034 CapProd(infinity_norms_[row_to_combine].value(),
2035 std::abs(mult2.value()))) ==
2036 std::numeric_limits<int64_t>::max()) {
2040 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
2041 dtime_num_entries += integer_lp_[entry.first].num_terms;
2042 entry.second *= mult1;
2044 dtime_num_entries += integer_lp_[row_to_combine].num_terms;
2045 integer_multipliers.push_back({row_to_combine, mult2});
2048 if (AddCutFromConstraints(absl::StrCat(
"MIR_",
i + 2),
2049 integer_multipliers)) {
2055 if (
i + 1 == kMaxAggregation)
break;
2057 for (ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
2058 dense_cut[col] *= mult1;
2060 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
2061 const int index = ct_to_combine.start_in_buffer +
i;
2062 const ColIndex col = integer_lp_cols_[index];
2063 const IntegerValue coeff = integer_lp_coeffs_[index];
2064 non_zeros_.Set(col);
2065 dense_cut[col] += coeff * mult2;
2071void LinearProgrammingConstraint::AddZeroHalfCuts() {
2072 if (time_limit_->LimitReached())
return;
2074 tmp_lp_values_.clear();
2075 tmp_var_lbs_.clear();
2076 tmp_var_ubs_.clear();
2077 for (
const IntegerVariable var : integer_variables_) {
2078 tmp_lp_values_.push_back(expanded_lp_solution_[var]);
2079 tmp_var_lbs_.push_back(integer_trail_->LevelZeroLowerBound(var));
2080 tmp_var_ubs_.push_back(integer_trail_->LevelZeroUpperBound(var));
2084 zero_half_cut_helper_.ProcessVariables(tmp_lp_values_, tmp_var_lbs_,
2086 for (glop::RowIndex row(0); row < integer_lp_.size(); ++row) {
2089 const auto status = simplex_.GetConstraintStatus(row);
2093 zero_half_cut_helper_.AddOneConstraint(
2094 row, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2095 integer_lp_[row].lb, integer_lp_[row].ub);
2097 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
2098 zero_half_cut_helper_.InterestingCandidates(random_)) {
2099 if (time_limit_->LimitReached())
break;
2105 AddCutFromConstraints(
"ZERO_HALF", multipliers);
2109void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
2110 const int64_t min_iter,
const int64_t max_iter) {
2111 if (parameters_.linearization_level() < 2)
return;
2112 const int64_t num_degenerate_columns = CalculateDegeneracy();
2113 const int64_t num_cols = simplex_.GetProblemNumCols().value();
2114 if (num_cols <= 0) {
2117 CHECK_GT(num_cols, 0);
2118 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
2123 if (is_degenerate_) {
2124 next_simplex_iter_ /= std::max(int64_t{1}, decrease_factor);
2126 next_simplex_iter_ *= 2;
2129 if (is_degenerate_) {
2130 next_simplex_iter_ /= std::max(int64_t{1}, 2 * decrease_factor);
2134 next_simplex_iter_ = num_cols / 40;
2137 next_simplex_iter_ =
2138 std::max(min_iter, std::min(max_iter, next_simplex_iter_));
2142 CHECK(!sat_solver_->ModelIsUnsat());
2144 const int old_num_force = num_force_lp_call_on_next_propagate_;
2145 num_force_lp_call_on_next_propagate_ = 0;
2146 if (!enabled_)
return true;
2147 if (time_limit_->LimitReached())
return true;
2148 const int64_t timestamp_at_function_start = integer_trail_->num_enqueues();
2162 const double dtime_at_function_start =
2163 time_limit_->GetElapsedDeterministicTime();
2164 if (trail_->CurrentDecisionLevel() == 0 && old_num_force == 0) {
2165 const double interval =
2166 dtime_at_function_start - last_root_level_deterministic_time_;
2167 if (last_root_level_deterministic_duration_ > interval) {
2168 ++num_root_level_skips_;
2171 ++num_root_level_solves_;
2173 const auto cleanup = ::absl::MakeCleanup([dtime_at_function_start,
this]() {
2174 if (trail_->CurrentDecisionLevel() == 0) {
2175 const double dtime_at_exit = time_limit_->GetElapsedDeterministicTime();
2176 last_root_level_deterministic_time_ = dtime_at_exit;
2177 last_root_level_deterministic_duration_ =
2178 dtime_at_exit - dtime_at_function_start;
2182 UpdateBoundsOfLpVariables();
2186 if ( (
false) && objective_is_defined_) {
2194 simplex_params_.set_objective_upper_limit(
2195 static_cast<double>(integer_trail_->UpperBound(objective_cp_).value() +
2196 100.0 * kCpEpsilon));
2202 if (trail_->CurrentDecisionLevel() == 0) {
2203 simplex_params_.set_max_number_of_iterations(
2204 parameters_.root_lp_iterations());
2206 simplex_params_.set_max_number_of_iterations(next_simplex_iter_);
2209 simplex_.SetParameters(simplex_params_);
2210 simplex_.SetRandom(*random_);
2211 if (!SolveLp())
return true;
2212 if (!AnalyzeLp())
return false;
2215 const int max_cuts_rounds = trail_->CurrentDecisionLevel() == 0
2216 ? parameters_.max_cut_rounds_at_level_zero()
2220 cuts_round < max_cuts_rounds) {
2228 if (integer_trail_->num_enqueues() > timestamp_at_function_start &&
2229 old_num_force < 3) {
2230 num_force_lp_call_on_next_propagate_ = old_num_force + 1;
2231 watcher_->CallAgainDuringThisPropagation();
2239 if (parameters_.cut_level() > 0 &&
2240 (num_solves_ > 1 || !parameters_.add_lp_constraints_lazily())) {
2242 implied_bounds_processor_.RecomputeCacheAndSeparateSomeImpliedBoundCuts(
2243 expanded_lp_solution_);
2244 if (parameters_.add_rlt_cuts()) {
2245 rlt_cut_helper_.Initialize(extended_integer_variables_);
2252 const int level = trail_->CurrentDecisionLevel();
2253 if (trail_->CurrentDecisionLevel() == 0) {
2254 problem_proven_infeasible_by_cuts_ =
false;
2255 if (parameters_.add_objective_cut()) AddObjectiveCut();
2256 if (parameters_.add_mir_cuts()) AddMirCuts();
2257 if (parameters_.add_cg_cuts()) AddCGCuts();
2258 if (parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
2259 if (problem_proven_infeasible_by_cuts_) {
2260 return integer_trail_->ReportConflict({});
2262 top_n_cuts_.TransferToManager(&constraint_manager_);
2266 if (level == 0 || !parameters_.only_add_cuts_at_level_zero()) {
2268 if (level > 0 && generator.only_run_at_level_zero)
continue;
2269 if (!generator.generate_cuts(&constraint_manager_))
return false;
2273 implied_bounds_processor_.IbCutPool().TransferToManager(
2274 &constraint_manager_);
2278 if (constraint_manager_.ChangeLp(&state_, &num_added)) {
2280 simplex_.LoadStateForNextSolve(state_);
2281 if (!CreateLpFromConstraintManager()) {
2282 sat_solver_->NotifyThatModelIsUnsat();
2283 return integer_trail_->ReportConflict({});
2288 if (num_added == 0) {
2292 const double old_obj = simplex_.GetObjectiveValue();
2293 if (!SolveLp())
return true;
2294 if (!AnalyzeLp())
return false;
2297 VLOG(3) <<
"Relaxation improvement " << old_obj <<
" -> "
2298 << simplex_.GetObjectiveValue()
2299 <<
" diff: " << simplex_.GetObjectiveValue() - old_obj
2300 <<
" level: " << trail_->CurrentDecisionLevel();
2303 if (trail_->CurrentDecisionLevel() == 0) {
2304 lp_at_level_zero_is_final_ =
true;
2313absl::int128 LinearProgrammingConstraint::GetImpliedLowerBound(
2315 absl::int128 lower_bound(0);
2317 for (
int i = 0;
i < size; ++
i) {
2318 const IntegerVariable var = terms.
vars[
i];
2319 const IntegerValue coeff = terms.
coeffs[
i];
2320 const IntegerValue bound = coeff > 0 ? integer_trail_->
LowerBound(var)
2322 lower_bound += absl::int128(bound.value()) * absl::int128(coeff.value());
2327bool LinearProgrammingConstraint::ScalingCanOverflow(
2328 int power,
bool take_objective_into_account,
2329 absl::Span<
const std::pair<glop::RowIndex, double>> multipliers,
2330 int64_t overflow_cap)
const {
2332 const int64_t factor = int64_t{1} << power;
2333 const double factor_as_double =
static_cast<double>(factor);
2334 if (take_objective_into_account) {
2336 if (bound >= overflow_cap)
return true;
2338 for (
const auto [row, double_coeff] : multipliers) {
2339 const double magnitude =
2340 std::abs(std::round(double_coeff * factor_as_double));
2341 if (std::isnan(magnitude))
return true;
2342 if (magnitude >=
static_cast<double>(std::numeric_limits<int64_t>::max())) {
2346 infinity_norms_[row].value()));
2347 if (bound >= overflow_cap)
return true;
2349 return bound >= overflow_cap;
2352void LinearProgrammingConstraint::IgnoreTrivialConstraintMultipliers(
2353 std::vector<std::pair<RowIndex, double>>* lp_multipliers) {
2355 for (
const std::pair<RowIndex, double>& p : *lp_multipliers) {
2356 const RowIndex row = p.first;
2358 if (lp_multi > 0.0 && integer_lp_[row].ub_is_trivial)
continue;
2359 if (lp_multi < 0.0 && integer_lp_[row].lb_is_trivial)
continue;
2360 (*lp_multipliers)[new_size++] = p;
2362 lp_multipliers->resize(new_size);
2365void LinearProgrammingConstraint::ScaleMultipliers(
2366 absl::Span<
const std::pair<RowIndex, double>> lp_multipliers,
2367 bool take_objective_into_account, IntegerValue* scaling,
2368 std::vector<std::pair<RowIndex, IntegerValue>>* output)
const {
2372 if (lp_multipliers.empty()) {
2379 const int64_t overflow_cap = std::numeric_limits<int64_t>::max();
2380 if (ScalingCanOverflow(0, take_objective_into_account,
2381 lp_multipliers, overflow_cap)) {
2382 ++num_scaling_issues_;
2396 if (candidate >= 63)
return false;
2398 return !ScalingCanOverflow(candidate, take_objective_into_account,
2399 lp_multipliers, overflow_cap);
2401 *scaling = int64_t{1} << power;
2405 int64_t gcd = scaling->value();
2406 const double scaling_as_double =
static_cast<double>(scaling->value());
2407 for (
const auto [row, double_coeff] : lp_multipliers) {
2408 const IntegerValue coeff(std::round(double_coeff * scaling_as_double));
2410 gcd = std::gcd(gcd, std::abs(coeff.value()));
2411 output->push_back({row, coeff});
2416 for (
auto& entry : *output) {
2417 entry.second /= gcd;
2422template <
bool check_overflow>
2423bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
2424 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers,
2428 scattered_vector->
ClearAndResize(extended_integer_variables_.size());
2432 for (
const std::pair<RowIndex, IntegerValue>& term : integer_multipliers) {
2433 const RowIndex row = term.first;
2434 const IntegerValue multiplier = term.second;
2435 DCHECK_LT(row, integer_lp_.size());
2439 multiplier, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2440 infinity_norms_[row])) {
2445 const IntegerValue bound =
2446 multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
2447 if (!
AddProductTo(multiplier, bound, upper_bound))
return false;
2454void LinearProgrammingConstraint::AdjustNewLinearConstraint(
2455 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
2457 const IntegerValue kMaxWantedCoeff(1e18);
2458 bool adjusted =
false;
2459 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
2460 const RowIndex row = term.first;
2461 const IntegerValue multiplier = term.second;
2462 if (multiplier == 0)
continue;
2468 IntegerValue negative_limit =
2469 FloorRatio(kMaxWantedCoeff, infinity_norms_[row]);
2470 IntegerValue positive_limit = negative_limit;
2474 if (integer_lp_[row].ub != integer_lp_[row].lb) {
2475 if (multiplier > 0) {
2476 negative_limit = std::min(negative_limit, multiplier);
2478 positive_limit = std::min(positive_limit, -multiplier);
2483 const IntegerValue row_bound =
2484 multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
2485 if (row_bound != 0) {
2487 std::max(IntegerValue(0), kMaxWantedCoeff -
IntTypeAbs(*upper_bound)),
2489 const IntegerValue limit2 =
2491 if ((*upper_bound > 0) == (row_bound > 0)) {
2492 positive_limit = std::min(positive_limit, limit1);
2493 negative_limit = std::min(negative_limit, limit2);
2495 negative_limit = std::min(negative_limit, limit1);
2496 positive_limit = std::min(positive_limit, limit2);
2508 double common_diff =
ToDouble(row_bound);
2509 double positive_diff = 0.0;
2510 double negative_diff = 0.0;
2515 const LinearConstraintInternal& ct = integer_lp_[row];
2516 for (
int i = 0;
i < ct.num_terms; ++
i) {
2517 const int index = ct.start_in_buffer +
i;
2518 const ColIndex col = integer_lp_cols_[index];
2519 const IntegerValue coeff = integer_lp_coeffs_[index];
2520 DCHECK_NE(coeff, 0);
2525 const IntegerValue current = (*scattered_vector)[col];
2527 const IntegerVariable var = integer_variables_[col.value()];
2528 const IntegerValue lb = integer_trail_->LowerBound(var);
2529 const IntegerValue ub = integer_trail_->UpperBound(var);
2550 const IntegerValue abs_coeff =
IntTypeAbs(coeff);
2551 const IntegerValue current_magnitude =
IntTypeAbs(current);
2552 const IntegerValue overflow_threshold =
2553 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude);
2554 if ((current > 0) == (coeff > 0)) {
2555 if (negative_limit * abs_coeff > current_magnitude) {
2556 negative_limit = current_magnitude / abs_coeff;
2557 if (positive_limit == 0 && negative_limit == 0)
break;
2559 if (positive_limit * abs_coeff > overflow_threshold) {
2560 positive_limit = overflow_threshold / abs_coeff;
2561 if (positive_limit == 0 && negative_limit == 0)
break;
2564 if (negative_limit * abs_coeff > overflow_threshold) {
2565 negative_limit = overflow_threshold / abs_coeff;
2566 if (positive_limit == 0 && negative_limit == 0)
break;
2568 if (positive_limit * abs_coeff > current_magnitude) {
2569 positive_limit = current_magnitude / abs_coeff;
2570 if (positive_limit == 0 && negative_limit == 0)
break;
2575 const IntegerVariable var = integer_variables_[col.value()];
2576 const IntegerValue implied = current > 0
2577 ? integer_trail_->LowerBound(var)
2578 : integer_trail_->UpperBound(var);
2584 positive_diff += common_diff;
2585 negative_diff += common_diff;
2591 IntegerValue to_add(0);
2592 if (positive_diff <= -1.0 && positive_limit > 0) {
2593 to_add = positive_limit;
2595 if (negative_diff >= 1.0 && negative_limit > 0) {
2598 std::abs(
ToDouble(negative_limit) * negative_diff) >
2599 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2600 to_add = -negative_limit;
2604 term.second += to_add;
2605 *upper_bound += to_add * row_bound;
2607 CHECK(scattered_vector
2608 ->AddLinearExpressionMultiple</*check_overflow=*/false>(
2609 to_add, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2610 infinity_norms_[row]));
2613 if (adjusted) ++num_adjusts_;
2616bool LinearProgrammingConstraint::PropagateLpConstraint(
LinearConstraint ct) {
2617 DCHECK(constraint_manager_.DebugCheckConstraint(ct));
2620 const int num_terms = ct.num_terms;
2621 if (num_terms == 0) {
2622 if (ct.ub >= 0)
return true;
2623 return integer_trail_->ReportConflict({});
2626 std::unique_ptr<IntegerSumLE128> cp_constraint(
2631 if (!cp_constraint->PropagateAtLevelZero())
return false;
2632 if (trail_->CurrentDecisionLevel() == 0)
return true;
2636 const int64_t stamp = integer_trail_->num_enqueues();
2637 const bool no_conflict = cp_constraint->Propagate();
2638 if (no_conflict && integer_trail_->num_enqueues() == stamp)
return true;
2640 const int64_t current_size =
2641 cumulative_optimal_constraint_sizes_.empty()
2643 : cumulative_optimal_constraint_sizes_.back();
2644 optimal_constraints_.push_back(std::move(cp_constraint));
2645 cumulative_optimal_constraint_sizes_.push_back(current_size + num_terms);
2646 rev_optimal_constraints_size_ = optimal_constraints_.size();
2664bool LinearProgrammingConstraint::PropagateExactLpReason() {
2666 integer_reason_.clear();
2667 deductions_.clear();
2668 deductions_reason_.clear();
2673 const RowIndex num_rows = simplex_.GetProblemNumRows();
2674 tmp_lp_multipliers_.clear();
2675 for (RowIndex row(0); row < num_rows; ++row) {
2676 const double value = -simplex_.GetDualValue(row);
2677 if (std::abs(value) < kZeroTolerance)
continue;
2678 tmp_lp_multipliers_.push_back({row, scaler_.UnscaleDualValue(row, value)});
2684 if (tmp_lp_multipliers_.empty())
return true;
2688 bool take_objective_into_account =
true;
2689 if (objective_cp_is_part_of_lp_) {
2692 CHECK_EQ(integer_objective_.size(), 1);
2693 CHECK_EQ(integer_objective_[0].first, mirror_lp_variable_[objective_cp_]);
2694 CHECK_EQ(integer_objective_[0].second, IntegerValue(1));
2696 take_objective_into_account =
false;
2699 IntegerValue scaling = 0;
2700 IgnoreTrivialConstraintMultipliers(&tmp_lp_multipliers_);
2701 ScaleMultipliers(tmp_lp_multipliers_, take_objective_into_account, &scaling,
2702 &tmp_integer_multipliers_);
2704 VLOG(1) << simplex_.GetProblemStatus();
2705 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2710 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2711 tmp_integer_multipliers_, &tmp_scattered_vector_, &rc_ub));
2713 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term =
2715 if (take_objective_into_account) {
2718 const IntegerValue obj_scale = scaling;
2722 tmp_coeffs_.clear();
2723 for (
const auto [col, coeff] : integer_objective_) {
2724 tmp_cols_.push_back(col);
2725 tmp_coeffs_.push_back(coeff);
2727 CHECK(tmp_scattered_vector_
2728 .AddLinearExpressionMultiple</*check_overflow=*/false>(
2729 obj_scale, tmp_cols_, tmp_coeffs_, objective_infinity_norm_));
2730 CHECK(
AddProductTo(-obj_scale, integer_objective_offset_, &rc_ub));
2732 extra_term = {objective_cp_, -obj_scale};
2738 if (take_objective_into_account) {
2739 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2745 LinearConstraint explanation =
2746 tmp_scattered_vector_.ConvertToLinearConstraint(
2747 extended_integer_variables_, rc_ub, extra_term);
2750 if (explanation.num_terms == 0) {
2751 trail_->MutableConflict()->clear();
2752 return explanation.ub >= 0;
2755 constraint_manager_.SetReducedCostsAsLinearConstraint(explanation);
2756 return PropagateLpConstraint(std::move(explanation));
2759bool LinearProgrammingConstraint::PropagateExactDualRay() {
2760 IntegerValue scaling;
2762 DCHECK_EQ(ray.size(), integer_lp_.size());
2763 tmp_lp_multipliers_.clear();
2764 for (RowIndex row(0); row < ray.size(); ++row) {
2765 const double value = ray[row];
2766 if (std::abs(value) < kZeroTolerance)
continue;
2770 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
2772 IgnoreTrivialConstraintMultipliers(&tmp_lp_multipliers_);
2773 ScaleMultipliers(tmp_lp_multipliers_,
false,
2774 &scaling, &tmp_integer_multipliers_);
2776 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2780 IntegerValue new_constraint_ub;
2781 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2782 tmp_integer_multipliers_, &tmp_scattered_vector_, &new_constraint_ub))
2785 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2786 &new_constraint_ub);
2788 LinearConstraint explanation =
2789 tmp_scattered_vector_.ConvertToLinearConstraint(
2790 extended_integer_variables_, new_constraint_ub);
2792 std::string message;
2793 if (VLOG_IS_ON(1)) {
2795 message = absl::StrCat(
"LP exact dual ray not infeasible,",
2796 " implied_lb: ", GetImpliedLowerBound(explanation),
2797 " ub: ", explanation.ub.value());
2801 if (!PropagateLpConstraint(std::move(explanation)))
return false;
2807int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2808 int num_non_basic_with_zero_rc = 0;
2809 const auto reduced_costs = simplex_.GetReducedCosts().const_view();
2810 for (
const glop::ColIndex
i : simplex_.GetNotBasicBitRow()) {
2811 if (reduced_costs[
i] == 0.0) {
2812 num_non_basic_with_zero_rc++;
2815 const int64_t num_cols = simplex_.GetProblemNumCols().value();
2816 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2817 return num_non_basic_with_zero_rc;
2820void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2821 double cp_objective_delta) {
2822 deductions_.clear();
2824 const double lp_objective_delta =
2825 cp_objective_delta / scaler_.ObjectiveScalingFactor();
2826 const int num_vars = integer_variables_.size();
2827 for (
int i = 0;
i < num_vars;
i++) {
2828 const IntegerVariable cp_var = integer_variables_[
i];
2829 const glop::ColIndex lp_var = glop::ColIndex(
i);
2830 const double rc = simplex_.GetReducedCost(lp_var);
2831 const double value = simplex_.GetVariableValue(lp_var);
2833 if (rc == 0.0)
continue;
2834 const double lp_other_bound = value + lp_objective_delta / rc;
2835 const double cp_other_bound =
2836 scaler_.UnscaleVariableValue(lp_var, lp_other_bound);
2838 if (rc > kLpEpsilon) {
2839 const double ub =
ToDouble(integer_trail_->UpperBound(cp_var));
2840 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2845 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2848 }
else if (rc < -kLpEpsilon) {
2849 const double lb =
ToDouble(integer_trail_->LowerBound(cp_var));
2850 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2852 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2853 deductions_.push_back(
2860void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2861 const int num_vars = integer_variables_.size();
2862 if (sum_cost_down_.size() < num_vars) {
2863 sum_cost_down_.resize(num_vars, 0.0);
2864 num_cost_down_.resize(num_vars, 0);
2865 sum_cost_up_.resize(num_vars, 0.0);
2866 num_cost_up_.resize(num_vars, 0);
2867 rc_scores_.resize(num_vars, 0.0);
2871 num_calls_since_reduced_cost_averages_reset_++;
2872 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2873 for (
int i = 0;
i < num_vars;
i++) {
2874 sum_cost_up_[
i] /= 2;
2875 num_cost_up_[
i] /= 2;
2876 sum_cost_down_[
i] /= 2;
2877 num_cost_down_[
i] /= 2;
2879 num_calls_since_reduced_cost_averages_reset_ = 0;
2883 for (
int i = 0;
i < num_vars;
i++) {
2884 const IntegerVariable var = integer_variables_[
i];
2887 if (integer_trail_->IsFixed(var))
continue;
2890 const double rc = lp_reduced_cost_[
i];
2891 if (std::abs(rc) < kCpEpsilon)
continue;
2894 sum_cost_down_[
i] -= rc;
2895 num_cost_down_[
i]++;
2897 sum_cost_up_[
i] += rc;
2904 rc_rev_int_repository_.SetLevel(0);
2905 rc_rev_int_repository_.SetLevel(trail_->CurrentDecisionLevel());
2910 positions_by_decreasing_rc_score_.clear();
2911 for (
int i = 0;
i < num_vars;
i++) {
2916 num_cost_up_[
i] > 0 ? sum_cost_up_[
i] / num_cost_up_[
i] : 0.0;
2917 const double a_down =
2918 num_cost_down_[
i] > 0 ? sum_cost_down_[
i] / num_cost_down_[
i] : 0.0;
2919 if (num_cost_down_[
i] > 0 && num_cost_up_[
i] > 0) {
2920 rc_scores_[
i] = std::min(a_up, a_down);
2922 rc_scores_[
i] = 0.5 * (a_down + a_up);
2927 if (rc_scores_[
i] > 0.0) {
2928 positions_by_decreasing_rc_score_.push_back({-rc_scores_[
i],
i});
2931 std::sort(positions_by_decreasing_rc_score_.begin(),
2932 positions_by_decreasing_rc_score_.end());
2937 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2940IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2942 int selected_index = -1;
2943 const int size = positions_by_decreasing_rc_score_.size();
2944 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2945 for (
int i = rev_rc_start_;
i < size; ++
i) {
2946 const int index = positions_by_decreasing_rc_score_[
i].second;
2947 const IntegerVariable var = integer_variables_[index];
2948 if (integer_trail_->
IsFixed(var))
continue;
2949 selected_index = index;
2954 if (selected_index == -1)
return IntegerLiteral();
2955 const IntegerVariable var = integer_variables_[selected_index];
2961 const IntegerValue ub = integer_trail_->
UpperBound(var);
2962 const IntegerValue value_ceil(
2964 if (value_ceil >= ub) {
2970 const IntegerValue lb = integer_trail_->
LowerBound(var);
2971 const IntegerValue value_floor(
2973 if (value_floor <= lb) {
2980 num_cost_up_[selected_index] > 0
2981 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2983 const double a_down =
2984 num_cost_down_[selected_index] > 0
2985 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2987 if (a_down < a_up) {
2994absl::Span<const glop::ColIndex> LinearProgrammingConstraint::IntegerLpRowCols(
2995 glop::RowIndex row)
const {
2996 const int start = integer_lp_[row].start_in_buffer;
2997 const size_t num_terms =
static_cast<size_t>(integer_lp_[row].num_terms);
2998 return {integer_lp_cols_.data() + start, num_terms};
3001absl::Span<const IntegerValue> LinearProgrammingConstraint::IntegerLpRowCoeffs(
3002 glop::RowIndex row)
const {
3003 const int start = integer_lp_[row].start_in_buffer;
3004 const size_t num_terms =
static_cast<size_t>(integer_lp_[row].num_terms);
3005 return {integer_lp_coeffs_.data() + start, num_terms};
3009 return absl::StrFormat(
"%d rows, %d columns, %d entries", integer_lp_.size(),
3010 integer_variables_.size(), integer_lp_coeffs_.size());
StrictITISpan< RowIndex, const Fractional > ConstView
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
Fractional GetVariableValue(ColIndex col) const
DenseRow * MutableLowerBounds()
DenseRow * MutableUpperBounds()
IntegerValue LowerBound(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
const std::vector< ConstraintIndex > & LpConstraints() const
const util_intops::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
std::string DimensionString() const
void RegisterWith(Model *model)
void SetLevel(int level) override
void AddCutGenerator(CutGenerator generator)
bool AddLinearConstraint(LinearConstraint ct)
bool Propagate() override
~LinearProgrammingConstraint() override
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)
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()
bool AddLinearExpressionMultiple(IntegerValue multiplier, absl::Span< const glop::ColIndex > cols, absl::Span< const IntegerValue > coeffs, IntegerValue max_coeff_magnitude)
void ClearAndResize(int size)
int CurrentDecisionLevel() const
void assign(size_type n, const value_type &val)
void push_back(const value_type &val)
constexpr ColIndex kInvalidCol(-1)
ColIndex RowToColIndex(RowIndex row)
RowIndex ColToRowIndex(ColIndex col)
StrictITIVector< RowIndex, Fractional > DenseColumn
StrictITIVector< ColIndex, Fractional > DenseRow
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
void DivideByGCD(LinearConstraint *constraint)
IntType IntTypeAbs(IntType t)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
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)
double ToDouble(IntegerValue value)
bool AtMinOrMaxInt64(int64_t x)
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)
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