30#include "absl/algorithm/container.h"
31#include "absl/container/flat_hash_map.h"
32#include "absl/log/check.h"
33#include "absl/numeric/int128.h"
34#include "absl/strings/str_cat.h"
35#include "absl/strings/str_format.h"
36#include "absl/strings/string_view.h"
37#include "absl/types/span.h"
42#include "ortools/glop/parameters.pb.h"
61#include "ortools/sat/sat_parameters.pb.h"
81 for (
const glop::ColIndex col : non_zeros_) {
82 dense_vector_[col] = IntegerValue(0);
84 dense_vector_.resize(size, IntegerValue(0));
86 dense_vector_.assign(size, IntegerValue(0));
88 for (
const glop::ColIndex col : non_zeros_) {
89 is_zeros_[col] =
true;
91 is_zeros_.resize(size,
true);
97 const int64_t add =
CapAdd(value.value(), dense_vector_[col].value());
98 if (add == std::numeric_limits<int64_t>::min() ||
99 add == std::numeric_limits<int64_t>::max())
101 dense_vector_[col] = IntegerValue(add);
102 if (is_sparse_ && is_zeros_[col]) {
103 is_zeros_[col] =
false;
104 non_zeros_.push_back(col);
109template <
bool check_overflow>
111 const IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
112 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude) {
114 if (check_overflow) {
115 const IntegerValue prod =
CapProdI(max_coeff_magnitude, multiplier);
119 IntegerValue* data = dense_vector_.data();
120 const double threshold = 0.1 *
static_cast<double>(dense_vector_.size());
121 const int num_terms = cols.size();
122 if (is_sparse_ &&
static_cast<double>(num_terms) < threshold) {
123 for (
int i = 0;
i < num_terms; ++
i) {
124 const glop::ColIndex col = cols[
i];
125 if (is_zeros_[col]) {
126 is_zeros_[col] =
false;
127 non_zeros_.push_back(col);
129 const IntegerValue product = multiplier * coeffs[
i];
130 if (check_overflow) {
132 data[col.value()].mutable_value())) {
136 data[col.value()] += product;
139 if (
static_cast<double>(non_zeros_.size()) > threshold) {
144 for (
int i = 0;
i < num_terms; ++
i) {
145 const glop::ColIndex col = cols[
i];
146 const IntegerValue product = multiplier * coeffs[
i];
147 if (check_overflow) {
149 data[col.value()].mutable_value())) {
153 data[col.value()] += product;
162 IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
163 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude);
166 IntegerValue multiplier, absl::Span<const glop::ColIndex> cols,
167 absl::Span<const IntegerValue> coeffs, IntegerValue max_coeff_magnitude);
170 absl::Span<const IntegerVariable> integer_variables,
171 IntegerValue upper_bound,
172 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term) {
176 for (
const glop::ColIndex col : non_zeros_) {
177 const IntegerValue coeff = dense_vector_[col];
178 if (coeff == 0)
continue;
182 for (
const IntegerValue coeff : dense_vector_) {
183 if (coeff != 0) ++final_size;
186 if (extra_term != std::nullopt) ++final_size;
190 result.
resize(final_size);
195 std::sort(non_zeros_.begin(), non_zeros_.end());
196 for (
const glop::ColIndex col : non_zeros_) {
197 const IntegerValue coeff = dense_vector_[col];
198 if (coeff == 0)
continue;
199 result.
vars[new_size] = integer_variables[col.value()];
200 result.
coeffs[new_size] = coeff;
204 const int size = dense_vector_.size();
205 for (glop::ColIndex col(0); col < size; ++col) {
206 const IntegerValue coeff = dense_vector_[col];
207 if (coeff == 0)
continue;
208 result.
vars[new_size] = integer_variables[col.value()];
209 result.
coeffs[new_size] = coeff;
215 result.
ub = upper_bound;
217 if (extra_term != std::nullopt) {
218 result.
vars[new_size] += extra_term->first;
219 result.
coeffs[new_size] += extra_term->second;
223 CHECK_EQ(new_size, final_size);
229 absl::int128 rhs, absl::Span<const IntegerVariable> integer_variables,
230 absl::Span<const double> lp_solution,
IntegerTrail* integer_trail,
232 result->
terms.clear();
234 absl::Span<const IntegerValue> dense_vector = dense_vector_;
236 std::sort(non_zeros_.begin(), non_zeros_.end());
237 for (
const glop::ColIndex col : non_zeros_) {
238 const IntegerValue coeff = dense_vector[col.value()];
239 if (coeff == 0)
continue;
240 const IntegerVariable var = integer_variables[col.value()];
241 CHECK(result->
AppendOneTerm(var, coeff, lp_solution[col.value()],
246 for (
int col(0); col < dense_vector.size(); ++col) {
247 const IntegerValue coeff = dense_vector[col];
248 if (coeff == 0)
continue;
249 const IntegerVariable var = integer_variables[col];
257std::vector<std::pair<glop::ColIndex, IntegerValue>>
259 std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
261 std::sort(non_zeros_.begin(), non_zeros_.end());
262 for (
const glop::ColIndex col : non_zeros_) {
263 const IntegerValue coeff = dense_vector_[col];
264 if (coeff != 0) result.push_back({col, coeff});
267 const int size = dense_vector_.size();
268 for (glop::ColIndex col(0); col < size; ++col) {
269 const IntegerValue coeff = dense_vector_[col];
270 if (coeff != 0) result.push_back({col, coeff});
279 Model* model, absl::Span<const IntegerVariable> vars)
280 : constraint_manager_(model),
281 parameters_(*(model->GetOrCreate<SatParameters>())),
283 time_limit_(model->GetOrCreate<
TimeLimit>()),
285 trail_(model->GetOrCreate<
Trail>()),
295 rlt_cut_helper_(model),
296 implied_bounds_processor_({}, integer_trail_,
303 simplex_params_.set_use_dual_simplex(
true);
304 simplex_params_.set_primal_feasibility_tolerance(
305 parameters_.lp_primal_tolerance());
306 simplex_params_.set_dual_feasibility_tolerance(
307 parameters_.lp_dual_tolerance());
308 if (parameters_.use_exact_lp_reason()) {
309 simplex_params_.set_change_status_to_imprecise(
false);
311 simplex_.SetParameters(simplex_params_);
312 if (parameters_.search_branching() == SatParameters::LP_SEARCH) {
313 compute_reduced_cost_averages_ =
true;
317 integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
318 mirror_lp_variable_.resize(integer_trail_->NumIntegerVariables(),
322 auto* stats = model->GetOrCreate<SharedStatistics>();
323 integer_rounding_cut_helper_.SetSharedStatistics(stats);
324 cover_cut_helper_.SetSharedStatistics(stats);
327 CHECK(std::is_sorted(vars.begin(), vars.end()));
333 for (
const IntegerVariable positive_variable : vars) {
335 implied_bounds_processor_.AddLpVariable(positive_variable);
336 (*dispatcher_)[positive_variable] =
this;
338 if (!symmetrizer_->AppearInFoldedProblem(positive_variable))
continue;
340 integer_variables_.push_back(positive_variable);
341 extended_integer_variables_.push_back(positive_variable);
343 mirror_lp_variable_[positive_variable] = col;
348 if (symmetrizer_->HasSymmetry()) {
349 for (
const IntegerVariable var : integer_variables_) {
350 if (!symmetrizer_->IsOrbitSumVar(var))
continue;
351 const int orbit_index = symmetrizer_->OrbitIndex(var);
352 for (
const IntegerVariable var : symmetrizer_->Orbit(orbit_index)) {
353 extended_integer_variables_.push_back(var);
355 mirror_lp_variable_[var] = col;
362 CHECK_EQ(vars.size(), extended_integer_variables_.size());
363 lp_solution_.assign(vars.size(), std::numeric_limits<double>::infinity());
364 lp_reduced_cost_.assign(vars.size(), 0.0);
367 const IntegerVariable max_index = std::max(
368 NegationOf(vars.back()), integer_trail_->NumIntegerVariables() - 1);
369 if (max_index >= expanded_lp_solution_.size()) {
370 expanded_lp_solution_.assign(max_index.value() + 1, 0.0);
372 if (max_index >= expanded_reduced_costs_.size()) {
373 expanded_reduced_costs_.assign(max_index.value() + 1, 0.0);
379 DCHECK(!lp_constraint_is_registered_);
382 const auto index = constraint_manager_.Add(std::move(ct), &added, &folded);
386 if (added && folded) {
387 const auto& info = constraint_manager_.AllConstraints()[index];
389 const absl::Span<const IntegerVariable> vars = new_ct.
VarsAsSpan();
390 if (!info.ub_is_trivial) {
391 if (!linear_propagator_->AddConstraint({}, vars, new_ct.
CoeffsAsSpan(),
396 if (!info.lb_is_trivial) {
397 tmp_vars_.assign(vars.begin(), vars.end());
398 for (IntegerVariable& var : tmp_vars_) var =
NegationOf(var);
399 if (!linear_propagator_->AddConstraint(
409 DCHECK(!lp_constraint_is_registered_);
410 lp_constraint_is_registered_ =
true;
417 std::sort(integer_objective_.begin(), integer_objective_.end());
418 objective_infinity_norm_ = 0;
419 for (
const auto [col, coeff] : integer_objective_) {
420 constraint_manager_.SetObjectiveCoefficient(integer_variables_[col.value()],
422 objective_infinity_norm_ =
423 std::max(objective_infinity_norm_,
IntTypeAbs(coeff));
431 if (!parameters_.add_lp_constraints_lazily() &&
432 constraint_manager_.num_constraints() < 1e6) {
433 constraint_manager_.AddAllConstraintsToLp();
435 if (!CreateLpFromConstraintManager()) {
440 watcher_id_ = watcher_->Register(
this);
441 const int num_vars = integer_variables_.size();
442 orbit_indices_.clear();
443 for (
int i = 0;
i < num_vars;
i++) {
444 const IntegerVariable pos_var = integer_variables_[
i];
445 DCHECK(symmetrizer_->AppearInFoldedProblem(pos_var));
446 watcher_->WatchIntegerVariable(pos_var, watcher_id_,
i);
447 if (symmetrizer_->IsOrbitSumVar(pos_var)) {
448 orbit_indices_.push_back(symmetrizer_->OrbitIndex(pos_var));
451 if (objective_is_defined_) {
452 watcher_->WatchUpperBound(objective_cp_, watcher_id_);
454 watcher_->SetPropagatorPriority(watcher_id_, 2);
455 watcher_->AlwaysCallAtLevelZero(watcher_id_);
459 integer_trail_->RegisterReversibleClass(
this);
460 watcher_->RegisterReversibleInt(watcher_id_, &rev_optimal_constraints_size_);
463glop::ColIndex LinearProgrammingConstraint::GetMirrorVariable(
464 IntegerVariable positive_variable) {
466 return mirror_lp_variable_[positive_variable];
470 IntegerValue coeff) {
471 CHECK(!lp_constraint_is_registered_);
472 objective_is_defined_ =
true;
474 if (ivar != pos_var) coeff = -coeff;
475 integer_objective_.push_back({GetMirrorVariable(pos_var), coeff});
491bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
494 integer_lp_cols_.clear();
495 integer_lp_coeffs_.clear();
497 infinity_norms_.clear();
498 const auto& all_constraints = constraint_manager_.
AllConstraints();
499 for (
const auto index : constraint_manager_.
LpConstraints()) {
502 VLOG(1) <<
"Trivial infeasible bound in an LP constraint";
506 integer_lp_.
push_back(LinearConstraintInternal());
507 LinearConstraintInternal& new_ct = integer_lp_.back();
510 new_ct.lb_is_trivial = all_constraints[index].lb_is_trivial;
511 new_ct.ub_is_trivial = all_constraints[index].ub_is_trivial;
513 IntegerValue infinity_norm = 0;
514 infinity_norm = std::max(infinity_norm,
IntTypeAbs(ct.
lb));
515 infinity_norm = std::max(infinity_norm,
IntTypeAbs(ct.
ub));
516 new_ct.start_in_buffer = integer_lp_cols_.size();
521 new_ct.num_terms = size;
522 for (
int i = 0;
i < size; ++
i) {
524 const IntegerVariable var = ct.
vars[
i];
525 const IntegerValue coeff = ct.
coeffs[
i];
526 infinity_norm = std::max(infinity_norm,
IntTypeAbs(coeff));
527 integer_lp_cols_.push_back(GetMirrorVariable(var));
528 integer_lp_coeffs_.push_back(coeff);
530 infinity_norms_.
push_back(infinity_norm);
533 DCHECK(std::is_sorted(
534 integer_lp_cols_.data() + new_ct.start_in_buffer,
535 integer_lp_cols_.data() + new_ct.start_in_buffer + new_ct.num_terms));
541 objective_infinity_norm_ = 0;
542 for (
const auto& entry : integer_objective_) {
543 const IntegerVariable var = integer_variables_[entry.first.value()];
544 if (integer_trail_->IsFixedAtLevelZero(var)) {
545 integer_objective_offset_ +=
546 entry.second * integer_trail_->LevelZeroLowerBound(var);
549 objective_infinity_norm_ =
550 std::max(objective_infinity_norm_,
IntTypeAbs(entry.second));
551 integer_objective_[new_size++] = entry;
553 integer_objective_.resize(new_size);
554 objective_infinity_norm_ =
555 std::max(objective_infinity_norm_,
IntTypeAbs(integer_objective_offset_));
560 ComputeIntegerLpScalingFactors();
566 scaler_.ConfigureFromFactors(row_factors_, col_factors_);
567 scaler_.AverageCostScaling(&obj_with_slack_);
568 scaler_.ContainOneBoundScaling(simplex_.MutableLowerBounds(),
569 simplex_.MutableUpperBounds());
572 UpdateBoundsOfLpVariables();
577 if (parameters_.polish_lp_solution()) {
578 simplex_.ClearIntegralityScales();
579 const int num_vars = integer_variables_.size();
580 for (
int i = 0;
i < num_vars; ++
i) {
581 const IntegerVariable cp_var = integer_variables_[
i];
582 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(cp_var);
583 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(cp_var);
584 if (lb != 0 || ub != 1)
continue;
585 simplex_.SetIntegralityScale(
587 1.0 / scaler_.VariableScalingFactor(glop::ColIndex(
i)));
591 VLOG(3) <<
"LP relaxation: " << integer_lp_.size() <<
" x "
592 << integer_variables_.size() <<
". "
593 << constraint_manager_.AllConstraints().size()
594 <<
" Managed constraints.";
600void LinearProgrammingConstraint::ComputeIntegerLpScalingFactors() {
601 const int num_rows = integer_lp_.size();
602 const int num_cols = integer_variables_.size();
605 const double infinity = std::numeric_limits<double>::infinity();
606 row_factors_.assign(num_rows, 1.0);
607 col_factors_.assign(num_cols, 1.0);
610 IntegerValue* coeffs = integer_lp_coeffs_.data();
611 glop::ColIndex* cols = integer_lp_cols_.data();
612 double* row_factors = row_factors_.data();
613 double* col_factors = col_factors_.data();
615 col_min_.assign(num_cols, infinity);
616 col_max_.assign(num_cols, 0.0);
617 double* col_min = col_min_.data();
618 double* col_max = col_max_.data();
620 for (
int i = 0;
i < 4; ++
i) {
622 for (
int row = 0; row < num_rows; ++row) {
623 double min_scaled = +infinity;
624 double max_scaled = 0.0;
625 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
626 for (
int i = 0;
i < ct.num_terms; ++
i) {
627 const int index = ct.start_in_buffer +
i;
628 const int col = cols[index].value();
629 const double coeff =
static_cast<double>(coeffs[index].value());
630 const double scaled_magnitude = col_factors[col] * std::abs(coeff);
631 min_scaled = std::min(min_scaled, scaled_magnitude);
632 max_scaled = std::max(max_scaled, scaled_magnitude);
635 if (ct.num_terms == 0)
continue;
636 const Fractional factor(std::sqrt(max_scaled * min_scaled));
637 row_factors[row] = 1.0 / factor;
641 for (
int row = 0; row < num_rows; ++row) {
642 const double row_factor = row_factors[row];
643 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
644 for (
int i = 0;
i < ct.num_terms; ++
i) {
645 const int index = ct.start_in_buffer +
i;
646 const int col = cols[index].value();
647 const double coeff =
static_cast<double>(coeffs[index].value());
648 const double scaled_magnitude = row_factor * std::abs(coeff);
649 col_min[col] = std::min(col_min[col], scaled_magnitude);
650 col_max[col] = std::max(col_max[col], scaled_magnitude);
653 for (
int col = 0; col < num_cols; ++col) {
654 if (col_min[col] == infinity)
continue;
655 col_factors[col] = 1.0 / std::sqrt(col_min[col] * col_max[col]);
658 col_min[col] = infinity;
664 for (
int row = 0; row < num_rows; ++row) {
665 double max_scaled = 0.0;
666 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
667 for (
int i = 0;
i < ct.num_terms; ++
i) {
668 const int index = ct.start_in_buffer +
i;
669 const int col = cols[index].value();
670 const double coeff =
static_cast<double>(coeffs[index].value());
671 const double scaled_magnitude = col_factors[col] * std::abs(coeff);
672 max_scaled = std::max(max_scaled, scaled_magnitude);
674 if (ct.num_terms == 0)
continue;
675 row_factors[row] = 1.0 / max_scaled;
679 for (
int row = 0; row < num_rows; ++row) {
680 const double row_factor = row_factors[row];
681 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
682 for (
int i = 0;
i < ct.num_terms; ++
i) {
683 const int index = ct.start_in_buffer +
i;
684 const int col = cols[index].value();
685 const double coeff =
static_cast<double>(coeffs[index].value());
686 const double scaled_magnitude = row_factor * std::abs(coeff);
687 col_max[col] = std::max(col_max[col], scaled_magnitude);
690 for (
int col = 0; col < num_cols; ++col) {
691 if (col_max[col] == 0)
continue;
692 col_factors[col] = 1.0 / col_max[col];
696void LinearProgrammingConstraint::FillLpData() {
697 const int num_rows = integer_lp_.size();
698 const int num_cols = integer_variables_.size();
699 IntegerValue* coeffs = integer_lp_coeffs_.data();
700 glop::ColIndex* cols = integer_lp_cols_.data();
701 double* row_factors = row_factors_.data();
702 double* col_factors = col_factors_.data();
705 glop::CompactSparseMatrix* data = simplex_.MutableTransposedMatrixWithSlack();
706 data->Reset(glop::RowIndex(num_cols + num_rows));
707 for (
int row = 0; row < num_rows; ++row) {
708 const LinearConstraintInternal& ct = integer_lp_[RowIndex(row)];
709 const double row_factor = row_factors[row];
710 for (
int i = 0;
i < ct.num_terms; ++
i) {
711 const int index = ct.start_in_buffer +
i;
712 const int col = cols[index].value();
713 const double coeff =
static_cast<double>(coeffs[index].value());
714 const double scaled_coeff = row_factor * col_factors[col] * coeff;
715 data->AddEntryToCurrentColumn(RowIndex(col), scaled_coeff);
719 data->AddEntryToCurrentColumn(RowIndex(num_cols + row), 1.0);
722 data->CloseCurrentColumn();
726 const glop::ColIndex num_cols_with_slacks(num_rows + num_cols);
727 obj_with_slack_.assign(num_cols_with_slacks, 0.0);
728 for (
const auto [col, value] : integer_objective_) {
729 obj_with_slack_[col] =
ToDouble(value) * col_factors[col.value()];
733 simplex_.MutableLowerBounds()->resize(num_cols_with_slacks);
734 simplex_.MutableUpperBounds()->resize(num_cols_with_slacks);
735 Fractional* lb_with_slack = simplex_.MutableLowerBounds()->data();
736 Fractional* ub_with_slack = simplex_.MutableUpperBounds()->data();
737 const double infinity = std::numeric_limits<double>::infinity();
738 for (
int row = 0; row < integer_lp_.size(); ++row) {
739 const LinearConstraintInternal& ct = integer_lp_[glop::RowIndex(row)];
745 const double factor = row_factors[row];
746 lb_with_slack[num_cols + row] =
747 ct.ub_is_trivial ? -infinity :
ToDouble(-ct.ub) * factor;
748 ub_with_slack[num_cols + row] =
749 ct.lb_is_trivial ? +infinity :
ToDouble(-ct.lb) * factor;
758 const int num_vars = integer_variables_.size();
759 for (
int i = 0;
i < num_vars;
i++) {
760 const IntegerVariable cp_var = integer_variables_[
i];
761 const double factor = col_factors[
i];
763 ToDouble(integer_trail_->LevelZeroLowerBound(cp_var)) * factor;
765 ToDouble(integer_trail_->LevelZeroUpperBound(cp_var)) * factor;
769void LinearProgrammingConstraint::FillReducedCostReasonIn(
771 std::vector<IntegerLiteral>* integer_reason) {
772 integer_reason->clear();
773 const int num_vars = integer_variables_.size();
774 for (
int i = 0;
i < num_vars;
i++) {
775 const double rc = reduced_costs[glop::ColIndex(
i)];
776 if (rc > kLpEpsilon) {
777 integer_reason->push_back(
778 integer_trail_->LowerBoundAsLiteral(integer_variables_[
i]));
779 }
else if (rc < -kLpEpsilon) {
780 integer_reason->push_back(
781 integer_trail_->UpperBoundAsLiteral(integer_variables_[
i]));
785 integer_trail_->RemoveLevelZeroBounds(integer_reason);
790 if (level == 0) rev_optimal_constraints_size_ = 0;
791 optimal_constraints_.resize(rev_optimal_constraints_size_);
792 cumulative_optimal_constraint_sizes_.resize(rev_optimal_constraints_size_);
793 if (lp_solution_is_set_ && level < lp_solution_level_) {
794 lp_solution_is_set_ =
false;
797 if (level < previous_level_) {
798 lp_at_optimal_ =
false;
799 lp_objective_lower_bound_ = -std::numeric_limits<double>::infinity();
801 previous_level_ = level;
810 if (level == 0 && !lp_solution_is_set_ && !level_zero_lp_solution_.empty()) {
811 lp_solution_is_set_ =
true;
812 lp_solution_ = level_zero_lp_solution_;
813 lp_solution_level_ = 0;
817 if (expanded_lp_solution_.size() < integer_trail_->NumIntegerVariables()) {
818 expanded_lp_solution_.resize(integer_trail_->NumIntegerVariables());
820 for (IntegerVariable
i(0);
i < integer_trail_->NumIntegerVariables(); ++
i) {
821 if (integer_trail_->IsFixed(
i)) {
822 expanded_lp_solution_[
i] =
ToDouble(integer_trail_->LowerBound(
i));
825 for (
int i = 0;
i < lp_solution_.size();
i++) {
826 const IntegerVariable var = extended_integer_variables_[
i];
827 expanded_lp_solution_[var] = lp_solution_[
i];
828 expanded_lp_solution_[
NegationOf(var)] = -lp_solution_[
i];
834 cut_generators_.push_back(std::move(generator));
838 const std::vector<int>& watch_indices) {
839 if (!enabled_)
return true;
849 if (!cumulative_optimal_constraint_sizes_.empty()) {
850 const double current_size =
851 static_cast<double>(cumulative_optimal_constraint_sizes_.back());
852 const double low_limit = 1e7;
853 if (current_size > low_limit) {
856 const double num_enqueues =
static_cast<double>(integer_trail_->Index());
857 if ((current_size - low_limit) > 100 * num_enqueues)
return true;
861 if (!lp_solution_is_set_) {
867 if (trail_->CurrentDecisionLevel() == 0 && !lp_at_level_zero_is_final_) {
873 for (
const int index : watch_indices) {
875 ToDouble(integer_trail_->LowerBound(integer_variables_[index]));
877 ToDouble(integer_trail_->UpperBound(integer_variables_[index]));
878 const double value = lp_solution_[index];
879 if (value < lb - kCpEpsilon || value > ub + kCpEpsilon)
return Propagate();
900 glop::ColIndex var) {
905 IntegerVariable variable)
const {
906 return lp_solution_[mirror_lp_variable_[variable].value()];
909void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
910 const int num_vars = integer_variables_.size();
913 for (
int i = 0;
i < num_vars;
i++) {
914 const IntegerVariable cp_var = integer_variables_[
i];
916 static_cast<double>(integer_trail_->
LowerBound(cp_var).value());
918 static_cast<double>(integer_trail_->
UpperBound(cp_var).value());
920 lb_with_slack[
i] = lb * factor;
921 ub_with_slack[
i] = ub * factor;
925bool LinearProgrammingConstraint::SolveLp() {
928 lp_at_level_zero_is_final_ =
false;
931 const double unscaling_factor = 1.0 / scaler_.ObjectiveScalingFactor();
932 const double offset_before_unscaling =
933 ToDouble(integer_objective_offset_) * scaler_.ObjectiveScalingFactor();
934 const auto status = simplex_.MinimizeFromTransposedMatrixWithSlack(
935 obj_with_slack_, unscaling_factor, offset_before_unscaling, time_limit_);
939 VLOG(2) <<
"The LP solver returned abnormal, resolving from scratch";
940 simplex_.ClearStateForNextSolve();
941 const auto status = simplex_.MinimizeFromTransposedMatrixWithSlack(
942 obj_with_slack_, unscaling_factor, offset_before_unscaling,
946 state_ = simplex_.GetState();
947 total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
949 VLOG(2) <<
"The LP solver encountered an error: " << status.error_message();
950 simplex_.ClearStateForNextSolve();
953 average_degeneracy_.AddData(CalculateDegeneracy());
954 if (average_degeneracy_.CurrentAverage() >= 1000.0) {
955 VLOG(2) <<
"High average degeneracy: "
956 << average_degeneracy_.CurrentAverage();
959 const int status_as_int =
static_cast<int>(simplex_.GetProblemStatus());
960 if (status_as_int >= num_solves_by_status_.size()) {
961 num_solves_by_status_.resize(status_as_int + 1);
964 num_solves_by_status_[status_as_int]++;
965 VLOG(2) <<
DimensionString() <<
" lvl:" << trail_->CurrentDecisionLevel()
966 <<
" " << simplex_.GetProblemStatus()
967 <<
" iter:" << simplex_.GetNumberOfIterations()
968 <<
" obj:" << simplex_.GetObjectiveValue() <<
" scaled:"
969 << objective_definition_->ScaleObjective(
970 simplex_.GetObjectiveValue());
974 lp_objective_lower_bound_ = simplex_.GetObjectiveValue();
981 parameters_.stop_after_root_propagation()) {
982 lp_solution_is_set_ =
true;
983 lp_solution_level_ = trail_->CurrentDecisionLevel();
984 const int num_vars = integer_variables_.size();
985 const auto reduced_costs = simplex_.GetReducedCosts().const_view();
986 for (
int i = 0;
i < num_vars;
i++) {
987 const glop::ColIndex col(
i);
988 const IntegerVariable var = integer_variables_[
i];
991 lp_solution_[
i] = value;
992 expanded_lp_solution_[var] = value;
993 expanded_lp_solution_[
NegationOf(var)] = -value;
996 scaler_.UnscaleReducedCost(col, reduced_costs[col]);
997 lp_reduced_cost_[
i] = rc;
998 expanded_reduced_costs_[var] = rc;
999 expanded_reduced_costs_[
NegationOf(var)] = -rc;
1004 for (
const int orbit_index : orbit_indices_) {
1005 const IntegerVariable sum_var = symmetrizer_->OrbitSumVar(orbit_index);
1006 const double lp_value = expanded_lp_solution_[sum_var];
1007 const absl::Span<const IntegerVariable> orbit =
1008 symmetrizer_->Orbit(orbit_index);
1016 bool to_lower =
false;
1017 bool to_upper =
false;
1018 if (trail_->CurrentDecisionLevel() > 0) {
1020 static_cast<double>(integer_trail_->LowerBound(sum_var).value());
1022 static_cast<double>(integer_trail_->UpperBound(sum_var).value());
1023 if (std::abs(lp_value - lb) < 1e-2) {
1025 }
else if (std::abs(lp_value - ub) < 1e-2) {
1043 const double even_split = lp_value /
static_cast<double>(orbit.size());
1047 const double new_rc = expanded_reduced_costs_[sum_var];
1049 for (
const IntegerVariable var : orbit) {
1050 const glop::ColIndex col = GetMirrorVariable(var);
1052 double new_value = even_split;
1055 static_cast<double>(integer_trail_->LowerBound(var).value());
1056 }
else if (to_upper) {
1058 static_cast<double>(integer_trail_->UpperBound(var).value());
1061 lp_solution_[col.value()] = new_value;
1062 expanded_lp_solution_[var] = new_value;
1063 expanded_lp_solution_[
NegationOf(var)] = -new_value;
1065 lp_reduced_cost_[col.value()] = new_rc;
1066 expanded_reduced_costs_[var] = new_rc;
1067 expanded_reduced_costs_[
NegationOf(var)] = -new_rc;
1072 lp_solution_is_integer_ =
true;
1073 for (
const double value : lp_solution_) {
1074 if (std::abs(value - std::round(value)) > kCpEpsilon) {
1075 lp_solution_is_integer_ =
false;
1080 if (lp_solution_level_ == 0) {
1081 level_zero_lp_solution_ = lp_solution_;
1088bool LinearProgrammingConstraint::AnalyzeLp() {
1091 if (parameters_.use_exact_lp_reason()) {
1092 return PropagateExactDualRay();
1094 FillReducedCostReasonIn(simplex_.GetDualRayRowCombination(),
1096 return integer_trail_->ReportConflict(integer_reason_);
1100 UpdateSimplexIterationLimit(10, 1000);
1104 if (objective_is_defined_ &&
1109 if (parameters_.use_exact_lp_reason()) {
1110 if (!PropagateExactLpReason())
return false;
1113 if (VLOG_IS_ON(2)) {
1114 const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1115 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1116 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1117 const IntegerValue propagated_lb =
1118 integer_trail_->LowerBound(objective_cp_);
1119 if (approximate_new_lb > propagated_lb) {
1120 VLOG(2) <<
"LP objective [ " <<
ToDouble(propagated_lb) <<
", "
1121 <<
ToDouble(integer_trail_->UpperBound(objective_cp_))
1122 <<
" ] approx_lb += "
1123 <<
ToDouble(approximate_new_lb - propagated_lb) <<
" gap: "
1124 << integer_trail_->UpperBound(objective_cp_) - propagated_lb;
1131 FillReducedCostReasonIn(simplex_.GetReducedCosts(), &integer_reason_);
1132 const double objective_cp_ub =
1133 ToDouble(integer_trail_->UpperBound(objective_cp_));
1134 const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1135 ReducedCostStrengtheningDeductions(objective_cp_ub -
1136 relaxed_optimal_objective);
1137 if (!deductions_.empty()) {
1138 deductions_reason_ = integer_reason_;
1139 deductions_reason_.push_back(
1140 integer_trail_->UpperBoundAsLiteral(objective_cp_));
1144 const IntegerValue approximate_new_lb(
static_cast<int64_t
>(
1145 std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1146 if (approximate_new_lb > integer_trail_->LowerBound(objective_cp_)) {
1147 const IntegerLiteral deduction =
1149 if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
1155 if (!deductions_.empty()) {
1156 const int trail_index_with_same_reason = integer_trail_->Index();
1157 for (
const IntegerLiteral deduction : deductions_) {
1158 if (!integer_trail_->Enqueue(deduction, {}, deductions_reason_,
1159 trail_index_with_same_reason)) {
1168 if (compute_reduced_cost_averages_ &&
1170 CHECK(lp_solution_is_set_);
1171 UpdateAverageReducedCosts();
1180 if (objective_is_defined_ &&
1181 objective_definition_->objective_var == objective_cp_ &&
1182 trail_->CurrentDecisionLevel() == 0) {
1183 shared_response_manager_->UpdateInnerObjectiveBounds(
1184 model_->Name(), integer_trail_->LowerBound(objective_cp_),
1185 integer_trail_->UpperBound(objective_cp_));
1197bool LinearProgrammingConstraint::PreprocessCut(IntegerVariable first_slack,
1200 cut->ComplementForPositiveCoefficients();
1202 problem_proven_infeasible_by_cuts_ =
true;
1209 for (
const CutTerm& term : cut->terms) {
1210 reachable_.Add(term.coeff.value());
1214 if (cut->rhs < absl::int128(reachable_.LastValue()) &&
1215 !reachable_.MightBeReachable(
static_cast<int64_t
>(cut->rhs))) {
1216 problem_proven_infeasible_by_cuts_ =
true;
1220 bool some_fixed_terms =
false;
1221 bool some_fractional_positions =
false;
1222 for (CutTerm& term : cut->terms) {
1223 const absl::int128 magnitude128 = term.coeff.value();
1224 const absl::int128 range =
1225 absl::int128(term.bound_diff.value()) * magnitude128;
1227 IntegerValue new_diff = term.bound_diff;
1228 if (range > cut->rhs) {
1229 new_diff =
static_cast<int64_t
>(cut->rhs / magnitude128);
1233 absl::int128 rest128 =
1234 cut->rhs - absl::int128(new_diff.value()) * magnitude128;
1235 while (rest128 < absl::int128(reachable_.LastValue()) &&
1236 !reachable_.MightBeReachable(
static_cast<int64_t
>(rest128))) {
1237 ++total_num_eq_propagations_;
1238 CHECK_GT(new_diff, 0);
1240 rest128 += magnitude128;
1244 if (new_diff < term.bound_diff) {
1245 term.bound_diff = new_diff;
1247 const IntegerVariable var = term.expr_vars[0];
1248 if (var < first_slack) {
1250 ++total_num_cut_propagations_;
1253 if (term.expr_coeffs[0] == 1) {
1255 if (!integer_trail_->Enqueue(
1257 var, term.bound_diff - term.expr_offset),
1259 problem_proven_infeasible_by_cuts_ =
true;
1263 CHECK_EQ(term.expr_coeffs[0], -1);
1265 if (!integer_trail_->Enqueue(
1267 var, term.expr_offset - term.bound_diff),
1269 problem_proven_infeasible_by_cuts_ =
true;
1277 const int slack_index = (var.value() - first_slack.value()) / 2;
1278 const glop::RowIndex row = tmp_slack_rows_[slack_index];
1279 if (term.expr_coeffs[0] == 1) {
1281 const IntegerValue new_ub = term.bound_diff - term.expr_offset;
1282 if (constraint_manager_.UpdateConstraintUb(row, new_ub)) {
1283 integer_lp_[row].ub = new_ub;
1287 CHECK_EQ(term.expr_coeffs[0], -1);
1288 const IntegerValue new_lb = term.expr_offset - term.bound_diff;
1289 if (constraint_manager_.UpdateConstraintLb(row, new_lb)) {
1290 integer_lp_[row].lb = new_lb;
1296 if (term.bound_diff == 0) {
1297 some_fixed_terms =
true;
1299 if (term.IsFractional()) {
1300 some_fractional_positions =
true;
1306 if (some_fixed_terms) {
1308 for (
const CutTerm& term : cut->terms) {
1309 if (term.bound_diff == 0)
continue;
1310 cut->terms[new_size++] = term;
1312 cut->terms.resize(new_size);
1314 return some_fractional_positions;
1317bool LinearProgrammingConstraint::AddCutFromConstraints(
1318 absl::string_view name,
1319 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers) {
1329 IntegerValue cut_ub;
1330 if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
1332 ++num_cut_overflows_;
1333 VLOG(1) <<
"Issue, overflow!";
1344 tmp_scattered_vector_.ConvertToCutData(
1345 cut_ub.value(), extended_integer_variables_, lp_solution_, integer_trail_,
1350 ImpliedBoundsProcessor* ib_processor =
nullptr;
1352 bool some_ints =
false;
1353 bool some_fractional_positions =
false;
1354 for (
const CutTerm& term : base_ct_.terms) {
1355 if (term.bound_diff > 1) some_ints =
true;
1356 if (term.IsFractional()) {
1357 some_fractional_positions =
true;
1362 if (!some_fractional_positions)
return false;
1363 if (some_ints) ib_processor = &implied_bounds_processor_;
1368 const IntegerVariable first_slack(expanded_lp_solution_.size());
1369 CHECK_EQ(first_slack.value() % 2, 0);
1370 tmp_slack_rows_.clear();
1371 for (
const auto [row, coeff] : integer_multipliers) {
1372 if (integer_lp_[row].lb == integer_lp_[row].ub)
continue;
1375 entry.coeff = coeff > 0 ? coeff : -coeff;
1376 entry.lp_value = 0.0;
1377 entry.bound_diff = integer_lp_[row].ub - integer_lp_[row].lb;
1378 entry.expr_vars[0] =
1379 first_slack + 2 * IntegerVariable(tmp_slack_rows_.size());
1380 entry.expr_coeffs[1] = 0;
1381 const double activity = scaler_.UnscaleConstraintActivity(
1382 row, simplex_.GetConstraintActivity(row));
1385 entry.lp_value =
ToDouble(integer_lp_[row].ub) - activity;
1386 entry.expr_coeffs[0] = IntegerValue(-1);
1387 entry.expr_offset = integer_lp_[row].ub;
1390 entry.lp_value = activity -
ToDouble(integer_lp_[row].lb);
1391 entry.expr_coeffs[0] = IntegerValue(1);
1392 entry.expr_offset = -integer_lp_[row].lb;
1395 base_ct_.terms.push_back(entry);
1396 tmp_slack_rows_.push_back(row);
1400 if (!PreprocessCut(first_slack, &base_ct_))
return false;
1403 if (base_ct_.rhs == 1)
return false;
1407 double activity = 0.0;
1409 for (
const CutTerm& term : base_ct_.terms) {
1410 const double coeff =
ToDouble(term.coeff);
1411 activity += term.lp_value * coeff;
1412 norm += coeff * coeff;
1414 if (std::abs(activity -
static_cast<double>(base_ct_.rhs)) / norm > 1e-4) {
1415 VLOG(1) <<
"Cut not tight " << activity
1416 <<
" <= " <<
static_cast<double>(base_ct_.rhs);
1421 bool at_least_one_added =
false;
1422 DCHECK(base_ct_.AllCoefficientsArePositive());
1427 if (integer_multipliers.size() == 1 && parameters_.add_rlt_cuts()) {
1428 if (rlt_cut_helper_.TrySimpleSeparation(base_ct_)) {
1429 at_least_one_added |= PostprocessAndAddCut(
1430 absl::StrCat(name,
"_RLT"), rlt_cut_helper_.Info(), first_slack,
1431 rlt_cut_helper_.cut());
1436 if (ib_processor !=
nullptr) {
1437 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1438 ib_processor =
nullptr;
1445 cover_cut_helper_.ClearCache();
1447 if (cover_cut_helper_.TrySingleNodeFlow(base_ct_, ib_processor)) {
1448 at_least_one_added |= PostprocessAndAddCut(
1449 absl::StrCat(name,
"_FF"), cover_cut_helper_.Info(), first_slack,
1450 cover_cut_helper_.cut());
1452 if (cover_cut_helper_.TrySimpleKnapsack(base_ct_, ib_processor)) {
1453 at_least_one_added |= PostprocessAndAddCut(
1454 absl::StrCat(name,
"_K"), cover_cut_helper_.Info(), first_slack,
1455 cover_cut_helper_.cut());
1460 if (cover_cut_helper_.TryWithLetchfordSouliLifting(base_ct_,
1462 at_least_one_added |= PostprocessAndAddCut(
1463 absl::StrCat(name,
"_KL"), cover_cut_helper_.Info(), first_slack,
1464 cover_cut_helper_.cut());
1470 base_ct_.ComplementForSmallerLpValues();
1472 RoundingOptions options;
1473 options.max_scaling = parameters_.max_integer_rounding_scaling();
1474 options.use_ib_before_heuristic =
false;
1475 if (integer_rounding_cut_helper_.ComputeCut(options, base_ct_,
1477 at_least_one_added |= PostprocessAndAddCut(
1478 absl::StrCat(name,
"_R"), integer_rounding_cut_helper_.Info(),
1479 first_slack, integer_rounding_cut_helper_.cut());
1482 options.use_ib_before_heuristic =
true;
1483 options.prefer_positive_ib =
false;
1484 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.ComputeCut(
1485 options, base_ct_, ib_processor)) {
1486 at_least_one_added |= PostprocessAndAddCut(
1487 absl::StrCat(name,
"_RB"), integer_rounding_cut_helper_.Info(),
1488 first_slack, integer_rounding_cut_helper_.cut());
1491 options.use_ib_before_heuristic =
true;
1492 options.prefer_positive_ib =
true;
1493 if (ib_processor !=
nullptr && integer_rounding_cut_helper_.ComputeCut(
1494 options, base_ct_, ib_processor)) {
1495 at_least_one_added |= PostprocessAndAddCut(
1496 absl::StrCat(name,
"_RBP"), integer_rounding_cut_helper_.Info(),
1497 first_slack, integer_rounding_cut_helper_.cut());
1501 return at_least_one_added;
1504bool LinearProgrammingConstraint::PostprocessAndAddCut(
1505 const std::string& name,
const std::string& info,
1506 IntegerVariable first_slack,
const CutData& cut) {
1507 if (cut.rhs > absl::int128(std::numeric_limits<int64_t>::max()) ||
1508 cut.rhs < absl::int128(std::numeric_limits<int64_t>::min())) {
1509 VLOG(2) <<
"RHS overflow " << name <<
" " << info;
1510 ++num_cut_overflows_;
1518 if (cut.ComputeViolation() < 1e-4) {
1519 VLOG(3) <<
"Bad cut " << name <<
" " << info;
1525 tmp_scattered_vector_.ClearAndResize(extended_integer_variables_.size());
1526 IntegerValue cut_ub =
static_cast<int64_t
>(cut.rhs);
1527 for (
const CutTerm& term : cut.terms) {
1528 if (term.coeff == 0)
continue;
1529 if (!
AddProductTo(-term.coeff, term.expr_offset, &cut_ub)) {
1530 VLOG(2) <<
"Overflow in conversion";
1531 ++num_cut_overflows_;
1534 for (
int i = 0;
i < 2; ++
i) {
1535 if (term.expr_coeffs[
i] == 0)
continue;
1536 const IntegerVariable var = term.expr_vars[
i];
1537 const IntegerValue coeff =
1538 CapProd(term.coeff.value(), term.expr_coeffs[
i].value());
1540 VLOG(2) <<
"Overflow in conversion";
1541 ++num_cut_overflows_;
1546 if (var < first_slack) {
1549 tmp_scattered_vector_.Add(col, coeff);
1551 tmp_scattered_vector_.Add(col, -coeff);
1556 const int slack_index = (var.value() - first_slack.value()) / 2;
1557 const glop::RowIndex row = tmp_slack_rows_[slack_index];
1558 if (!tmp_scattered_vector_.AddLinearExpressionMultiple(
1559 coeff, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
1560 infinity_norms_[row])) {
1561 VLOG(2) <<
"Overflow in slack removal";
1562 ++num_cut_overflows_;
1572 LinearConstraint converted_cut =
1573 tmp_scattered_vector_.ConvertToLinearConstraint(
1574 extended_integer_variables_, cut_ub);
1579 top_n_cuts_.AddCut(std::move(converted_cut), name, expanded_lp_solution_);
1582 return constraint_manager_.AddCut(std::move(converted_cut), name, info);
1589void LinearProgrammingConstraint::AddCGCuts() {
1590 std::vector<std::pair<RowIndex, double>> sorted_columns;
1591 const RowIndex num_rows(integer_lp_.size());
1593 for (RowIndex index(0); index < num_rows; ++index) {
1594 const ColIndex basis_col = simplex_.GetBasis(index);
1603 if (basis_col >= integer_variables_.size())
continue;
1608 simplex_.GetVariableValue(basis_col) /
1609 scaler_.VariableScalingFactorWithSlack(basis_col);
1617 const double fractionality = std::abs(lp_value - std::round(lp_value));
1618 if (fractionality < 0.01)
continue;
1620 const double score = fractionality * (1.0 - fractionality) / norms[index];
1621 sorted_columns.push_back({index, score});
1623 absl::c_sort(sorted_columns, [](
const std::pair<RowIndex, double>& a,
1624 const std::pair<RowIndex, double>&
b) {
1625 return a.second >
b.second;
1629 for (
const auto [index, _] : sorted_columns) {
1630 if (time_limit_->LimitReached())
return;
1635 tmp_lp_multipliers_.clear();
1636 const glop::ScatteredRow& lambda = simplex_.GetUnitRowLeftInverse(index);
1637 if (lambda.non_zeros.empty()) {
1638 for (RowIndex row(0); row < num_rows; ++row) {
1640 if (std::abs(value) < kZeroTolerance)
continue;
1641 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
1644 for (
const ColIndex col : lambda.non_zeros) {
1646 const double value = lambda.values[col];
1647 if (std::abs(value) < kZeroTolerance)
continue;
1648 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
1653 if (tmp_lp_multipliers_.size() <= 1)
continue;
1655 IntegerValue scaling;
1656 for (
int i = 0;
i < 2; ++
i) {
1657 tmp_cg_multipliers_ = tmp_lp_multipliers_;
1663 for (std::pair<RowIndex, double>& p : tmp_cg_multipliers_) {
1664 p.second = -p.second;
1675 IgnoreTrivialConstraintMultipliers(&tmp_cg_multipliers_);
1676 if (tmp_cg_multipliers_.size() <= 1)
continue;
1678 ScaleMultipliers(tmp_cg_multipliers_,
1680 &tmp_integer_multipliers_);
1682 if (AddCutFromConstraints(
"CG", tmp_integer_multipliers_)) {
1689 if (num_added > 10)
break;
1702void LinearProgrammingConstraint::AddObjectiveCut() {
1703 if (integer_objective_.size() <= 1)
return;
1708 const double obj_lp_value = simplex_.GetObjectiveValue();
1709 const IntegerValue obj_lower_bound =
1710 integer_trail_->LevelZeroLowerBound(objective_cp_);
1711 if (obj_lp_value + 1.0 >=
ToDouble(obj_lower_bound))
return;
1714 LinearConstraint objective_ct;
1716 objective_ct.ub = integer_objective_offset_ -
1717 integer_trail_->LevelZeroLowerBound(objective_cp_);
1718 IntegerValue obj_coeff_magnitude(0);
1719 objective_ct.resize(integer_objective_.size());
1721 for (
const auto& [col, coeff] : integer_objective_) {
1722 const IntegerVariable var = integer_variables_[col.value()];
1723 objective_ct.vars[
i] = var;
1724 objective_ct.coeffs[
i] = -coeff;
1725 obj_coeff_magnitude = std::max(obj_coeff_magnitude,
IntTypeAbs(coeff));
1729 if (!base_ct_.FillFromLinearConstraint(objective_ct, expanded_lp_solution_,
1737 if (obj_coeff_magnitude < 1e9 &&
1738 constraint_manager_.AddCut(std::move(objective_ct),
"Objective")) {
1744 ImpliedBoundsProcessor* ib_processor =
nullptr;
1746 bool some_ints =
false;
1747 bool some_relevant_positions =
false;
1748 for (
const CutTerm& term : base_ct_.terms) {
1749 if (term.bound_diff > 1) some_ints =
true;
1750 if (term.HasRelevantLpValue()) some_relevant_positions =
true;
1754 if (!some_relevant_positions)
return;
1755 if (some_ints) ib_processor = &implied_bounds_processor_;
1759 const IntegerVariable first_slack(
1760 std::numeric_limits<IntegerVariable::ValueType>::max());
1761 if (ib_processor !=
nullptr) {
1762 if (!ib_processor->CacheDataForCut(first_slack, &base_ct_)) {
1763 ib_processor =
nullptr;
1768 base_ct_.ComplementForPositiveCoefficients();
1769 cover_cut_helper_.ClearCache();
1770 if (cover_cut_helper_.TrySimpleKnapsack(base_ct_, ib_processor)) {
1771 PostprocessAndAddCut(
"Objective_K", cover_cut_helper_.Info(), first_slack,
1772 cover_cut_helper_.cut());
1776 RoundingOptions options;
1777 options.max_scaling = parameters_.max_integer_rounding_scaling();
1778 base_ct_.ComplementForSmallerLpValues();
1779 if (integer_rounding_cut_helper_.ComputeCut(options, base_ct_,
1781 PostprocessAndAddCut(
"Objective_R", integer_rounding_cut_helper_.Info(),
1782 first_slack, integer_rounding_cut_helper_.cut());
1786void LinearProgrammingConstraint::AddMirCuts() {
1801 util_intops::StrongVector<ColIndex, IntegerValue> dense_cut(
1802 integer_variables_.size(), IntegerValue(0));
1803 SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1807 const int num_cols = integer_variables_.size();
1808 const int num_rows = integer_lp_.size();
1809 std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1810 util_intops::StrongVector<RowIndex, double> row_weights(num_rows, 0.0);
1811 Fractional* lb_with_slack = simplex_.MutableLowerBounds()->data();
1812 Fractional* ub_with_slack = simplex_.MutableUpperBounds()->data();
1813 for (RowIndex row(0); row < num_rows; ++row) {
1820 const auto status = simplex_.GetConstraintStatus(row);
1821 const double activity = simplex_.GetConstraintActivity(row);
1822 const double ct_lb = -ub_with_slack[num_cols + row.value()];
1823 const double ct_ub = -lb_with_slack[num_cols + row.value()];
1824 if (activity > ct_ub - 1e-4 ||
1827 base_rows.push_back({row, IntegerValue(1)});
1829 if (activity < ct_lb + 1e-4 ||
1832 base_rows.push_back({row, IntegerValue(-1)});
1850 row_weights[row] = std::max(1e-8, std::abs(simplex_.GetDualValue(row)));
1856 int64_t dtime_num_entries = 0;
1857 std::shuffle(base_rows.begin(), base_rows.end(), *random_);
1859 std::vector<double> weights;
1860 util_intops::StrongVector<RowIndex, bool> used_rows;
1861 std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1862 const auto matrix = simplex_.MatrixWithSlack().view();
1863 for (
const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1864 if (time_limit_->LimitReached())
break;
1865 if (dtime_num_entries > 1e7)
break;
1867 const glop::RowIndex base_row = entry.first;
1868 const IntegerValue multiplier = entry.second;
1877 integer_multipliers = {entry};
1878 if ((multiplier > 0 && !integer_lp_[base_row].ub_is_trivial) ||
1879 (multiplier < 0 && !integer_lp_[base_row].lb_is_trivial)) {
1880 if (AddCutFromConstraints(
"MIR_1", integer_multipliers))
continue;
1884 for (
const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1885 dense_cut[col] = IntegerValue(0);
1887 non_zeros_.SparseClearAll();
1890 const LinearConstraintInternal& ct = integer_lp_[entry.first];
1891 for (
int i = 0;
i < ct.num_terms; ++
i) {
1892 const int index = ct.start_in_buffer +
i;
1893 const ColIndex col = integer_lp_cols_[index];
1894 const IntegerValue coeff = integer_lp_coeffs_[index];
1895 non_zeros_.Set(col);
1896 dense_cut[col] += coeff * multiplier;
1899 used_rows.
assign(num_rows,
false);
1900 used_rows[entry.first] =
true;
1905 const int kMaxAggregation = 5;
1906 for (
int i = 0;
i < kMaxAggregation; ++
i) {
1909 IntegerValue max_magnitude(0);
1911 std::vector<ColIndex> col_candidates;
1912 for (
const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1913 if (dense_cut[col] == 0)
continue;
1915 max_magnitude = std::max(max_magnitude,
IntTypeAbs(dense_cut[col]));
1916 const int col_degree = matrix.ColumnNumEntries(col).value();
1917 if (col_degree <= 1)
continue;
1922 const IntegerVariable var = integer_variables_[col.value()];
1923 const double lp_value = expanded_lp_solution_[var];
1924 const double lb =
ToDouble(integer_trail_->LevelZeroLowerBound(var));
1925 const double ub =
ToDouble(integer_trail_->LevelZeroUpperBound(var));
1926 const double bound_distance = std::min(ub - lp_value, lp_value - lb);
1927 if (bound_distance > 1e-2) {
1928 weights.push_back(bound_distance);
1929 col_candidates.push_back(col);
1932 if (col_candidates.empty())
break;
1934 const ColIndex var_to_eliminate =
1938 std::vector<RowIndex> possible_rows;
1940 for (
const auto entry_index : matrix.Column(var_to_eliminate)) {
1941 const RowIndex row = matrix.EntryRow(entry_index);
1947 if (used_rows[row])
continue;
1948 used_rows[row] =
true;
1955 bool add_row =
false;
1956 if (!integer_lp_[row].ub_is_trivial) {
1958 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1960 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1963 if (!integer_lp_[row].lb_is_trivial) {
1965 if (dense_cut[var_to_eliminate] > 0) add_row =
true;
1967 if (dense_cut[var_to_eliminate] < 0) add_row =
true;
1971 possible_rows.push_back(row);
1972 weights.push_back(row_weights[row]);
1975 if (possible_rows.empty())
break;
1977 const RowIndex row_to_combine =
1981 IntegerValue to_combine_coeff = 0;
1982 const LinearConstraintInternal& ct_to_combine =
1983 integer_lp_[row_to_combine];
1984 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
1985 const int index = ct_to_combine.start_in_buffer +
i;
1986 if (integer_lp_cols_[index] == var_to_eliminate) {
1987 to_combine_coeff = integer_lp_coeffs_[index];
1991 CHECK_NE(to_combine_coeff, 0);
1993 IntegerValue mult1 = -to_combine_coeff;
1994 IntegerValue mult2 = dense_cut[var_to_eliminate];
2001 const IntegerValue gcd = IntegerValue(
2011 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
2012 max_magnitude = std::max(max_magnitude,
IntTypeAbs(entry.second));
2014 if (
CapAdd(
CapProd(max_magnitude.value(), std::abs(mult1.value())),
2015 CapProd(infinity_norms_[row_to_combine].value(),
2016 std::abs(mult2.value()))) ==
2017 std::numeric_limits<int64_t>::max()) {
2021 for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
2022 dtime_num_entries += integer_lp_[entry.first].num_terms;
2023 entry.second *= mult1;
2025 dtime_num_entries += integer_lp_[row_to_combine].num_terms;
2026 integer_multipliers.push_back({row_to_combine, mult2});
2029 if (AddCutFromConstraints(absl::StrCat(
"MIR_",
i + 2),
2030 integer_multipliers)) {
2036 if (
i + 1 == kMaxAggregation)
break;
2038 for (ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
2039 dense_cut[col] *= mult1;
2041 for (
int i = 0;
i < ct_to_combine.num_terms; ++
i) {
2042 const int index = ct_to_combine.start_in_buffer +
i;
2043 const ColIndex col = integer_lp_cols_[index];
2044 const IntegerValue coeff = integer_lp_coeffs_[index];
2045 non_zeros_.Set(col);
2046 dense_cut[col] += coeff * mult2;
2052void LinearProgrammingConstraint::AddZeroHalfCuts() {
2053 if (time_limit_->LimitReached())
return;
2055 tmp_lp_values_.clear();
2056 tmp_var_lbs_.clear();
2057 tmp_var_ubs_.clear();
2058 for (
const IntegerVariable var : integer_variables_) {
2059 tmp_lp_values_.push_back(expanded_lp_solution_[var]);
2060 tmp_var_lbs_.push_back(integer_trail_->LevelZeroLowerBound(var));
2061 tmp_var_ubs_.push_back(integer_trail_->LevelZeroUpperBound(var));
2065 zero_half_cut_helper_.ProcessVariables(tmp_lp_values_, tmp_var_lbs_,
2067 for (glop::RowIndex row(0); row < integer_lp_.size(); ++row) {
2070 const auto status = simplex_.GetConstraintStatus(row);
2074 zero_half_cut_helper_.AddOneConstraint(
2075 row, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2076 integer_lp_[row].lb, integer_lp_[row].ub);
2078 for (
const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
2079 zero_half_cut_helper_.InterestingCandidates(random_)) {
2080 if (time_limit_->LimitReached())
break;
2086 AddCutFromConstraints(
"ZERO_HALF", multipliers);
2090void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
2091 const int64_t min_iter,
const int64_t max_iter) {
2092 if (parameters_.linearization_level() < 2)
return;
2093 const int64_t num_degenerate_columns = CalculateDegeneracy();
2094 const int64_t num_cols = simplex_.GetProblemNumCols().value();
2095 if (num_cols <= 0) {
2098 CHECK_GT(num_cols, 0);
2099 const int64_t decrease_factor = (10 * num_degenerate_columns) / num_cols;
2104 if (is_degenerate_) {
2105 next_simplex_iter_ /= std::max(int64_t{1}, decrease_factor);
2107 next_simplex_iter_ *= 2;
2110 if (is_degenerate_) {
2111 next_simplex_iter_ /= std::max(int64_t{1}, 2 * decrease_factor);
2115 next_simplex_iter_ = num_cols / 40;
2118 next_simplex_iter_ =
2119 std::max(min_iter, std::min(max_iter, next_simplex_iter_));
2123 if (!enabled_)
return true;
2124 if (time_limit_->LimitReached())
return true;
2125 UpdateBoundsOfLpVariables();
2129 if ( (
false) && objective_is_defined_) {
2137 simplex_params_.set_objective_upper_limit(
2138 static_cast<double>(integer_trail_->UpperBound(objective_cp_).value() +
2139 100.0 * kCpEpsilon));
2145 if (trail_->CurrentDecisionLevel() == 0) {
2146 simplex_params_.set_max_number_of_iterations(
2147 parameters_.root_lp_iterations());
2149 simplex_params_.set_max_number_of_iterations(next_simplex_iter_);
2152 simplex_.SetParameters(simplex_params_);
2153 if (!SolveLp())
return true;
2154 if (!AnalyzeLp())
return false;
2157 const int max_cuts_rounds = trail_->CurrentDecisionLevel() == 0
2158 ? parameters_.max_cut_rounds_at_level_zero()
2162 cuts_round < max_cuts_rounds) {
2167 if (parameters_.cut_level() > 0 &&
2168 (num_solves_ > 1 || !parameters_.add_lp_constraints_lazily())) {
2170 implied_bounds_processor_.RecomputeCacheAndSeparateSomeImpliedBoundCuts(
2171 expanded_lp_solution_);
2172 if (parameters_.add_rlt_cuts()) {
2173 rlt_cut_helper_.Initialize(extended_integer_variables_);
2180 const int level = trail_->CurrentDecisionLevel();
2181 if (trail_->CurrentDecisionLevel() == 0) {
2182 problem_proven_infeasible_by_cuts_ =
false;
2183 if (parameters_.add_objective_cut()) AddObjectiveCut();
2184 if (parameters_.add_mir_cuts()) AddMirCuts();
2185 if (parameters_.add_cg_cuts()) AddCGCuts();
2186 if (parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
2187 if (problem_proven_infeasible_by_cuts_) {
2188 return integer_trail_->ReportConflict({});
2190 top_n_cuts_.TransferToManager(&constraint_manager_);
2194 if (level == 0 || !parameters_.only_add_cuts_at_level_zero()) {
2196 if (level > 0 && generator.only_run_at_level_zero)
continue;
2197 if (!generator.generate_cuts(&constraint_manager_)) {
2203 implied_bounds_processor_.IbCutPool().TransferToManager(
2204 &constraint_manager_);
2208 if (constraint_manager_.ChangeLp(&state_, &num_added)) {
2210 simplex_.LoadStateForNextSolve(state_);
2211 if (!CreateLpFromConstraintManager()) {
2212 return integer_trail_->ReportConflict({});
2217 if (num_added == 0) {
2221 const double old_obj = simplex_.GetObjectiveValue();
2222 if (!SolveLp())
return true;
2223 if (!AnalyzeLp())
return false;
2225 VLOG(3) <<
"Relaxation improvement " << old_obj <<
" -> "
2226 << simplex_.GetObjectiveValue()
2227 <<
" diff: " << simplex_.GetObjectiveValue() - old_obj
2228 <<
" level: " << trail_->CurrentDecisionLevel();
2231 if (trail_->CurrentDecisionLevel() == 0) {
2232 lp_at_level_zero_is_final_ =
true;
2241absl::int128 LinearProgrammingConstraint::GetImpliedLowerBound(
2243 absl::int128 lower_bound(0);
2245 for (
int i = 0;
i < size; ++
i) {
2246 const IntegerVariable var = terms.
vars[
i];
2247 const IntegerValue coeff = terms.
coeffs[
i];
2248 const IntegerValue bound = coeff > 0 ? integer_trail_->
LowerBound(var)
2250 lower_bound += absl::int128(bound.value()) * absl::int128(coeff.value());
2255bool LinearProgrammingConstraint::ScalingCanOverflow(
2256 int power,
bool take_objective_into_account,
2257 absl::Span<
const std::pair<glop::RowIndex, double>> multipliers,
2258 int64_t overflow_cap)
const {
2260 const int64_t factor = int64_t{1} << power;
2261 const double factor_as_double =
static_cast<double>(factor);
2262 if (take_objective_into_account) {
2264 if (bound >= overflow_cap)
return true;
2266 for (
const auto [row, double_coeff] : multipliers) {
2267 const double magnitude =
2268 std::abs(std::round(double_coeff * factor_as_double));
2269 if (std::isnan(magnitude))
return true;
2270 if (magnitude >=
static_cast<double>(std::numeric_limits<int64_t>::max())) {
2274 infinity_norms_[row].value()));
2275 if (bound >= overflow_cap)
return true;
2277 return bound >= overflow_cap;
2280void LinearProgrammingConstraint::IgnoreTrivialConstraintMultipliers(
2281 std::vector<std::pair<RowIndex, double>>* lp_multipliers) {
2283 for (
const std::pair<RowIndex, double>& p : *lp_multipliers) {
2284 const RowIndex row = p.first;
2286 if (lp_multi > 0.0 && integer_lp_[row].ub_is_trivial)
continue;
2287 if (lp_multi < 0.0 && integer_lp_[row].lb_is_trivial)
continue;
2288 (*lp_multipliers)[new_size++] = p;
2290 lp_multipliers->resize(new_size);
2293void LinearProgrammingConstraint::ScaleMultipliers(
2294 absl::Span<
const std::pair<RowIndex, double>> lp_multipliers,
2295 bool take_objective_into_account, IntegerValue* scaling,
2296 std::vector<std::pair<RowIndex, IntegerValue>>* output)
const {
2300 if (lp_multipliers.empty()) {
2307 const int64_t overflow_cap = std::numeric_limits<int64_t>::max();
2308 if (ScalingCanOverflow(0, take_objective_into_account,
2309 lp_multipliers, overflow_cap)) {
2310 ++num_scaling_issues_;
2324 if (candidate >= 63)
return false;
2326 return !ScalingCanOverflow(candidate, take_objective_into_account,
2327 lp_multipliers, overflow_cap);
2329 *scaling = int64_t{1} << power;
2333 int64_t gcd = scaling->value();
2334 const double scaling_as_double =
static_cast<double>(scaling->value());
2335 for (
const auto [row, double_coeff] : lp_multipliers) {
2336 const IntegerValue coeff(std::round(double_coeff * scaling_as_double));
2338 gcd = std::gcd(gcd, std::abs(coeff.value()));
2339 output->push_back({row, coeff});
2344 for (
auto& entry : *output) {
2345 entry.second /= gcd;
2350template <
bool check_overflow>
2351bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
2352 absl::Span<
const std::pair<RowIndex, IntegerValue>> integer_multipliers,
2356 scattered_vector->
ClearAndResize(extended_integer_variables_.size());
2360 for (
const std::pair<RowIndex, IntegerValue>& term : integer_multipliers) {
2361 const RowIndex row = term.first;
2362 const IntegerValue multiplier = term.second;
2363 DCHECK_LT(row, integer_lp_.size());
2367 multiplier, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2368 infinity_norms_[row])) {
2373 const IntegerValue bound =
2374 multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
2375 if (!
AddProductTo(multiplier, bound, upper_bound))
return false;
2382void LinearProgrammingConstraint::AdjustNewLinearConstraint(
2383 std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
2385 const IntegerValue kMaxWantedCoeff(1e18);
2386 bool adjusted =
false;
2387 for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
2388 const RowIndex row = term.first;
2389 const IntegerValue multiplier = term.second;
2390 if (multiplier == 0)
continue;
2396 IntegerValue negative_limit =
2397 FloorRatio(kMaxWantedCoeff, infinity_norms_[row]);
2398 IntegerValue positive_limit = negative_limit;
2402 if (integer_lp_[row].ub != integer_lp_[row].lb) {
2403 if (multiplier > 0) {
2404 negative_limit = std::min(negative_limit, multiplier);
2406 positive_limit = std::min(positive_limit, -multiplier);
2411 const IntegerValue row_bound =
2412 multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
2413 if (row_bound != 0) {
2415 std::max(IntegerValue(0), kMaxWantedCoeff -
IntTypeAbs(*upper_bound)),
2417 const IntegerValue limit2 =
2419 if ((*upper_bound > 0) == (row_bound > 0)) {
2420 positive_limit = std::min(positive_limit, limit1);
2421 negative_limit = std::min(negative_limit, limit2);
2423 negative_limit = std::min(negative_limit, limit1);
2424 positive_limit = std::min(positive_limit, limit2);
2436 double common_diff =
ToDouble(row_bound);
2437 double positive_diff = 0.0;
2438 double negative_diff = 0.0;
2443 const LinearConstraintInternal& ct = integer_lp_[row];
2444 for (
int i = 0;
i < ct.num_terms; ++
i) {
2445 const int index = ct.start_in_buffer +
i;
2446 const ColIndex col = integer_lp_cols_[index];
2447 const IntegerValue coeff = integer_lp_coeffs_[index];
2448 DCHECK_NE(coeff, 0);
2453 const IntegerValue current = (*scattered_vector)[col];
2455 const IntegerVariable var = integer_variables_[col.value()];
2456 const IntegerValue lb = integer_trail_->LowerBound(var);
2457 const IntegerValue ub = integer_trail_->UpperBound(var);
2478 const IntegerValue abs_coeff =
IntTypeAbs(coeff);
2479 const IntegerValue current_magnitude =
IntTypeAbs(current);
2480 const IntegerValue overflow_threshold =
2481 std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude);
2482 if ((current > 0) == (coeff > 0)) {
2483 if (negative_limit * abs_coeff > current_magnitude) {
2484 negative_limit = current_magnitude / abs_coeff;
2485 if (positive_limit == 0 && negative_limit == 0)
break;
2487 if (positive_limit * abs_coeff > overflow_threshold) {
2488 positive_limit = overflow_threshold / abs_coeff;
2489 if (positive_limit == 0 && negative_limit == 0)
break;
2492 if (negative_limit * abs_coeff > overflow_threshold) {
2493 negative_limit = overflow_threshold / abs_coeff;
2494 if (positive_limit == 0 && negative_limit == 0)
break;
2496 if (positive_limit * abs_coeff > current_magnitude) {
2497 positive_limit = current_magnitude / abs_coeff;
2498 if (positive_limit == 0 && negative_limit == 0)
break;
2503 const IntegerVariable var = integer_variables_[col.value()];
2504 const IntegerValue implied = current > 0
2505 ? integer_trail_->LowerBound(var)
2506 : integer_trail_->UpperBound(var);
2512 positive_diff += common_diff;
2513 negative_diff += common_diff;
2519 IntegerValue to_add(0);
2520 if (positive_diff <= -1.0 && positive_limit > 0) {
2521 to_add = positive_limit;
2523 if (negative_diff >= 1.0 && negative_limit > 0) {
2526 std::abs(
ToDouble(negative_limit) * negative_diff) >
2527 std::abs(
ToDouble(positive_limit) * positive_diff)) {
2528 to_add = -negative_limit;
2532 term.second += to_add;
2533 *upper_bound += to_add * row_bound;
2535 CHECK(scattered_vector
2536 ->AddLinearExpressionMultiple</*check_overflow=*/false>(
2537 to_add, IntegerLpRowCols(row), IntegerLpRowCoeffs(row),
2538 infinity_norms_[row]));
2541 if (adjusted) ++num_adjusts_;
2544bool LinearProgrammingConstraint::PropagateLpConstraint(
LinearConstraint ct) {
2545 DCHECK(constraint_manager_.DebugCheckConstraint(ct));
2548 const int num_terms = ct.num_terms;
2549 if (num_terms == 0) {
2550 if (ct.ub >= 0)
return true;
2551 return integer_trail_->ReportConflict({});
2554 std::unique_ptr<IntegerSumLE128> cp_constraint(
2559 if (!cp_constraint->PropagateAtLevelZero())
return false;
2560 if (trail_->CurrentDecisionLevel() == 0)
return true;
2564 const int64_t stamp = integer_trail_->num_enqueues();
2565 const bool no_conflict = cp_constraint->Propagate();
2566 if (no_conflict && integer_trail_->num_enqueues() == stamp)
return true;
2568 const int64_t current_size =
2569 cumulative_optimal_constraint_sizes_.empty()
2571 : cumulative_optimal_constraint_sizes_.back();
2572 optimal_constraints_.push_back(std::move(cp_constraint));
2573 cumulative_optimal_constraint_sizes_.push_back(current_size + num_terms);
2574 rev_optimal_constraints_size_ = optimal_constraints_.size();
2592bool LinearProgrammingConstraint::PropagateExactLpReason() {
2594 integer_reason_.clear();
2595 deductions_.clear();
2596 deductions_reason_.clear();
2601 const RowIndex num_rows = simplex_.GetProblemNumRows();
2602 tmp_lp_multipliers_.clear();
2603 for (RowIndex row(0); row < num_rows; ++row) {
2604 const double value = -simplex_.GetDualValue(row);
2605 if (std::abs(value) < kZeroTolerance)
continue;
2606 tmp_lp_multipliers_.push_back({row, scaler_.UnscaleDualValue(row, value)});
2612 if (tmp_lp_multipliers_.empty())
return true;
2616 bool take_objective_into_account =
true;
2617 if (objective_cp_is_part_of_lp_) {
2620 CHECK_EQ(integer_objective_.size(), 1);
2621 CHECK_EQ(integer_objective_[0].first, mirror_lp_variable_[objective_cp_]);
2622 CHECK_EQ(integer_objective_[0].second, IntegerValue(1));
2624 take_objective_into_account =
false;
2627 IntegerValue scaling = 0;
2628 IgnoreTrivialConstraintMultipliers(&tmp_lp_multipliers_);
2629 ScaleMultipliers(tmp_lp_multipliers_, take_objective_into_account, &scaling,
2630 &tmp_integer_multipliers_);
2632 VLOG(1) << simplex_.GetProblemStatus();
2633 VLOG(1) <<
"Issue while computing the exact LP reason. Aborting.";
2638 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2639 tmp_integer_multipliers_, &tmp_scattered_vector_, &rc_ub));
2641 std::optional<std::pair<IntegerVariable, IntegerValue>> extra_term =
2643 if (take_objective_into_account) {
2646 const IntegerValue obj_scale = scaling;
2650 tmp_coeffs_.clear();
2651 for (
const auto [col, coeff] : integer_objective_) {
2652 tmp_cols_.push_back(col);
2653 tmp_coeffs_.push_back(coeff);
2655 CHECK(tmp_scattered_vector_
2656 .AddLinearExpressionMultiple</*check_overflow=*/false>(
2657 obj_scale, tmp_cols_, tmp_coeffs_, objective_infinity_norm_));
2658 CHECK(
AddProductTo(-obj_scale, integer_objective_offset_, &rc_ub));
2660 extra_term = {objective_cp_, -obj_scale};
2666 if (take_objective_into_account) {
2667 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2673 LinearConstraint explanation =
2674 tmp_scattered_vector_.ConvertToLinearConstraint(
2675 extended_integer_variables_, rc_ub, extra_term);
2678 if (explanation.num_terms == 0) {
2679 trail_->MutableConflict()->clear();
2680 return explanation.ub >= 0;
2683 return PropagateLpConstraint(std::move(explanation));
2686bool LinearProgrammingConstraint::PropagateExactDualRay() {
2687 IntegerValue scaling;
2689 tmp_lp_multipliers_.clear();
2690 for (RowIndex row(0); row < ray.size(); ++row) {
2691 const double value = ray[row];
2692 if (std::abs(value) < kZeroTolerance)
continue;
2696 tmp_lp_multipliers_.push_back({row, row_factors_[row.value()] * value});
2698 IgnoreTrivialConstraintMultipliers(&tmp_lp_multipliers_);
2699 ScaleMultipliers(tmp_lp_multipliers_,
false,
2700 &scaling, &tmp_integer_multipliers_);
2702 VLOG(1) <<
"Isse while computing the exact dual ray reason. Aborting.";
2706 IntegerValue new_constraint_ub;
2707 CHECK(ComputeNewLinearConstraint</*check_overflow=*/false>(
2708 tmp_integer_multipliers_, &tmp_scattered_vector_, &new_constraint_ub))
2711 AdjustNewLinearConstraint(&tmp_integer_multipliers_, &tmp_scattered_vector_,
2712 &new_constraint_ub);
2714 LinearConstraint explanation =
2715 tmp_scattered_vector_.ConvertToLinearConstraint(
2716 extended_integer_variables_, new_constraint_ub);
2718 std::string message;
2719 if (VLOG_IS_ON(1)) {
2721 message = absl::StrCat(
"LP exact dual ray not infeasible,",
2722 " implied_lb: ", GetImpliedLowerBound(explanation),
2723 " ub: ", explanation.ub.value());
2727 if (!PropagateLpConstraint(std::move(explanation)))
return false;
2733int64_t LinearProgrammingConstraint::CalculateDegeneracy() {
2734 int num_non_basic_with_zero_rc = 0;
2735 const auto reduced_costs = simplex_.GetReducedCosts().const_view();
2736 for (
const glop::ColIndex
i : simplex_.GetNotBasicBitRow()) {
2737 if (reduced_costs[
i] == 0.0) {
2738 num_non_basic_with_zero_rc++;
2741 const int64_t num_cols = simplex_.GetProblemNumCols().value();
2742 is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2743 return num_non_basic_with_zero_rc;
2746void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2747 double cp_objective_delta) {
2748 deductions_.clear();
2750 const double lp_objective_delta =
2751 cp_objective_delta / scaler_.ObjectiveScalingFactor();
2752 const int num_vars = integer_variables_.size();
2753 for (
int i = 0;
i < num_vars;
i++) {
2754 const IntegerVariable cp_var = integer_variables_[
i];
2755 const glop::ColIndex lp_var = glop::ColIndex(
i);
2756 const double rc = simplex_.GetReducedCost(lp_var);
2757 const double value = simplex_.GetVariableValue(lp_var);
2759 if (rc == 0.0)
continue;
2760 const double lp_other_bound = value + lp_objective_delta / rc;
2761 const double cp_other_bound =
2762 scaler_.UnscaleVariableValue(lp_var, lp_other_bound);
2764 if (rc > kLpEpsilon) {
2765 const double ub =
ToDouble(integer_trail_->UpperBound(cp_var));
2766 const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2771 const IntegerValue new_ub_int(
static_cast<IntegerValue
>(new_ub));
2774 }
else if (rc < -kLpEpsilon) {
2775 const double lb =
ToDouble(integer_trail_->LowerBound(cp_var));
2776 const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2778 const IntegerValue new_lb_int(
static_cast<IntegerValue
>(new_lb));
2779 deductions_.push_back(
2786void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2787 const int num_vars = integer_variables_.size();
2788 if (sum_cost_down_.size() < num_vars) {
2789 sum_cost_down_.resize(num_vars, 0.0);
2790 num_cost_down_.resize(num_vars, 0);
2791 sum_cost_up_.resize(num_vars, 0.0);
2792 num_cost_up_.resize(num_vars, 0);
2793 rc_scores_.resize(num_vars, 0.0);
2797 num_calls_since_reduced_cost_averages_reset_++;
2798 if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2799 for (
int i = 0;
i < num_vars;
i++) {
2800 sum_cost_up_[
i] /= 2;
2801 num_cost_up_[
i] /= 2;
2802 sum_cost_down_[
i] /= 2;
2803 num_cost_down_[
i] /= 2;
2805 num_calls_since_reduced_cost_averages_reset_ = 0;
2809 for (
int i = 0;
i < num_vars;
i++) {
2810 const IntegerVariable var = integer_variables_[
i];
2813 if (integer_trail_->IsFixed(var))
continue;
2816 const double rc = lp_reduced_cost_[
i];
2817 if (std::abs(rc) < kCpEpsilon)
continue;
2820 sum_cost_down_[
i] -= rc;
2821 num_cost_down_[
i]++;
2823 sum_cost_up_[
i] += rc;
2830 rc_rev_int_repository_.SetLevel(0);
2831 rc_rev_int_repository_.SetLevel(trail_->CurrentDecisionLevel());
2836 positions_by_decreasing_rc_score_.clear();
2837 for (
int i = 0;
i < num_vars;
i++) {
2842 num_cost_up_[
i] > 0 ? sum_cost_up_[
i] / num_cost_up_[
i] : 0.0;
2843 const double a_down =
2844 num_cost_down_[
i] > 0 ? sum_cost_down_[
i] / num_cost_down_[
i] : 0.0;
2845 if (num_cost_down_[
i] > 0 && num_cost_up_[
i] > 0) {
2846 rc_scores_[
i] = std::min(a_up, a_down);
2848 rc_scores_[
i] = 0.5 * (a_down + a_up);
2853 if (rc_scores_[
i] > 0.0) {
2854 positions_by_decreasing_rc_score_.push_back({-rc_scores_[
i],
i});
2857 std::sort(positions_by_decreasing_rc_score_.begin(),
2858 positions_by_decreasing_rc_score_.end());
2863 return [
this]() {
return this->LPReducedCostAverageDecision(); };
2866IntegerLiteral LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2868 int selected_index = -1;
2869 const int size = positions_by_decreasing_rc_score_.size();
2870 rc_rev_int_repository_.
SaveState(&rev_rc_start_);
2871 for (
int i = rev_rc_start_;
i < size; ++
i) {
2872 const int index = positions_by_decreasing_rc_score_[
i].second;
2873 const IntegerVariable var = integer_variables_[index];
2874 if (integer_trail_->
IsFixed(var))
continue;
2875 selected_index = index;
2880 if (selected_index == -1)
return IntegerLiteral();
2881 const IntegerVariable var = integer_variables_[selected_index];
2887 const IntegerValue ub = integer_trail_->
UpperBound(var);
2888 const IntegerValue value_ceil(
2890 if (value_ceil >= ub) {
2896 const IntegerValue lb = integer_trail_->
LowerBound(var);
2897 const IntegerValue value_floor(
2899 if (value_floor <= lb) {
2906 num_cost_up_[selected_index] > 0
2907 ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2909 const double a_down =
2910 num_cost_down_[selected_index] > 0
2911 ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2913 if (a_down < a_up) {
2920absl::Span<const glop::ColIndex> LinearProgrammingConstraint::IntegerLpRowCols(
2921 glop::RowIndex row)
const {
2922 const int start = integer_lp_[row].start_in_buffer;
2923 const size_t num_terms =
static_cast<size_t>(integer_lp_[row].num_terms);
2924 return {integer_lp_cols_.data() + start, num_terms};
2927absl::Span<const IntegerValue> LinearProgrammingConstraint::IntegerLpRowCoeffs(
2928 glop::RowIndex row)
const {
2929 const int start = integer_lp_[row].start_in_buffer;
2930 const size_t num_terms =
static_cast<size_t>(integer_lp_[row].num_terms);
2931 return {integer_lp_coeffs_.data() + start, num_terms};
2935 return absl::StrFormat(
"%d rows, %d columns, %d entries", integer_lp_.size(),
2936 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)
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)
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)
int64_t CapProd(int64_t x, int64_t y)
Point BinarySearch(Point x_true, Point x_false, std::function< bool(Point)> f)
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.