22#include "absl/container/flat_hash_map.h"
23#include "absl/hash/hash.h"
24#include "absl/strings/str_format.h"
25#include "absl/types/span.h"
29#include "ortools/sat/sat_parameters.pb.h"
40bool LiteralComparator(
const LiteralWithCoeff&
a,
const LiteralWithCoeff&
b) {
41 return a.literal.Index() <
b.literal.Index();
44bool CoeffComparator(
const LiteralWithCoeff&
a,
const LiteralWithCoeff&
b) {
45 if (
a.coefficient ==
b.coefficient) {
46 return a.literal.Index() <
b.literal.Index();
48 return a.coefficient <
b.coefficient;
54 std::vector<LiteralWithCoeff>* cst,
Coefficient* bound_shift,
63 std::sort(cst->begin(), cst->end(), LiteralComparator);
66 for (
int i = 0; i < cst->size(); ++i) {
87 (*cst)[
index] = current;
99 for (
int i = 0; i < cst->size(); ++i) {
110 std::sort(cst->begin(), cst->end(), CoeffComparator);
117 std::vector<LiteralWithCoeff>* cst,
Coefficient* bound_shift,
122 if (mapping[entry.literal.Index()] >= 0) {
127 if (!
SafeAddInto(-entry.coefficient, &shift_due_to_fixed_variables)) {
137 *bound_shift = shift_due_to_fixed_variables;
143 if (!
SafeAddInto(shift_due_to_fixed_variables, bound_shift))
return false;
149 const std::vector<LiteralWithCoeff>& cst) {
152 if (term.coefficient < previous)
return false;
153 previous = term.coefficient;
161 std::vector<LiteralWithCoeff>* cst,
Coefficient* rhs) {
168 if (x.coefficient > *rhs) x.coefficient = *rhs + 1;
177 if (bound_shift > 0) {
197 if (bound_shift > 0) {
205 if (shifted_lb <= 0) {
210 return max_value - shifted_lb;
223 if (use_upper_bound) {
226 if (!AddConstraint(*cst, max_value, rhs))
return false;
228 if (use_lower_bound) {
230 for (
int i = 0; i < cst->size(); ++i) {
231 (*cst)[i].literal = (*cst)[i].literal.Negated();
235 if (!AddConstraint(*cst, max_value, rhs))
return false;
240bool CanonicalBooleanLinearProblem::AddConstraint(
241 const std::vector<LiteralWithCoeff>& cst,
Coefficient max_value,
243 if (rhs < 0)
return false;
244 if (rhs >= max_value)
return true;
245 constraints_.emplace_back(cst.begin(), cst.end());
252 if (terms_.
size() != num_variables) {
274 CHECK_LT(rhs_, max_sum_) <<
"Trivially sat.";
285 max_sum_ -= removed_sum;
292 if (!result.empty()) result +=
" + ";
296 result += absl::StrFormat(
" <= %d", rhs_.value());
303 const Trail& trail,
int trail_index)
const {
312 return rhs_ - activity;
340 max_sum_ -= removed_sum;
342 return rhs_ - activity;
360 if (slack == target)
return;
372 terms_[
var] = (terms_[
var] > 0) ? terms_[
var] - diff : terms_[
var] + diff;
383 std::vector<LiteralWithCoeff>* output) {
391 std::sort(output->begin(), output->end(), CoeffComparator);
394Coefficient MutableUpperBoundedLinearConstraint::ComputeMaxSum()
const {
403 const std::vector<LiteralWithCoeff>& cst)
404 : is_marked_for_deletion_(false),
406 first_reason_trail_index_(-1),
409 DCHECK(std::is_sorted(cst.begin(), cst.end(), CoeffComparator));
410 literals_.reserve(cst.size());
417 if (term.coefficient != prev) {
418 prev = term.coefficient;
422 coeffs_.reserve(size);
423 starts_.reserve(size + 1);
428 if (term.coefficient != prev) {
429 prev = term.coefficient;
430 coeffs_.push_back(term.coefficient);
431 starts_.push_back(literals_.size());
433 literals_.push_back(term.literal);
437 starts_.push_back(literals_.size());
439 hash_ = absl::Hash<std::vector<LiteralWithCoeff>>()(cst);
444 int literal_index = 0;
449 if (literal_index == starts_[coeff_index + 1]) ++coeff_index;
455 const std::vector<LiteralWithCoeff>& cst) {
456 if (cst.size() != literals_.size())
return false;
457 int literal_index = 0;
460 if (literals_[literal_index] != term.literal)
return false;
461 if (coeffs_[coeff_index] != term.coefficient)
return false;
463 if (literal_index == starts_[coeff_index + 1]) {
483 std::vector<Coefficient> sum_at_previous_level(last_level + 2,
486 int max_relevant_trail_index = 0;
487 if (trail_index > 0) {
488 int literal_index = 0;
491 const BooleanVariable
var =
literal.Variable();
495 max_relevant_trail_index =
501 if (literal_index == starts_[coeff_index + 1]) ++coeff_index;
505 if (slack < 0)
return false;
508 for (
int i = 1; i < sum_at_previous_level.size(); ++i) {
509 sum_at_previous_level[i] += sum_at_previous_level[i - 1];
514 int literal_index = 0;
517 const BooleanVariable
var =
literal.Variable();
522 CHECK_LE(coeffs_[coeff_index], rhs_ - sum_at_previous_level[level])
523 <<
"var should have been propagated at an earlier level !";
526 if (literal_index == starts_[coeff_index + 1]) ++coeff_index;
536 index_ = coeffs_.size() - 1;
537 already_propagated_end_ = literals_.size();
538 Update(slack, threshold);
539 return *threshold < 0
540 ?
Propagate(max_relevant_trail_index, threshold, trail, helper)
548 const Coefficient slack = GetSlackFromThreshold(*threshold);
549 DCHECK_GE(slack, 0) <<
"The constraint is already a conflict!";
550 while (index_ >= 0 && coeffs_[index_] > slack) --index_;
553 BooleanVariable first_propagated_variable(-1);
554 for (
int i = starts_[index_ + 1]; i < already_propagated_end_; ++i) {
559 FillReason(*trail, trail_index, literals_[i].Variable(),
561 helper->
conflict.push_back(literals_[i].Negated());
562 Update(slack, threshold);
567 if (first_propagated_variable < 0) {
568 if (first_reason_trail_index_ == -1) {
569 first_reason_trail_index_ = trail->
Index();
571 helper->
Enqueue(literals_[i].Negated(), trail_index,
this, trail);
572 first_propagated_variable = literals_[i].Variable();
578 first_propagated_variable);
582 Update(slack, threshold);
588 const Trail& trail,
int source_trail_index,
589 BooleanVariable propagated_variable, std::vector<Literal>* reason) {
595 reason->push_back(trail[source_trail_index].Negated());
602 int last_coeff_index = 0;
610 int coeff_index = coeffs_.size() - 1;
611 for (
int i = literals_.size() - 1; i >= 0; --i) {
613 if (
literal.Variable() == propagated_variable) {
614 propagated_variable_coefficient = coeffs_[coeff_index];
619 reason->push_back(
literal.Negated());
621 last_coeff_index = coeff_index;
623 slack -= coeffs_[coeff_index];
626 if (i == starts_[coeff_index]) {
630 DCHECK_GT(propagated_variable_coefficient, slack);
631 DCHECK_GE(propagated_variable_coefficient, 0);
634 if (reason->size() <= 1 || coeffs_.size() == 1)
return;
636 Coefficient limit = propagated_variable_coefficient - slack;
641 coeff_index = last_coeff_index;
642 if (coeffs_[coeff_index] >= limit)
return;
643 for (
int i = last_i; i < literals_.size(); ++i) {
645 if (i == starts_[coeff_index + 1]) {
647 if (coeffs_[coeff_index] >= limit)
break;
649 if (
literal.Negated() != reason->back())
continue;
650 limit -= coeffs_[coeff_index];
652 if (coeffs_[coeff_index] >= limit)
break;
659 const Trail& trail,
int trail_index,
662 int literal_index = 0;
670 if (literal_index == starts_[coeff_index + 1]) ++coeff_index;
676 const Trail& trail, BooleanVariable
var,
685 int literal_index = 0;
691 var_coeff = coeffs_[coeff_index];
697 if (literal_index == starts_[coeff_index + 1]) ++coeff_index;
744 const Coefficient new_slack_ub = slack + *conflict_slack - min_coeffs;
745 CHECK_LT(*conflict_slack, conflict_var_coeff);
747 if (new_slack_ub < 0) {
749 DCHECK_EQ(*conflict_slack + slack - cancelation,
767 if (new_slack_ub <
std::max(var_coeff, conflict_var_coeff) - min_coeffs) {
769 if (var_coeff < conflict_var_coeff) {
770 conflict_diff += reduc;
779 conflict_diff = *conflict_slack;
784 CHECK_LE(conflict_diff, *conflict_slack);
785 if (conflict_diff > 0) {
786 conflict->
ReduceSlackTo(trail, limit_trail_index, *conflict_slack,
787 *conflict_slack - conflict_diff);
788 *conflict_slack -= conflict_diff;
808 const Coefficient new_coeff = coeffs_[coeff_index] - diff;
816 if (literal_index == starts_[coeff_index + 1]) ++coeff_index;
825 const Coefficient slack = GetSlackFromThreshold(*threshold);
826 while (index_ + 1 < coeffs_.size() && coeffs_[index_ + 1] <= slack) ++index_;
827 Update(slack, threshold);
828 if (first_reason_trail_index_ >= trail_index) {
829 first_reason_trail_index_ = -1;
839 DCHECK(std::is_sorted(cst.begin(), cst.end(), CoeffComparator));
842 if (constraints_.empty()) {
849 std::unique_ptr<UpperBoundedLinearConstraint> c(
851 std::vector<UpperBoundedLinearConstraint*>& duplicate_candidates =
852 possible_duplicates_[c->hash()];
856 if (candidate->HasIdenticalTerms(cst)) {
857 if (rhs < candidate->Rhs()) {
861 ConstraintIndex i(0);
862 while (i < constraints_.size() &&
863 constraints_[i.value()].get() != candidate) {
868 &thresholds_[i], trail,
879 trail, &enqueue_helper_)) {
884 const ConstraintIndex cst_index(constraints_.size());
885 duplicate_candidates.push_back(c.get());
886 constraints_.emplace_back(c.release());
889 to_update_[term.literal.Index()].
push_back(ConstraintIndexWithCoeff(
891 cst_index, term.coefficient));
898 DeleteSomeLearnedConstraintIfNeeded();
899 const int old_num_constraints = constraints_.size();
904 if (result && constraints_.size() > old_num_constraints) {
905 constraints_.back()->set_is_learned(
true);
910bool PbConstraints::PropagateNext(
Trail* trail) {
918 bool conflict =
false;
919 num_threshold_updates_ += to_update_[true_literal.
Index()].
size();
920 for (ConstraintIndexWithCoeff& update : to_update_[true_literal.
Index()]) {
922 thresholds_[update.index] - update.coefficient;
923 thresholds_[update.index] = threshold;
924 if (threshold < 0 && !conflict) {
926 constraints_[update.index.value()].get();
927 update.need_untrail_inspection =
true;
928 ++num_constraint_lookups_;
930 if (!cst->
Propagate(source_trail_index, &thresholds_[update.index], trail,
933 conflicting_constraint_index_ = update.index;
939 num_inspected_constraint_literals_ +=
947 const int old_index = trail->
Index();
949 if (!PropagateNext(trail))
return false;
960 for (ConstraintIndexWithCoeff& update : to_update_[
literal.Index()]) {
961 thresholds_[update.index] += update.coefficient;
965 if (update.need_untrail_inspection) {
966 update.need_untrail_inspection =
false;
967 to_untrail_.
Set(update.index);
972 constraints_[cst_index.value()]->Untrail(&(thresholds_[cst_index]),
978 int trail_index)
const {
981 enqueue_helper_.
reasons[trail_index];
984 trail[trail_index].Variable(), reason);
989 int trail_index)
const {
991 enqueue_helper_.
reasons[trail_index];
998void PbConstraints::ComputeNewLearnedConstraintLimit() {
999 const int num_constraints = constraints_.size();
1000 target_number_of_learned_constraint_ =
1001 num_constraints + parameters_->pb_cleanup_increment();
1002 num_learned_constraint_before_cleanup_ =
1003 static_cast<int>(target_number_of_learned_constraint_ /
1004 parameters_->pb_cleanup_ratio()) -
1008void PbConstraints::DeleteSomeLearnedConstraintIfNeeded() {
1009 if (num_learned_constraint_before_cleanup_ == 0) {
1011 ComputeNewLearnedConstraintLimit();
1014 --num_learned_constraint_before_cleanup_;
1015 if (num_learned_constraint_before_cleanup_ > 0)
return;
1020 std::vector<double> activities;
1021 for (
int i = 0; i < constraints_.size(); ++i) {
1022 const UpperBoundedLinearConstraint& constraint = *(constraints_[i].get());
1023 if (constraint.is_learned() && !constraint.is_used_as_a_reason()) {
1024 activities.push_back(constraint.activity());
1030 std::sort(activities.begin(), activities.end());
1031 const int num_constraints_to_delete =
1032 constraints_.size() - target_number_of_learned_constraint_;
1033 CHECK_GT(num_constraints_to_delete, 0);
1034 if (num_constraints_to_delete >= activities.size()) {
1037 for (
int i = 0; i < constraints_.size(); ++i) {
1038 UpperBoundedLinearConstraint& constraint = *(constraints_[i].get());
1039 if (constraint.is_learned() && !constraint.is_used_as_a_reason()) {
1040 constraint.MarkForDeletion();
1044 const double limit_activity = activities[num_constraints_to_delete];
1045 int num_constraint_at_limit_activity = 0;
1046 for (
int i = num_constraints_to_delete; i >= 0; --i) {
1047 if (activities[i] == limit_activity) {
1048 ++num_constraint_at_limit_activity;
1058 for (
int i = constraints_.size() - 1; i >= 0; --i) {
1059 UpperBoundedLinearConstraint& constraint = *(constraints_[i].get());
1060 if (constraint.is_learned() && !constraint.is_used_as_a_reason()) {
1061 if (constraint.activity() <= limit_activity) {
1062 if (constraint.activity() == limit_activity &&
1063 num_constraint_at_limit_activity > 0) {
1064 --num_constraint_at_limit_activity;
1066 constraint.MarkForDeletion();
1074 DeleteConstraintMarkedForDeletion();
1075 ComputeNewLearnedConstraintLimit();
1080 const double max_activity = parameters_->max_clause_activity_value();
1082 constraint_activity_increment_);
1083 if (constraint->
activity() > max_activity) {
1089 constraint_activity_increment_ *= scaling_factor;
1090 for (
int i = 0; i < constraints_.size(); ++i) {
1091 constraints_[i]->set_activity(constraints_[i]->activity() * scaling_factor);
1096 const double decay = parameters_->clause_activity_decay();
1097 constraint_activity_increment_ *= 1.0 / decay;
1100void PbConstraints::DeleteConstraintMarkedForDeletion() {
1102 constraints_.size(), ConstraintIndex(-1));
1103 ConstraintIndex new_index(0);
1104 for (ConstraintIndex i(0); i < constraints_.size(); ++i) {
1105 if (!constraints_[i.value()]->is_marked_for_deletion()) {
1106 index_mapping[i] = new_index;
1107 if (new_index < i) {
1108 constraints_[new_index.value()] = std::move(constraints_[i.value()]);
1109 thresholds_[new_index] = thresholds_[i];
1114 UpperBoundedLinearConstraint* c = constraints_[i.value()].get();
1115 std::vector<UpperBoundedLinearConstraint*>& ref =
1116 possible_duplicates_[c->hash()];
1117 for (
int i = 0; i < ref.size(); ++i) {
1126 constraints_.resize(new_index.value());
1127 thresholds_.
resize(new_index.value());
1131 for (LiteralIndex lit(0); lit < to_update_.
size(); ++lit) {
1132 std::vector<ConstraintIndexWithCoeff>& updates = to_update_[lit];
1134 for (
int i = 0; i < updates.size(); ++i) {
1135 const ConstraintIndex m = index_mapping[updates[i].index];
1137 updates[new_index] = updates[i];
1138 updates[new_index].index = m;
1142 updates.
resize(new_index);
#define CHECK_LT(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
void assign(size_type n, const value_type &val)
void resize(size_type new_size)
void push_back(const value_type &x)
void Set(IntegerType index)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void ClearAndResize(IntegerType size)
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
LiteralIndex Index() const
BooleanVariable Variable() const
void ReduceCoefficients()
Literal GetLiteral(BooleanVariable var) const
const std::vector< BooleanVariable > & PossibleNonZeros() const
Coefficient ComputeSlackForTrailPrefix(const Trail &trail, int trail_index) const
Coefficient ReduceCoefficientsAndComputeSlackForTrailPrefix(const Trail &trail, int trail_index)
void ReduceSlackTo(const Trail &trail, int trail_index, Coefficient initial_slack, Coefficient target)
std::string DebugString()
Coefficient CancelationAmount(Literal literal, Coefficient coeff) const
void AddTerm(Literal literal, Coefficient coeff)
void AddToRhs(Coefficient value)
void ClearAndResize(int num_variables)
void CopyIntoVector(std::vector< LiteralWithCoeff > *output)
Coefficient GetCoefficient(BooleanVariable var) const
bool Propagate(Trail *trail) final
void RescaleActivities(double scaling_factor)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
bool AddConstraint(const std::vector< LiteralWithCoeff > &cst, Coefficient rhs, Trail *trail)
UpperBoundedLinearConstraint * ReasonPbConstraint(int trail_index) const
void UpdateActivityIncrement()
void BumpActivity(UpperBoundedLinearConstraint *constraint)
void Untrail(const Trail &trail, int trail_index) final
bool AddLearnedConstraint(const std::vector< LiteralWithCoeff > &cst, Coefficient rhs, Trail *trail)
int propagation_trail_index_
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
const VariablesAssignment & Assignment() const
std::vector< Literal > * MutableConflict()
const AssignmentInfo & Info(BooleanVariable var) const
int CurrentDecisionLevel() const
void set_activity(double activity)
Coefficient ComputeCancelation(const Trail &trail, int trail_index, const MutableUpperBoundedLinearConstraint &conflict)
bool Propagate(int trail_index, Coefficient *threshold, Trail *trail, PbConstraintsEnqueueHelper *helper)
void FillReason(const Trail &trail, int source_trail_index, BooleanVariable propagated_variable, std::vector< Literal > *reason)
bool HasIdenticalTerms(const std::vector< LiteralWithCoeff > &cst)
void ResolvePBConflict(const Trail &trail, BooleanVariable var, MutableUpperBoundedLinearConstraint *conflict, Coefficient *conflict_slack)
bool InitializeRhs(Coefficient rhs, int trail_index, Coefficient *threshold, Trail *trail, PbConstraintsEnqueueHelper *helper)
int already_propagated_end() const
void Untrail(Coefficient *threshold, int trail_index)
void AddToConflict(MutableUpperBoundedLinearConstraint *conflict)
UpperBoundedLinearConstraint(const std::vector< LiteralWithCoeff > &cst)
bool VariableIsAssigned(BooleanVariable var) const
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::tuple< int64_t, int64_t, const double > Coefficient
Coefficient ComputeCanonicalRhs(Coefficient upper_bound, Coefficient bound_shift, Coefficient max_value)
bool ApplyLiteralMapping(const absl::StrongVector< LiteralIndex, LiteralIndex > &mapping, std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
Coefficient ComputeNegatedCanonicalRhs(Coefficient lower_bound, Coefficient bound_shift, Coefficient max_value)
void SimplifyCanonicalBooleanLinearConstraint(std::vector< LiteralWithCoeff > *cst, Coefficient *rhs)
const LiteralIndex kTrueLiteralIndex(-2)
bool ComputeBooleanLinearExpressionCanonicalForm(std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
const LiteralIndex kFalseLiteralIndex(-3)
bool BooleanLinearExpressionIsCanonical(const std::vector< LiteralWithCoeff > &cst)
Collection of objects used to extend the Constraint Solver library.
bool SafeAddInto(IntegerType a, IntegerType *b)
#define SCOPED_TIME_STAT(stats)
UpperBoundedLinearConstraint * pb_constraint
std::vector< ReasonInfo > reasons
void Enqueue(Literal l, int source_trail_index, UpperBoundedLinearConstraint *ct, Trail *trail)
std::vector< Literal > conflict