21 #include "absl/container/flat_hash_set.h"
31 const LinearConstraintManager::ConstraintIndex kInvalidConstraintIndex(-1);
33 size_t ComputeHashOfTerms(
const LinearConstraint&
ct) {
34 DCHECK(std::is_sorted(
ct.vars.begin(),
ct.vars.end()));
36 const int num_terms =
ct.vars.size();
37 for (
int i = 0; i < num_terms; ++i) {
47 if (num_merged_constraints_ > 0) {
48 VLOG(2) <<
"num_merged_constraints: " << num_merged_constraints_;
50 if (num_shortened_constraints_ > 0) {
51 VLOG(2) <<
"num_shortened_constraints: " << num_shortened_constraints_;
53 if (num_splitted_constraints_ > 0) {
54 VLOG(2) <<
"num_splitted_constraints: " << num_splitted_constraints_;
56 if (num_coeff_strenghtening_ > 0) {
57 VLOG(2) <<
"num_coeff_strenghtening: " << num_coeff_strenghtening_;
59 if (sat_parameters_.log_search_progress() && num_cuts_ > 0) {
60 LOG(
INFO) <<
"Total cuts added: " << num_cuts_ <<
" (out of "
61 << num_add_cut_calls_ <<
" calls) worker: '" << model_->
Name()
63 LOG(
INFO) <<
"Num simplifications: " << num_simplifications_;
64 for (
const auto& entry : type_to_num_cuts_) {
65 LOG(
INFO) <<
"Added " << entry.second <<
" cuts of type '" << entry.first
71 void LinearConstraintManager::RescaleActiveCounts(
const double scaling_factor) {
72 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
73 constraint_infos_[i].active_count *= scaling_factor;
75 constraint_active_count_increase_ *= scaling_factor;
76 VLOG(2) <<
"Rescaled active counts by " << scaling_factor;
79 bool LinearConstraintManager::MaybeRemoveSomeInactiveConstraints(
80 glop::BasisState* solution_state) {
81 if (solution_state->IsEmpty())
return false;
82 const glop::RowIndex num_rows(lp_constraints_.size());
83 const glop::ColIndex num_cols =
86 for (
int i = 0; i < num_rows; ++i) {
87 const ConstraintIndex constraint_index = lp_constraints_[i];
96 solution_state->statuses[num_cols + glop::ColIndex(i)];
98 constraint_infos_[constraint_index].inactive_count++;
99 if (constraint_infos_[constraint_index].inactive_count >
100 sat_parameters_.max_consecutive_inactive_count()) {
101 constraint_infos_[constraint_index].is_in_lp =
false;
106 constraint_infos_[constraint_index].inactive_count = 0;
109 lp_constraints_[new_size] = constraint_index;
110 solution_state->statuses[num_cols + glop::ColIndex(new_size)] = row_status;
113 const int num_removed_constraints = lp_constraints_.size() - new_size;
114 lp_constraints_.resize(new_size);
115 solution_state->statuses.resize(num_cols + glop::ColIndex(new_size));
116 if (num_removed_constraints > 0) {
117 VLOG(2) <<
"Removed " << num_removed_constraints <<
" constraints";
119 return num_removed_constraints > 0;
129 SimplifyConstraint(&
ct);
135 const size_t key = ComputeHashOfTerms(
ct);
137 const ConstraintIndex ct_index = equiv_constraints_[key];
138 if (constraint_infos_[ct_index].constraint.vars ==
ct.vars &&
139 constraint_infos_[ct_index].constraint.coeffs ==
ct.coeffs) {
140 if (added !=
nullptr) *added =
false;
141 if (
ct.lb > constraint_infos_[ct_index].constraint.lb) {
142 if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ =
true;
143 constraint_infos_[ct_index].constraint.lb =
ct.lb;
144 if (added !=
nullptr) *added =
true;
146 if (
ct.ub < constraint_infos_[ct_index].constraint.ub) {
147 if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ =
true;
148 constraint_infos_[ct_index].constraint.ub =
ct.ub;
149 if (added !=
nullptr) *added =
true;
151 ++num_merged_constraints_;
156 if (added !=
nullptr) *added =
true;
157 const ConstraintIndex ct_index(constraint_infos_.size());
162 equiv_constraints_[key] = ct_index;
163 ct_info.
active_count = constraint_active_count_increase_;
164 constraint_infos_.push_back(std::move(ct_info));
168 void LinearConstraintManager::ComputeObjectiveParallelism(
169 const ConstraintIndex ct_index) {
170 CHECK(objective_is_defined_);
172 if (!objective_norm_computed_) {
174 for (
const double coeff : dense_objective_coeffs_) {
175 sum += coeff * coeff;
177 objective_l2_norm_ = std::sqrt(sum);
178 objective_norm_computed_ =
true;
182 constraint_infos_[ct_index].objective_parallelism_computed =
true;
183 if (constraint_infos_[ct_index].l2_norm == 0.0) {
184 constraint_infos_[ct_index].objective_parallelism = 0.0;
188 const LinearConstraint& lc = constraint_infos_[ct_index].constraint;
189 double unscaled_objective_parallelism = 0.0;
190 for (
int i = 0; i < lc.vars.size(); ++i) {
191 const IntegerVariable
var = lc.vars[i];
193 if (
var < dense_objective_coeffs_.
size()) {
194 unscaled_objective_parallelism +=
195 ToDouble(lc.coeffs[i]) * dense_objective_coeffs_[
var];
198 const double objective_parallelism =
199 unscaled_objective_parallelism /
200 (constraint_infos_[ct_index].l2_norm * objective_l2_norm_);
201 constraint_infos_[ct_index].objective_parallelism =
202 std::abs(objective_parallelism);
210 std::string extra_info) {
211 ++num_add_cut_calls_;
212 if (
ct.vars.empty())
return false;
215 const double violation =
220 if (violation / l2_norm < 1e-5)
return false;
223 const ConstraintIndex ct_index =
Add(std::move(
ct), &added);
227 if (!added)
return false;
231 constraint_infos_[ct_index].is_deletable =
true;
233 VLOG(1) <<
"Cut '" << type_name <<
"'"
234 <<
" size=" << constraint_infos_[ct_index].constraint.vars.size()
237 <<
" norm=" << l2_norm <<
" violation=" << violation
238 <<
" eff=" << violation / l2_norm <<
" " << extra_info;
241 num_deletable_constraints_++;
242 type_to_num_cuts_[type_name]++;
246 void LinearConstraintManager::PermanentlyRemoveSomeConstraints() {
247 std::vector<double> deletable_constraint_counts;
248 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
249 if (constraint_infos_[i].is_deletable && !constraint_infos_[i].is_in_lp) {
250 deletable_constraint_counts.push_back(constraint_infos_[i].active_count);
253 if (deletable_constraint_counts.empty())
return;
254 std::sort(deletable_constraint_counts.begin(),
255 deletable_constraint_counts.end());
259 double active_count_threshold = std::numeric_limits<double>::infinity();
260 if (sat_parameters_.cut_cleanup_target() <
261 deletable_constraint_counts.size()) {
262 active_count_threshold =
263 deletable_constraint_counts[sat_parameters_.cut_cleanup_target()];
266 ConstraintIndex new_size(0);
267 equiv_constraints_.clear();
269 constraint_infos_.size());
270 int num_deleted_constraints = 0;
271 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
272 if (constraint_infos_[i].is_deletable && !constraint_infos_[i].is_in_lp &&
273 constraint_infos_[i].active_count <= active_count_threshold &&
274 num_deleted_constraints < sat_parameters_.cut_cleanup_target()) {
275 ++num_deleted_constraints;
280 constraint_infos_[new_size] = std::move(constraint_infos_[i]);
282 index_mapping[i] = new_size;
285 equiv_constraints_[constraint_infos_[new_size].hash] = new_size;
288 constraint_infos_.resize(new_size.value());
291 for (
int i = 0; i < lp_constraints_.size(); ++i) {
292 lp_constraints_[i] = index_mapping[lp_constraints_[i]];
295 if (num_deleted_constraints > 0) {
296 VLOG(1) <<
"Constraint manager cleanup: #deleted:"
297 << num_deleted_constraints;
299 num_deletable_constraints_ -= num_deleted_constraints;
303 IntegerValue coeff) {
304 if (coeff == IntegerValue(0))
return;
305 objective_is_defined_ =
true;
310 if (
var.value() >= dense_objective_coeffs_.
size()) {
311 dense_objective_coeffs_.
resize(
var.value() + 1, 0.0);
317 bool term_changed =
false;
319 IntegerValue min_sum(0);
320 IntegerValue max_sum(0);
321 IntegerValue max_magnitude(0);
323 const int num_terms =
ct->vars.size();
324 for (
int i = 0; i < num_terms; ++i) {
325 const IntegerVariable
var =
ct->vars[i];
326 const IntegerValue coeff =
ct->coeffs[i];
332 if (lb == ub)
continue;
337 min_sum += coeff * lb;
338 max_sum += coeff * ub;
340 min_sum += coeff * ub;
341 max_sum += coeff * lb;
346 if (new_size < num_terms) {
348 ++num_shortened_constraints_;
350 for (
int i = 0; i < num_terms; ++i) {
351 const IntegerVariable
var =
ct->vars[i];
352 const IntegerValue coeff =
ct->coeffs[i];
356 const IntegerValue rhs_adjust = lb * coeff;
361 ct->vars[new_size] =
var;
362 ct->coeffs[new_size] = coeff;
365 ct->vars.resize(new_size);
366 ct->coeffs.resize(new_size);
390 ++num_splitted_constraints_;
393 ++num_coeff_strenghtening_;
394 const int num_terms =
ct->vars.size();
395 const IntegerValue target = max_sum -
ct->ub;
396 for (
int i = 0; i < num_terms; ++i) {
397 const IntegerValue coeff =
ct->coeffs[i];
398 if (coeff > target) {
399 const IntegerVariable
var =
ct->vars[i];
401 ct->coeffs[i] = target;
402 ct->ub -= (coeff - target) * ub;
403 }
else if (coeff < -target) {
404 const IntegerVariable
var =
ct->vars[i];
406 ct->coeffs[i] = -target;
407 ct->ub += (-target - coeff) * lb;
415 ++num_splitted_constraints_;
418 ++num_coeff_strenghtening_;
419 const int num_terms =
ct->vars.size();
420 const IntegerValue target =
ct->lb - min_sum;
421 for (
int i = 0; i < num_terms; ++i) {
422 const IntegerValue coeff =
ct->coeffs[i];
423 if (coeff > target) {
424 const IntegerVariable
var =
ct->vars[i];
426 ct->coeffs[i] = target;
427 ct->lb -= (coeff - target) * lb;
428 }
else if (coeff < -target) {
429 const IntegerVariable
var =
ct->vars[i];
431 ct->coeffs[i] = -target;
432 ct->lb += (-target - coeff) * ub;
444 VLOG(3) <<
"Enter ChangeLP, scan " << constraint_infos_.size()
446 const double saved_dtime = dtime_;
447 std::vector<ConstraintIndex> new_constraints;
448 std::vector<double> new_constraints_efficacies;
449 std::vector<double> new_constraints_orthogonalities;
451 const bool simplify_constraints =
458 bool rescale_active_count =
false;
459 const double tolerance = 1e-6;
460 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
462 if (simplify_constraints &&
463 SimplifyConstraint(&constraint_infos_[i].constraint)) {
464 ++num_simplifications_;
472 constraint_infos_[i].objective_parallelism_computed =
false;
473 constraint_infos_[i].l2_norm =
476 if (constraint_infos_[i].is_in_lp) current_lp_is_changed_ =
true;
477 equiv_constraints_.erase(constraint_infos_[i].
hash);
478 constraint_infos_[i].hash =
479 ComputeHashOfTerms(constraint_infos_[i].constraint);
483 equiv_constraints_[constraint_infos_[i].hash] = i;
486 if (constraint_infos_[i].is_in_lp)
continue;
491 static_cast<double>(constraint_infos_[i].constraint.vars.size());
492 const double activity =
494 const double lb_violation =
495 ToDouble(constraint_infos_[i].constraint.lb) - activity;
496 const double ub_violation =
497 activity -
ToDouble(constraint_infos_[i].constraint.ub);
498 const double violation =
std::max(lb_violation, ub_violation);
499 if (violation >= tolerance) {
500 constraint_infos_[i].inactive_count = 0;
501 new_constraints.push_back(i);
502 new_constraints_efficacies.push_back(violation /
503 constraint_infos_[i].l2_norm);
504 new_constraints_orthogonalities.push_back(1.0);
506 if (objective_is_defined_ &&
507 !constraint_infos_[i].objective_parallelism_computed) {
508 ComputeObjectiveParallelism(i);
509 }
else if (!objective_is_defined_) {
510 constraint_infos_[i].objective_parallelism = 0.0;
513 constraint_infos_[i].current_score =
514 new_constraints_efficacies.back() +
515 constraint_infos_[i].objective_parallelism;
517 if (constraint_infos_[i].is_deletable) {
518 constraint_infos_[i].active_count += constraint_active_count_increase_;
519 if (constraint_infos_[i].active_count >
520 sat_parameters_.cut_max_active_count_value()) {
521 rescale_active_count =
true;
528 if (solution_state !=
nullptr) {
529 const glop::RowIndex num_rows(lp_constraints_.size());
530 const glop::ColIndex num_cols =
533 for (
int i = 0; i < num_rows; ++i) {
534 const ConstraintIndex constraint_index = lp_constraints_[i];
536 solution_state->
statuses[num_cols + glop::ColIndex(i)];
538 constraint_infos_[constraint_index].is_deletable) {
539 constraint_infos_[constraint_index].active_count +=
540 constraint_active_count_increase_;
541 if (constraint_infos_[constraint_index].active_count >
542 sat_parameters_.cut_max_active_count_value()) {
543 rescale_active_count =
true;
549 if (rescale_active_count) {
550 CHECK_GT(sat_parameters_.cut_max_active_count_value(), 0.0);
551 RescaleActiveCounts(1.0 / sat_parameters_.cut_max_active_count_value());
555 constraint_active_count_increase_ *=
556 1.0 / sat_parameters_.cut_active_count_decay();
561 if (MaybeRemoveSomeInactiveConstraints(solution_state)) {
562 current_lp_is_changed_ =
true;
575 const int kBlowupFactor = 4;
576 int constraint_limit =
std::min(sat_parameters_.new_constraints_batch_size(),
577 static_cast<int>(new_constraints.size()));
578 if (lp_constraints_.empty()) {
579 constraint_limit =
std::min(1000,
static_cast<int>(new_constraints.size()));
581 VLOG(3) <<
" - size = " << new_constraints.size()
582 <<
", limit = " << constraint_limit;
584 std::stable_sort(new_constraints.begin(), new_constraints.end(),
585 [&](ConstraintIndex
a, ConstraintIndex
b) {
586 return constraint_infos_[a].current_score >
587 constraint_infos_[b].current_score;
589 if (new_constraints.size() > kBlowupFactor * constraint_limit) {
590 VLOG(3) <<
"Resize candidate constraints from " << new_constraints.size()
591 <<
" down to " << kBlowupFactor * constraint_limit;
592 new_constraints.resize(kBlowupFactor * constraint_limit);
596 int num_skipped_checks = 0;
597 const int kCheckFrequency = 100;
598 ConstraintIndex last_added_candidate = kInvalidConstraintIndex;
599 for (
int i = 0; i < constraint_limit; ++i) {
602 double best_score = 0.0;
603 ConstraintIndex best_candidate = kInvalidConstraintIndex;
604 for (
int j = 0; j < new_constraints.size(); ++j) {
606 if (++num_skipped_checks >= kCheckFrequency) {
607 if (time_limit_->
LimitReached())
return current_lp_is_changed_;
608 num_skipped_checks = 0;
611 const ConstraintIndex new_constraint = new_constraints[j];
612 if (constraint_infos_[new_constraint].is_in_lp)
continue;
614 if (last_added_candidate != kInvalidConstraintIndex) {
615 const double current_orthogonality =
617 constraint_infos_[last_added_candidate].constraint,
618 constraint_infos_[new_constraint].constraint)) /
619 (constraint_infos_[last_added_candidate].l2_norm *
620 constraint_infos_[new_constraint].l2_norm));
621 new_constraints_orthogonalities[j] =
622 std::min(new_constraints_orthogonalities[j], current_orthogonality);
630 if (new_constraints_orthogonalities[j] <
631 sat_parameters_.min_orthogonality_for_lp_constraints()) {
637 const double score = new_constraints_orthogonalities[j] +
638 constraint_infos_[new_constraint].current_score;
640 if (score > best_score || best_candidate == kInvalidConstraintIndex) {
642 best_candidate = new_constraint;
646 if (best_candidate != kInvalidConstraintIndex) {
648 constraint_infos_[best_candidate].is_in_lp =
true;
653 current_lp_is_changed_ =
true;
654 lp_constraints_.push_back(best_candidate);
655 last_added_candidate = best_candidate;
661 VLOG(2) <<
"Added " << num_added <<
" constraints.";
668 if (num_deletable_constraints_ > sat_parameters_.max_num_cuts()) {
669 PermanentlyRemoveSomeConstraints();
676 if (current_lp_is_changed_) {
677 current_lp_is_changed_ =
false;
684 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
685 if (constraint_infos_[i].is_in_lp)
continue;
686 constraint_infos_[i].is_in_lp =
true;
687 lp_constraints_.push_back(i);
695 if (debug_solution.empty())
return true;
697 IntegerValue activity(0);
698 for (
int i = 0; i < cut.
vars.size(); ++i) {
699 const IntegerVariable
var = cut.
vars[i];
700 const IntegerValue coeff = cut.
coeffs[i];
701 activity += coeff * debug_solution[
var];
703 if (activity > cut.
ub || activity < cut.
lb) {
704 LOG(
INFO) <<
"activity " << activity <<
" not in [" << cut.
lb <<
","
714 if (
ct.vars.empty())
return;
716 const double violation =
719 cuts_.
Add({
name,
ct}, violation / l2_norm);
726 manager->
AddCut(candidate.cut, candidate.name, lp_solution);
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
void resize(size_type new_size)
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
void resize(IntType size)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
int64 num_level_zero_enqueues() const
~LinearConstraintManager()
bool ChangeLp(const absl::StrongVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
bool DebugCheckConstraint(const LinearConstraint &cut)
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
void AddAllConstraintsToLp()
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
const std::string & Name() const
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
void AddCut(LinearConstraint ct, const std::string &name, const absl::StrongVector< IntegerVariable, double > &lp_solution)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
const std::vector< Element > & UnorderedElements() const
void Add(Element e, double score)
bool ContainsKey(const Collection &collection, const Key &key)
ColIndex RowToColIndex(RowIndex row)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
double ScalarProduct(const LinearConstraint &constraint1, const LinearConstraint &constraint2)
void CanonicalizeConstraint(LinearConstraint *ct)
bool NoDuplicateVariable(const LinearConstraint &ct)
double ComputeL2Norm(const LinearConstraint &constraint)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
bool VariableIsPositive(IntegerVariable i)
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
uint64 Hash(uint64 num, uint64 c)
VariableStatusRow statuses
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
LinearConstraint constraint