27#include "absl/container/btree_map.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/log/check.h"
30#include "absl/log/log.h"
31#include "absl/log/vlog_is_on.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/string_view.h"
34#include "absl/types/span.h"
55 if (!VLOG_IS_ON(1))
return;
56 std::vector<std::pair<std::string, int64_t>> stats;
57 stats.push_back({
"symmetrizer/overflows", num_overflows_});
58 shared_stats_->AddStats(stats);
62 IntegerVariable sum_var, absl::Span<const IntegerVariable> orbit) {
63 CHECK_GT(orbit.size(), 1);
66 const int orbit_index = orbits_.size();
68 orbit_sum_vars_.push_back(sum_var);
72 const int new_size = integer_trail_->NumIntegerVariables().value() / 2;
73 if (var_to_orbit_index_.size() < new_size) {
74 var_to_orbit_index_.resize(new_size, -1);
79 for (
const IntegerVariable var : orbit) {
108 if (!has_symmetry_)
return true;
112 int64_t scaling_factor = 1;
114 const IntegerVariable var = ct->
vars[
i];
118 if (orbit_index == -1 || orbit_sum_vars_[orbit_index] == var) {
125 const int orbit_size = orbits_[orbit_index].size();
128 VLOG(2) <<
"SYMMETRY skip constraint due to overflow";
131 scaling_factor = std::lcm(scaling_factor, orbit_size);
134 if (scaling_factor == 1) {
139 if (folded !=
nullptr) *folded =
true;
147 const IntegerVariable var = ct->
vars[
i];
148 const IntegerValue coeff = ct->
coeffs[
i];
151 if (orbit_index == -1 || orbit_sum_vars_[orbit_index] == var) {
152 const int64_t scaled_coeff =
CapProd(coeff.value(), scaling_factor);
155 VLOG(2) <<
"SYMMETRY skip constraint due to overflow";
158 builder_.AddTerm(var, scaled_coeff);
160 const int64_t orbit_size = orbits_[orbit_index].size();
161 const int64_t factor = scaling_factor / orbit_size;
162 const int64_t scaled_coeff =
CapProd(coeff.value(), factor);
165 VLOG(2) <<
"SYMMETRY skip constraint due to overflow";
168 builder_.AddTerm(orbit_sum_vars_[orbit_index], scaled_coeff);
175 VLOG(2) <<
"SYMMETRY skip constraint due to lb/ub overflow";
178 if (!builder_.BuildIntoConstraintAndCheckOverflow(
179 ct->
lb * scaling_factor, ct->
ub * scaling_factor, ct)) {
181 VLOG(2) <<
"SYMMETRY skip constraint due to overflow";
189 VLOG(2) <<
"SYMMETRY skip constraint due to overflow factor = "
201 if (!has_symmetry_)
return -1;
206 if (!has_symmetry_)
return false;
208 return orbit_index >= 0 && orbit_sum_vars_[orbit_index] == var;
212 IntegerVariable var)
const {
213 if (!has_symmetry_)
return true;
215 return orbit_index == -1 || orbit_sum_vars_[orbit_index] == var;
220const LinearConstraintManager::ConstraintIndex kInvalidConstraintIndex(-1);
226 for (
int i = 0;
i < num_terms; ++
i) {
236 if (!VLOG_IS_ON(1))
return;
239 std::vector<std::pair<std::string, int64_t>> cut_stats;
240 for (
const auto& entry : type_to_num_cuts_) {
241 cut_stats.push_back({absl::StrCat(
"cut/", entry.first), entry.second});
246void LinearConstraintManager::RescaleActiveCounts(
const double scaling_factor) {
247 for (ConstraintIndex
i(0);
i < constraint_infos_.size(); ++
i) {
248 constraint_infos_[
i].active_count *= scaling_factor;
250 constraint_active_count_increase_ *= scaling_factor;
251 VLOG(3) <<
"Rescaled active counts by " << scaling_factor;
254bool LinearConstraintManager::MaybeRemoveSomeInactiveConstraints(
255 glop::BasisState* solution_state) {
256 if (solution_state->IsEmpty())
return false;
257 const glop::RowIndex num_rows(lp_constraints_.size());
258 const glop::ColIndex num_cols =
259 solution_state->statuses.size() - RowToColIndex(num_rows);
261 for (
int i = 0;
i < num_rows; ++
i) {
262 const ConstraintIndex constraint_index = lp_constraints_[
i];
263 if (constraint_infos_[constraint_index].constraint.num_terms == 0) {
269 constraint_infos_[constraint_index].is_in_lp =
false;
280 solution_state->statuses[num_cols + glop::ColIndex(
i)];
282 constraint_infos_[constraint_index].inactive_count++;
283 if (constraint_infos_[constraint_index].inactive_count >
284 sat_parameters_.max_consecutive_inactive_count()) {
285 constraint_infos_[constraint_index].is_in_lp =
false;
290 constraint_infos_[constraint_index].inactive_count = 0;
293 lp_constraints_[new_size] = constraint_index;
294 solution_state->statuses[num_cols + glop::ColIndex(new_size)] = row_status;
297 const int num_removed_constraints = lp_constraints_.size() - new_size;
298 lp_constraints_.resize(new_size);
299 solution_state->statuses.resize(num_cols + glop::ColIndex(new_size));
300 if (num_removed_constraints > 0) {
301 VLOG(3) <<
"Removed " << num_removed_constraints <<
" constraints";
303 return num_removed_constraints > 0;
314 SimplifyConstraint(&ct);
321 if (symmetrizer_->HasSymmetry() &&
322 !symmetrizer_->FoldLinearConstraint(&ct, folded)) {
323 return kInvalidConstraintIndex;
328 const size_t key = ComputeHashOfTerms(ct);
329 if (equiv_constraints_.contains(key)) {
330 const ConstraintIndex ct_index = equiv_constraints_[key];
331 if (constraint_infos_[ct_index].constraint.IsEqualIgnoringBounds(ct)) {
332 bool tightened =
false;
333 if (ct.
lb > constraint_infos_[ct_index].constraint.lb) {
335 if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ =
true;
336 constraint_infos_[ct_index].constraint.lb = ct.
lb;
338 if (ct.
ub < constraint_infos_[ct_index].constraint.ub) {
340 if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ =
true;
341 constraint_infos_[ct_index].constraint.ub = ct.
ub;
343 if (added !=
nullptr) *added = tightened;
345 ++num_merged_constraints_;
346 FillDerivedFields(&constraint_infos_[ct_index]);
352 if (added !=
nullptr) *added =
true;
353 const ConstraintIndex ct_index(constraint_infos_.size());
357 FillDerivedFields(&ct_info);
359 equiv_constraints_[key] = ct_index;
360 ct_info.
active_count = constraint_active_count_increase_;
361 constraint_infos_.push_back(std::move(ct_info));
366 IntegerValue new_lb) {
367 const ConstraintIndex index = lp_constraints_[index_in_lp.value()];
370 ++num_constraint_updates_;
371 current_lp_is_changed_ =
true;
377 IntegerValue new_ub) {
378 const ConstraintIndex index = lp_constraints_[index_in_lp.value()];
381 ++num_constraint_updates_;
382 current_lp_is_changed_ =
true;
387void LinearConstraintManager::ComputeObjectiveParallelism(
388 const ConstraintIndex ct_index) {
389 CHECK(objective_is_defined_);
391 if (!objective_norm_computed_) {
392 objective_l2_norm_ = std::sqrt(sum_of_squared_objective_coeffs_);
393 objective_norm_computed_ =
true;
395 CHECK_GT(objective_l2_norm_, 0.0);
397 constraint_infos_[ct_index].objective_parallelism_computed =
true;
398 if (constraint_infos_[ct_index].l2_norm == 0.0) {
399 constraint_infos_[ct_index].objective_parallelism = 0.0;
403 const LinearConstraint& lc = constraint_infos_[ct_index].constraint;
404 double unscaled_objective_parallelism = 0.0;
405 for (
int i = 0;
i < lc.num_terms; ++
i) {
406 const IntegerVariable var = lc.vars[
i];
407 const auto it = objective_map_.find(var);
408 if (it == objective_map_.end())
continue;
409 unscaled_objective_parallelism += it->second *
ToDouble(lc.coeffs[
i]);
411 const double objective_parallelism =
412 unscaled_objective_parallelism /
413 (constraint_infos_[ct_index].l2_norm * objective_l2_norm_);
414 constraint_infos_[ct_index].objective_parallelism =
415 std::abs(objective_parallelism);
421 std::string extra_info) {
422 ++num_add_cut_calls_;
426 const double violation =
431 if (violation / l2_norm < 1e-4) {
432 VLOG(3) <<
"BAD Cut '" << type_name <<
"'"
435 <<
" norm=" << l2_norm <<
" violation=" << violation
436 <<
" eff=" << violation / l2_norm <<
" " << extra_info;
447 const ConstraintIndex ct_index =
Add(std::move(ct), &added);
451 if (!added)
return false;
455 constraint_infos_[ct_index].is_deletable =
true;
457 VLOG(2) <<
"Cut '" << type_name <<
"'"
458 <<
" size=" << constraint_infos_[ct_index].constraint.num_terms
461 <<
" norm=" << l2_norm <<
" violation=" << violation
462 <<
" eff=" << violation / l2_norm <<
" " << extra_info;
465 num_deletable_constraints_++;
466 type_to_num_cuts_[type_name]++;
470void LinearConstraintManager::PermanentlyRemoveSomeConstraints() {
471 std::vector<double> deletable_constraint_counts;
472 for (ConstraintIndex
i(0);
i < constraint_infos_.size(); ++
i) {
473 if (constraint_infos_[
i].is_deletable && !constraint_infos_[
i].is_in_lp) {
474 deletable_constraint_counts.push_back(constraint_infos_[
i].active_count);
477 if (deletable_constraint_counts.empty())
return;
478 std::sort(deletable_constraint_counts.begin(),
479 deletable_constraint_counts.end());
483 double active_count_threshold = std::numeric_limits<double>::infinity();
485 deletable_constraint_counts.size()) {
486 active_count_threshold =
490 ConstraintIndex new_size(0);
491 equiv_constraints_.clear();
492 util_intops::StrongVector<ConstraintIndex, ConstraintIndex> index_mapping(
493 constraint_infos_.size());
494 int num_deleted_constraints = 0;
495 for (ConstraintIndex
i(0);
i < constraint_infos_.size(); ++
i) {
496 if (constraint_infos_[
i].is_deletable && !constraint_infos_[
i].is_in_lp &&
497 constraint_infos_[
i].active_count <= active_count_threshold &&
498 num_deleted_constraints < sat_parameters_.cut_cleanup_target()) {
499 ++num_deleted_constraints;
504 constraint_infos_[new_size] = std::move(constraint_infos_[
i]);
506 index_mapping[
i] = new_size;
509 equiv_constraints_[constraint_infos_[new_size].hash] = new_size;
512 constraint_infos_.resize(new_size.value());
515 for (
int i = 0;
i < lp_constraints_.size(); ++
i) {
516 lp_constraints_[
i] = index_mapping[lp_constraints_[
i]];
519 if (num_deleted_constraints > 0) {
520 VLOG(3) <<
"Constraint manager cleanup: #deleted:"
521 << num_deleted_constraints;
523 num_deletable_constraints_ -= num_deleted_constraints;
527 IntegerValue coeff) {
528 if (coeff == IntegerValue(0))
return;
529 objective_is_defined_ =
true;
534 const double coeff_as_double =
ToDouble(coeff);
535 const auto insert = objective_map_.insert({var, coeff_as_double});
537 <<
"SetObjectiveCoefficient() called twice with same variable";
538 sum_of_squared_objective_coeffs_ += coeff_as_double * coeff_as_double;
543 bool term_changed =
false;
545 IntegerValue min_sum(0);
546 IntegerValue max_sum(0);
547 IntegerValue max_magnitude(0);
551 for (
int i = 0;
i < num_terms; ++
i) {
552 const IntegerVariable var = ct->
vars[
i];
553 const IntegerValue coeff = ct->
coeffs[
i];
559 if (lb == ub)
continue;
562 const IntegerValue magnitude =
IntTypeAbs(coeff);
563 max_magnitude = std::max(max_magnitude, magnitude);
564 min_magnitude = std::min(min_magnitude, magnitude);
566 min_sum += coeff * lb;
567 max_sum += coeff * ub;
569 min_sum += coeff * ub;
570 max_sum += coeff * lb;
575 IntegerValue fixed_part = 0;
576 if (new_size < num_terms) {
578 ++num_shortened_constraints_;
580 for (
int i = 0;
i < num_terms; ++
i) {
581 const IntegerVariable var = ct->
vars[
i];
582 const IntegerValue coeff = ct->
coeffs[
i];
583 const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
584 const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
586 fixed_part += coeff * lb;
589 ct->
vars[new_size] = var;
590 ct->
coeffs[new_size] = coeff;
598 if (min_sum + fixed_part >= ct->
lb && max_sum + fixed_part <= ct->ub) {
606 ct->
lb = std::max(ct->
lb, min_sum + fixed_part);
607 ct->
ub = std::min(ct->
ub, max_sum + fixed_part);
608 ct->
lb -= fixed_part;
609 ct->
ub -= fixed_part;
617 const IntegerValue threshold_ub = max_sum - ct->
ub;
618 const IntegerValue threshold_lb = ct->
lb - min_sum;
619 const IntegerValue threshold = std::max(threshold_lb, threshold_ub);
620 CHECK_GT(threshold, 0);
624 if (threshold_ub > 0 && threshold_lb > 0 && threshold_lb != threshold_ub) {
625 if (max_magnitude > std::min(threshold_lb, threshold_ub)) {
626 ++num_split_constraints_;
632 const IntegerValue second_threshold = std::max(
633 {
CeilRatio(threshold, IntegerValue(2)), threshold - min_magnitude,
634 std::min(threshold_lb, threshold_ub)});
635 if (max_magnitude > second_threshold) {
637 for (
int i = 0;
i < num_terms; ++
i) {
642 const IntegerValue coeff = ct->
coeffs[
i];
643 const IntegerVariable var = ct->
vars[
i];
644 const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
645 const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
646 if (coeff > threshold) {
648 ++num_coeff_strenghtening_;
650 ct->
ub -= (coeff - threshold) * ub;
651 ct->
lb -= (coeff - threshold) * lb;
652 }
else if (coeff > second_threshold && coeff < threshold) {
654 ++num_coeff_strenghtening_;
655 ct->
coeffs[
i] = second_threshold;
656 ct->
ub -= (coeff - second_threshold) * ub;
657 ct->
lb -= (coeff - second_threshold) * lb;
658 }
else if (coeff < -threshold) {
660 ++num_coeff_strenghtening_;
662 ct->
ub -= (coeff + threshold) * lb;
663 ct->
lb -= (coeff + threshold) * ub;
664 }
else if (coeff < -second_threshold && coeff > -threshold) {
666 ++num_coeff_strenghtening_;
667 ct->
coeffs[
i] = -second_threshold;
668 ct->
ub -= (coeff + second_threshold) * lb;
669 ct->
lb -= (coeff + second_threshold) * ub;
677void LinearConstraintManager::FillDerivedFields(ConstraintInfo* info) {
678 IntegerValue min_sum(0);
679 IntegerValue max_sum(0);
680 const int num_terms = info->constraint.num_terms;
681 for (
int i = 0;
i < num_terms; ++
i) {
682 const IntegerVariable var = info->constraint.vars[
i];
683 const IntegerValue coeff = info->constraint.coeffs[
i];
684 const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
685 const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
687 min_sum += coeff * lb;
688 max_sum += coeff * ub;
690 min_sum += coeff * ub;
691 max_sum += coeff * lb;
694 info->constraint.lb = std::max(min_sum, info->constraint.lb);
695 info->constraint.ub = std::min(max_sum, info->constraint.ub);
696 CHECK_NE(
CapSub(info->constraint.ub.value(), info->constraint.lb.value()),
697 std::numeric_limits<int64_t>::max());
698 info->lb_is_trivial = min_sum >= info->constraint.lb;
699 info->ub_is_trivial = max_sum <= info->constraint.ub;
703 int* num_new_constraints) {
704 VLOG(3) <<
"Enter ChangeLP, scan " << constraint_infos_.size()
706 const double saved_dtime = dtime_;
707 std::vector<std::pair<ConstraintIndex, double>> new_constraints_by_score;
708 std::vector<double> new_constraints_efficacies;
709 std::vector<double> new_constraints_orthogonalities;
711 const bool simplify_constraints =
712 integer_trail_.num_level_zero_enqueues() > last_simplification_timestamp_;
713 last_simplification_timestamp_ = integer_trail_.num_level_zero_enqueues();
718 bool rescale_active_count =
false;
719 const double tolerance = 1e-6;
720 for (ConstraintIndex
i(0);
i < constraint_infos_.size(); ++
i) {
722 if (simplify_constraints &&
723 SimplifyConstraint(&constraint_infos_[
i].constraint)) {
724 ++num_simplifications_;
732 constraint_infos_[
i].objective_parallelism_computed =
false;
733 constraint_infos_[
i].l2_norm =
735 FillDerivedFields(&constraint_infos_[
i]);
737 if (constraint_infos_[
i].is_in_lp) current_lp_is_changed_ =
true;
738 equiv_constraints_.erase(constraint_infos_[
i].hash);
739 constraint_infos_[
i].hash =
740 ComputeHashOfTerms(constraint_infos_[
i].constraint);
744 equiv_constraints_[constraint_infos_[
i].hash] =
i;
747 if (constraint_infos_[
i].is_in_lp)
continue;
752 1.7e-9 *
static_cast<double>(constraint_infos_[
i].constraint.num_terms);
753 const double activity =
755 const double lb_violation =
756 ToDouble(constraint_infos_[
i].constraint.lb) - activity;
757 const double ub_violation =
758 activity -
ToDouble(constraint_infos_[
i].constraint.ub);
759 const double violation = std::max(lb_violation, ub_violation);
760 if (violation >= tolerance) {
761 constraint_infos_[
i].inactive_count = 0;
762 if (objective_is_defined_ &&
763 !constraint_infos_[
i].objective_parallelism_computed) {
764 ComputeObjectiveParallelism(
i);
765 }
else if (!objective_is_defined_) {
766 constraint_infos_[
i].objective_parallelism = 0.0;
769 const double score = violation / constraint_infos_[
i].l2_norm +
770 constraint_infos_[
i].objective_parallelism;
771 new_constraints_by_score.push_back({
i, score});
773 if (constraint_infos_[
i].is_deletable) {
774 constraint_infos_[
i].active_count += constraint_active_count_increase_;
775 if (constraint_infos_[
i].active_count >
776 sat_parameters_.cut_max_active_count_value()) {
777 rescale_active_count =
true;
784 if (solution_state !=
nullptr) {
785 const glop::RowIndex num_rows(lp_constraints_.size());
786 const glop::ColIndex num_cols =
787 solution_state->
statuses.
size() - RowToColIndex(num_rows);
789 for (
int i = 0;
i < num_rows; ++
i) {
790 const ConstraintIndex constraint_index = lp_constraints_[
i];
792 solution_state->
statuses[num_cols + glop::ColIndex(
i)];
794 constraint_infos_[constraint_index].is_deletable) {
795 constraint_infos_[constraint_index].active_count +=
796 constraint_active_count_increase_;
797 if (constraint_infos_[constraint_index].active_count >
798 sat_parameters_.cut_max_active_count_value()) {
799 rescale_active_count =
true;
805 if (rescale_active_count) {
806 CHECK_GT(sat_parameters_.cut_max_active_count_value(), 0.0);
807 RescaleActiveCounts(1.0 / sat_parameters_.cut_max_active_count_value());
811 constraint_active_count_increase_ *=
812 1.0 / sat_parameters_.cut_active_count_decay();
817 if (MaybeRemoveSomeInactiveConstraints(solution_state)) {
818 current_lp_is_changed_ =
true;
831 const int kBlowupFactor = 4;
832 const int current_size =
static_cast<int>(new_constraints_by_score.size());
833 int constraint_limit =
834 std::min(sat_parameters_.new_constraints_batch_size(), current_size);
835 if (lp_constraints_.empty()) {
836 constraint_limit = std::min(1000, current_size);
838 VLOG(3) <<
" - size = " << current_size <<
", limit = " << constraint_limit;
840 std::stable_sort(new_constraints_by_score.begin(),
841 new_constraints_by_score.end(),
842 [&](std::pair<ConstraintIndex, double> a,
843 std::pair<ConstraintIndex, double>
b) {
844 return a.second > b.second;
846 if (new_constraints_by_score.size() > kBlowupFactor * constraint_limit) {
847 VLOG(3) <<
"Resize candidate constraints from "
848 << new_constraints_by_score.size() <<
" down to "
849 << kBlowupFactor * constraint_limit;
850 new_constraints_by_score.resize(kBlowupFactor * constraint_limit);
855 ConstraintIndex last_added_candidate = kInvalidConstraintIndex;
856 std::vector<double> orthogonality_score(new_constraints_by_score.size(), 1.0);
857 for (
int i = 0;
i < constraint_limit; ++
i) {
862 double best_score = 0.0;
863 ConstraintIndex best_candidate = kInvalidConstraintIndex;
864 for (
int j = 0; j < new_constraints_by_score.size(); ++j) {
866 if (time_limit_check.
LimitReached())
return current_lp_is_changed_;
868 const ConstraintIndex new_index = new_constraints_by_score[j].first;
869 if (constraint_infos_[new_index].is_in_lp)
continue;
871 if (last_added_candidate != kInvalidConstraintIndex) {
872 const double current_orthogonality =
874 constraint_infos_[last_added_candidate].constraint,
875 constraint_infos_[new_index].constraint)) /
876 (constraint_infos_[last_added_candidate].l2_norm *
877 constraint_infos_[new_index].l2_norm));
878 orthogonality_score[j] =
879 std::min(orthogonality_score[j], current_orthogonality);
887 if (orthogonality_score[j] <
888 sat_parameters_.min_orthogonality_for_lp_constraints()) {
895 orthogonality_score[j] + new_constraints_by_score[j].second;
896 CHECK_GE(score, 0.0);
897 if (score > best_score || best_candidate == kInvalidConstraintIndex) {
899 best_candidate = new_index;
903 if (best_candidate != kInvalidConstraintIndex) {
905 constraint_infos_[best_candidate].is_in_lp =
true;
910 current_lp_is_changed_ =
true;
911 lp_constraints_.push_back(best_candidate);
912 last_added_candidate = best_candidate;
919 if (num_new_constraints !=
nullptr) {
920 *num_new_constraints = num_added;
924 VLOG(3) <<
"Added " << num_added <<
" constraints.";
931 if (num_deletable_constraints_ > sat_parameters_.max_num_cuts()) {
932 PermanentlyRemoveSomeConstraints();
935 time_limit_->AdvanceDeterministicTime(dtime_ - saved_dtime);
939 if (current_lp_is_changed_) {
940 current_lp_is_changed_ =
false;
947 for (ConstraintIndex
i(0);
i < constraint_infos_.size(); ++
i) {
948 if (constraint_infos_[
i].is_in_lp)
continue;
949 if (constraint_infos_[
i].constraint.num_terms == 0)
continue;
951 constraint_infos_[
i].is_in_lp =
true;
952 lp_constraints_.push_back(
i);
959 const auto& debug_solution = *(model_->Get<
DebugSolution>());
961 IntegerValue activity(0);
963 const IntegerVariable var = cut.
vars[
i];
964 const IntegerValue coeff = cut.
coeffs[
i];
965 CHECK(debug_solution.ivar_has_value[var]);
966 activity += coeff * debug_solution.ivar_values[var];
968 if (activity > cut.
ub || activity < cut.
lb) {
970 LOG(INFO) <<
"activity " << activity <<
" not in [" << cut.
lb <<
","
982 cuts_.Add({std::string(name), std::move(ct)}, normalized_violation);
986 for (CutCandidate& candidate : *cuts_.MutableUnorderedElements()) {
987 manager->
AddCut(std::move(candidate.cut), candidate.name);
void resize(IntType size)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
~LinearConstraintManager()
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr, bool *folded=nullptr)
bool ChangeLp(glop::BasisState *solution_state, int *num_new_constraints=nullptr)
bool AddCut(LinearConstraint ct, std::string type_name, std::string extra_info="")
bool UpdateConstraintUb(glop::RowIndex index_in_lp, IntegerValue new_ub)
bool DebugCheckConstraint(const LinearConstraint &cut)
bool UpdateConstraintLb(glop::RowIndex index_in_lp, IntegerValue new_lb)
These must be level zero bounds.
void AddAllConstraintsToLp()
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
bool FoldLinearConstraint(LinearConstraint *ct, bool *folded=nullptr)
int OrbitIndex(IntegerVariable var) const
void AddSymmetryOrbit(IntegerVariable sum_var, absl::Span< const IntegerVariable > orbit)
~LinearConstraintSymmetrizer()
bool IsOrbitSumVar(IntegerVariable var) const
Returns true iff var is one of the sum_var passed to AddSymmetryOrbit().
bool AppearInFoldedProblem(IntegerVariable var) const
::int32_t cut_cleanup_target() const
Simple class to add statistics by name and print them at the end.
void TransferToManager(LinearConstraintManager *manager)
Empty the local pool and add all its content to the manager.
void AddCut(LinearConstraint ct, absl::string_view name, const util_intops::StrongVector< IntegerVariable, double > &lp_solution)
Adds a cut to the local pool.
double ComputeActivity(const LinearConstraint &constraint, const util_intops::StrongVector< IntegerVariable, double > &values)
void DivideByGCD(LinearConstraint *constraint)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
double ComputeL2Norm(const LinearConstraint &ct)
Returns sqrt(sum square(coeff)).
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
IntegerValue ComputeInfinityNorm(const LinearConstraint &ct)
Returns the maximum absolute value of the coefficients.
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Returns the vector of the negated variables.
bool PossibleOverflow(const IntegerTrail &integer_trail, const LinearConstraint &constraint)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
void MakeAllVariablesPositive(LinearConstraint *constraint)
Makes all variables "positive" by transforming a variable to its negation.
bool NoDuplicateVariable(const LinearConstraint &ct)
Returns false if duplicate variables are found in ct.
bool VariableIsPositive(IntegerVariable i)
double ToDouble(IntegerValue value)
double ScalarProduct(const LinearConstraint &ct1, const LinearConstraint &ct2)
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 CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
uint64_t Hash(uint64_t num, uint64_t c)
VariableStatusRow statuses
LinearConstraint constraint
std::unique_ptr< IntegerValue[]> coeffs
std::unique_ptr< IntegerVariable[]> vars
std::string DebugString() const
absl::Span< const IntegerVariable > VarsAsSpan() const
double NormalizedViolation(const util_intops::StrongVector< IntegerVariable, double > &lp_values) const