21 #include "absl/container/flat_hash_set.h"
22 #include "absl/strings/match.h"
32 const LinearConstraintManager::ConstraintIndex kInvalidConstraintIndex(-1);
34 size_t ComputeHashOfTerms(
const LinearConstraint&
ct) {
35 DCHECK(std::is_sorted(
ct.vars.begin(),
ct.vars.end()));
37 const int num_terms =
ct.vars.size();
38 for (
int i = 0; i < num_terms; ++i) {
49 if (num_merged_constraints_ > 0) {
50 VLOG(2) <<
"num_merged_constraints: " << num_merged_constraints_;
52 if (num_shortened_constraints_ > 0) {
53 VLOG(2) <<
"num_shortened_constraints: " << num_shortened_constraints_;
55 if (num_splitted_constraints_ > 0) {
56 VLOG(2) <<
"num_splitted_constraints: " << num_splitted_constraints_;
58 if (num_coeff_strenghtening_ > 0) {
59 VLOG(2) <<
"num_coeff_strenghtening: " << num_coeff_strenghtening_;
62 VLOG(2) <<
"Total cuts added: " << num_cuts_ <<
" (out of "
63 << num_add_cut_calls_ <<
" calls) worker: '" << model_->
Name()
65 VLOG(2) <<
" - num simplifications: " << num_simplifications_;
66 for (
const auto& entry : type_to_num_cuts_) {
67 if (entry.second == 1) {
68 VLOG(2) <<
" - added 1 cut of type '" << entry.first <<
"'.";
70 VLOG(2) <<
" - added " << entry.second <<
" cuts of type '"
71 << entry.first <<
"'.";
77 SOLVER_LOG(logger_,
"Total cuts added: ", num_cuts_,
" (out of ",
78 num_add_cut_calls_,
" calls) worker: '", model_->
Name(),
"'");
79 SOLVER_LOG(logger_,
" - num simplifications: ", num_simplifications_);
80 for (
const auto& entry : type_to_num_cuts_) {
81 if (entry.second == 1) {
82 SOLVER_LOG(logger_,
" - added 1 cut of type '", entry.first,
"'.");
84 SOLVER_LOG(logger_,
" - added ", entry.second,
" cuts of type '",
91 void LinearConstraintManager::RescaleActiveCounts(
const double scaling_factor) {
92 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
93 constraint_infos_[i].active_count *= scaling_factor;
95 constraint_active_count_increase_ *= scaling_factor;
96 VLOG(3) <<
"Rescaled active counts by " << scaling_factor;
99 bool LinearConstraintManager::MaybeRemoveSomeInactiveConstraints(
100 glop::BasisState* solution_state) {
101 if (solution_state->IsEmpty())
return false;
102 const glop::RowIndex num_rows(lp_constraints_.size());
103 const glop::ColIndex num_cols =
106 for (
int i = 0; i < num_rows; ++i) {
107 const ConstraintIndex constraint_index = lp_constraints_[i];
116 solution_state->statuses[num_cols + glop::ColIndex(i)];
118 constraint_infos_[constraint_index].inactive_count++;
119 if (constraint_infos_[constraint_index].inactive_count >
120 sat_parameters_.max_consecutive_inactive_count()) {
121 constraint_infos_[constraint_index].is_in_lp =
false;
126 constraint_infos_[constraint_index].inactive_count = 0;
129 lp_constraints_[new_size] = constraint_index;
130 solution_state->statuses[num_cols + glop::ColIndex(new_size)] = row_status;
133 const int num_removed_constraints = lp_constraints_.size() - new_size;
134 lp_constraints_.resize(new_size);
135 solution_state->statuses.resize(num_cols + glop::ColIndex(new_size));
136 if (num_removed_constraints > 0) {
137 VLOG(3) <<
"Removed " << num_removed_constraints <<
" constraints";
139 return num_removed_constraints > 0;
149 SimplifyConstraint(&
ct);
155 const size_t key = ComputeHashOfTerms(
ct);
157 const ConstraintIndex ct_index = equiv_constraints_[key];
158 if (constraint_infos_[ct_index].constraint.vars ==
ct.vars &&
159 constraint_infos_[ct_index].constraint.coeffs ==
ct.coeffs) {
160 if (added !=
nullptr) *added =
false;
161 if (
ct.lb > constraint_infos_[ct_index].constraint.lb) {
162 if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ =
true;
163 constraint_infos_[ct_index].constraint.lb =
ct.lb;
164 if (added !=
nullptr) *added =
true;
166 if (
ct.ub < constraint_infos_[ct_index].constraint.ub) {
167 if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ =
true;
168 constraint_infos_[ct_index].constraint.ub =
ct.ub;
169 if (added !=
nullptr) *added =
true;
171 ++num_merged_constraints_;
176 if (added !=
nullptr) *added =
true;
177 const ConstraintIndex ct_index(constraint_infos_.size());
182 equiv_constraints_[key] = ct_index;
183 ct_info.
active_count = constraint_active_count_increase_;
184 constraint_infos_.push_back(std::move(ct_info));
188 void LinearConstraintManager::ComputeObjectiveParallelism(
189 const ConstraintIndex ct_index) {
190 CHECK(objective_is_defined_);
192 if (!objective_norm_computed_) {
193 objective_l2_norm_ = std::sqrt(sum_of_squared_objective_coeffs_);
194 objective_norm_computed_ =
true;
198 constraint_infos_[ct_index].objective_parallelism_computed =
true;
199 if (constraint_infos_[ct_index].l2_norm == 0.0) {
200 constraint_infos_[ct_index].objective_parallelism = 0.0;
204 const LinearConstraint& lc = constraint_infos_[ct_index].constraint;
205 double unscaled_objective_parallelism = 0.0;
206 for (
int i = 0; i < lc.vars.size(); ++i) {
207 const IntegerVariable
var = lc.vars[i];
208 const auto it = objective_map_.find(
var);
209 if (it == objective_map_.end())
continue;
210 unscaled_objective_parallelism += it->second *
ToDouble(lc.coeffs[i]);
212 const double objective_parallelism =
213 unscaled_objective_parallelism /
214 (constraint_infos_[ct_index].l2_norm * objective_l2_norm_);
215 constraint_infos_[ct_index].objective_parallelism =
216 std::abs(objective_parallelism);
224 std::string extra_info) {
225 ++num_add_cut_calls_;
226 if (
ct.vars.empty())
return false;
229 const double violation =
234 if (violation / l2_norm < 1e-5)
return false;
237 const ConstraintIndex ct_index =
Add(std::move(
ct), &added);
241 if (!added)
return false;
245 constraint_infos_[ct_index].is_deletable =
true;
247 VLOG(3) <<
"Cut '" << type_name <<
"'"
248 <<
" size=" << constraint_infos_[ct_index].constraint.vars.size()
251 <<
" norm=" << l2_norm <<
" violation=" << violation
252 <<
" eff=" << violation / l2_norm <<
" " << extra_info;
255 num_deletable_constraints_++;
256 type_to_num_cuts_[type_name]++;
260 void LinearConstraintManager::PermanentlyRemoveSomeConstraints() {
261 std::vector<double> deletable_constraint_counts;
262 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
263 if (constraint_infos_[i].is_deletable && !constraint_infos_[i].is_in_lp) {
264 deletable_constraint_counts.push_back(constraint_infos_[i].active_count);
267 if (deletable_constraint_counts.empty())
return;
268 std::sort(deletable_constraint_counts.begin(),
269 deletable_constraint_counts.end());
273 double active_count_threshold = std::numeric_limits<double>::infinity();
274 if (sat_parameters_.cut_cleanup_target() <
275 deletable_constraint_counts.size()) {
276 active_count_threshold =
277 deletable_constraint_counts[sat_parameters_.cut_cleanup_target()];
280 ConstraintIndex new_size(0);
281 equiv_constraints_.clear();
283 constraint_infos_.size());
284 int num_deleted_constraints = 0;
285 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
286 if (constraint_infos_[i].is_deletable && !constraint_infos_[i].is_in_lp &&
287 constraint_infos_[i].active_count <= active_count_threshold &&
288 num_deleted_constraints < sat_parameters_.cut_cleanup_target()) {
289 ++num_deleted_constraints;
294 constraint_infos_[new_size] = std::move(constraint_infos_[i]);
296 index_mapping[i] = new_size;
299 equiv_constraints_[constraint_infos_[new_size].hash] = new_size;
302 constraint_infos_.resize(new_size.value());
305 for (
int i = 0; i < lp_constraints_.size(); ++i) {
306 lp_constraints_[i] = index_mapping[lp_constraints_[i]];
309 if (num_deleted_constraints > 0) {
310 VLOG(1) <<
"Constraint manager cleanup: #deleted:"
311 << num_deleted_constraints;
313 num_deletable_constraints_ -= num_deleted_constraints;
317 IntegerValue coeff) {
318 if (coeff == IntegerValue(0))
return;
319 objective_is_defined_ =
true;
324 const double coeff_as_double =
ToDouble(coeff);
325 const auto insert = objective_map_.insert({
var, coeff_as_double});
327 <<
"SetObjectiveCoefficient() called twice with same variable";
328 sum_of_squared_objective_coeffs_ += coeff_as_double * coeff_as_double;
332 bool term_changed =
false;
334 IntegerValue min_sum(0);
335 IntegerValue max_sum(0);
336 IntegerValue max_magnitude(0);
338 const int num_terms =
ct->vars.size();
339 for (
int i = 0; i < num_terms; ++i) {
340 const IntegerVariable
var =
ct->vars[i];
341 const IntegerValue coeff =
ct->coeffs[i];
347 if (lb == ub)
continue;
352 min_sum += coeff * lb;
353 max_sum += coeff * ub;
355 min_sum += coeff * ub;
356 max_sum += coeff * lb;
361 if (new_size < num_terms) {
363 ++num_shortened_constraints_;
365 for (
int i = 0; i < num_terms; ++i) {
366 const IntegerVariable
var =
ct->vars[i];
367 const IntegerValue coeff =
ct->coeffs[i];
371 const IntegerValue rhs_adjust = lb * coeff;
376 ct->vars[new_size] =
var;
377 ct->coeffs[new_size] = coeff;
380 ct->vars.resize(new_size);
381 ct->coeffs.resize(new_size);
405 ++num_splitted_constraints_;
408 ++num_coeff_strenghtening_;
409 const int num_terms =
ct->vars.size();
410 const IntegerValue target = max_sum -
ct->ub;
411 for (
int i = 0; i < num_terms; ++i) {
412 const IntegerValue coeff =
ct->coeffs[i];
413 if (coeff > target) {
414 const IntegerVariable
var =
ct->vars[i];
416 ct->coeffs[i] = target;
417 ct->ub -= (coeff - target) * ub;
418 }
else if (coeff < -target) {
419 const IntegerVariable
var =
ct->vars[i];
421 ct->coeffs[i] = -target;
422 ct->ub += (-target - coeff) * lb;
430 ++num_splitted_constraints_;
433 ++num_coeff_strenghtening_;
434 const int num_terms =
ct->vars.size();
435 const IntegerValue target =
ct->lb - min_sum;
436 for (
int i = 0; i < num_terms; ++i) {
437 const IntegerValue coeff =
ct->coeffs[i];
438 if (coeff > target) {
439 const IntegerVariable
var =
ct->vars[i];
441 ct->coeffs[i] = target;
442 ct->lb -= (coeff - target) * lb;
443 }
else if (coeff < -target) {
444 const IntegerVariable
var =
ct->vars[i];
446 ct->coeffs[i] = -target;
447 ct->lb += (-target - coeff) * ub;
459 VLOG(3) <<
"Enter ChangeLP, scan " << constraint_infos_.size()
461 const double saved_dtime = dtime_;
462 std::vector<ConstraintIndex> new_constraints;
463 std::vector<double> new_constraints_efficacies;
464 std::vector<double> new_constraints_orthogonalities;
466 const bool simplify_constraints =
473 bool rescale_active_count =
false;
474 const double tolerance = 1e-6;
475 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
477 if (simplify_constraints &&
478 SimplifyConstraint(&constraint_infos_[i].constraint)) {
479 ++num_simplifications_;
487 constraint_infos_[i].objective_parallelism_computed =
false;
488 constraint_infos_[i].l2_norm =
491 if (constraint_infos_[i].is_in_lp) current_lp_is_changed_ =
true;
492 equiv_constraints_.erase(constraint_infos_[i].
hash);
493 constraint_infos_[i].hash =
494 ComputeHashOfTerms(constraint_infos_[i].constraint);
498 equiv_constraints_[constraint_infos_[i].hash] = i;
501 if (constraint_infos_[i].is_in_lp)
continue;
506 static_cast<double>(constraint_infos_[i].constraint.vars.size());
507 const double activity =
509 const double lb_violation =
510 ToDouble(constraint_infos_[i].constraint.lb) - activity;
511 const double ub_violation =
512 activity -
ToDouble(constraint_infos_[i].constraint.ub);
513 const double violation =
std::max(lb_violation, ub_violation);
514 if (violation >= tolerance) {
515 constraint_infos_[i].inactive_count = 0;
516 new_constraints.push_back(i);
517 new_constraints_efficacies.push_back(violation /
518 constraint_infos_[i].l2_norm);
519 new_constraints_orthogonalities.push_back(1.0);
521 if (objective_is_defined_ &&
522 !constraint_infos_[i].objective_parallelism_computed) {
523 ComputeObjectiveParallelism(i);
524 }
else if (!objective_is_defined_) {
525 constraint_infos_[i].objective_parallelism = 0.0;
528 constraint_infos_[i].current_score =
529 new_constraints_efficacies.back() +
530 constraint_infos_[i].objective_parallelism;
532 if (constraint_infos_[i].is_deletable) {
533 constraint_infos_[i].active_count += constraint_active_count_increase_;
534 if (constraint_infos_[i].active_count >
535 sat_parameters_.cut_max_active_count_value()) {
536 rescale_active_count =
true;
543 if (solution_state !=
nullptr) {
544 const glop::RowIndex num_rows(lp_constraints_.size());
545 const glop::ColIndex num_cols =
548 for (
int i = 0; i < num_rows; ++i) {
549 const ConstraintIndex constraint_index = lp_constraints_[i];
551 solution_state->
statuses[num_cols + glop::ColIndex(i)];
553 constraint_infos_[constraint_index].is_deletable) {
554 constraint_infos_[constraint_index].active_count +=
555 constraint_active_count_increase_;
556 if (constraint_infos_[constraint_index].active_count >
557 sat_parameters_.cut_max_active_count_value()) {
558 rescale_active_count =
true;
564 if (rescale_active_count) {
565 CHECK_GT(sat_parameters_.cut_max_active_count_value(), 0.0);
566 RescaleActiveCounts(1.0 / sat_parameters_.cut_max_active_count_value());
570 constraint_active_count_increase_ *=
571 1.0 / sat_parameters_.cut_active_count_decay();
576 if (MaybeRemoveSomeInactiveConstraints(solution_state)) {
577 current_lp_is_changed_ =
true;
590 const int kBlowupFactor = 4;
591 int constraint_limit =
std::min(sat_parameters_.new_constraints_batch_size(),
592 static_cast<int>(new_constraints.size()));
593 if (lp_constraints_.empty()) {
594 constraint_limit =
std::min(1000,
static_cast<int>(new_constraints.size()));
596 VLOG(3) <<
" - size = " << new_constraints.size()
597 <<
", limit = " << constraint_limit;
599 std::stable_sort(new_constraints.begin(), new_constraints.end(),
600 [&](ConstraintIndex
a, ConstraintIndex
b) {
601 return constraint_infos_[a].current_score >
602 constraint_infos_[b].current_score;
604 if (new_constraints.size() > kBlowupFactor * constraint_limit) {
605 VLOG(3) <<
"Resize candidate constraints from " << new_constraints.size()
606 <<
" down to " << kBlowupFactor * constraint_limit;
607 new_constraints.resize(kBlowupFactor * constraint_limit);
611 int num_skipped_checks = 0;
612 const int kCheckFrequency = 100;
613 ConstraintIndex last_added_candidate = kInvalidConstraintIndex;
614 for (
int i = 0; i < constraint_limit; ++i) {
617 double best_score = 0.0;
618 ConstraintIndex best_candidate = kInvalidConstraintIndex;
619 for (
int j = 0; j < new_constraints.size(); ++j) {
621 if (++num_skipped_checks >= kCheckFrequency) {
622 if (time_limit_->
LimitReached())
return current_lp_is_changed_;
623 num_skipped_checks = 0;
626 const ConstraintIndex new_constraint = new_constraints[j];
627 if (constraint_infos_[new_constraint].is_in_lp)
continue;
629 if (last_added_candidate != kInvalidConstraintIndex) {
630 const double current_orthogonality =
632 constraint_infos_[last_added_candidate].constraint,
633 constraint_infos_[new_constraint].constraint)) /
634 (constraint_infos_[last_added_candidate].l2_norm *
635 constraint_infos_[new_constraint].l2_norm));
636 new_constraints_orthogonalities[j] =
637 std::min(new_constraints_orthogonalities[j], current_orthogonality);
645 if (new_constraints_orthogonalities[j] <
646 sat_parameters_.min_orthogonality_for_lp_constraints()) {
652 const double score = new_constraints_orthogonalities[j] +
653 constraint_infos_[new_constraint].current_score;
655 if (score > best_score || best_candidate == kInvalidConstraintIndex) {
657 best_candidate = new_constraint;
661 if (best_candidate != kInvalidConstraintIndex) {
663 constraint_infos_[best_candidate].is_in_lp =
true;
668 current_lp_is_changed_ =
true;
669 lp_constraints_.push_back(best_candidate);
670 last_added_candidate = best_candidate;
676 VLOG(3) <<
"Added " << num_added <<
" constraints.";
683 if (num_deletable_constraints_ > sat_parameters_.max_num_cuts()) {
684 PermanentlyRemoveSomeConstraints();
691 if (current_lp_is_changed_) {
692 current_lp_is_changed_ =
false;
699 for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
700 if (constraint_infos_[i].is_in_lp)
continue;
701 constraint_infos_[i].is_in_lp =
true;
702 lp_constraints_.push_back(i);
710 if (debug_solution.empty())
return true;
712 IntegerValue activity(0);
713 for (
int i = 0; i < cut.
vars.size(); ++i) {
714 const IntegerVariable
var = cut.
vars[i];
715 const IntegerValue coeff = cut.
coeffs[i];
716 activity += coeff * debug_solution[
var];
718 if (activity > cut.
ub || activity < cut.
lb) {
719 LOG(
INFO) <<
"activity " << activity <<
" not in [" << cut.
lb <<
","
729 if (
ct.vars.empty())
return;
731 const double violation =
734 cuts_.
Add({
name,
ct}, violation / l2_norm);
741 manager->
AddCut(candidate.cut, candidate.name, lp_solution);
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
bool LoggingIsEnabled() const
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)
int64_t num_level_zero_enqueues() const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) 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)
Collection of objects used to extend the Constraint Solver library.
uint64_t Hash(uint64_t num, uint64_t c)
VariableStatusRow statuses
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
LinearConstraint constraint
#define SOLVER_LOG(logger,...)
#define VLOG_IS_ON(verboselevel)