26#include "absl/base/attributes.h"
27#include "absl/cleanup/cleanup.h"
28#include "absl/container/btree_map.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/inlined_vector.h"
31#include "absl/log/check.h"
32#include "absl/meta/type_traits.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.h"
40#include "ortools/sat/sat_parameters.pb.h"
53 absl::Span<const IntegerVariable> vars) {
54 std::vector<IntegerVariable> result(vars.size());
55 for (
int i = 0;
i < vars.size(); ++
i) {
62 return absl::StrCat(
"(literal = ",
literal.DebugString(),
63 ", value = ",
value.value(),
")");
75 encoding_by_var_.reserve(num_vars);
76 equality_to_associated_literal_.reserve(num_vars);
77 equality_by_var_.reserve(num_vars);
83 CHECK_EQ(0, sat_solver_->CurrentDecisionLevel());
86 CHECK(!domains_[index].IsEmpty());
87 CHECK_LT(domains_[index].Size(), 100000)
88 <<
"Domain too large for full encoding.";
99 for (
const int64_t v : domains_[index].Values()) {
100 tmp_values_.push_back(IntegerValue(v));
102 for (
const IntegerValue v : tmp_values_) {
113 if (index >= is_fully_encoded_.size())
return false;
116 if (is_fully_encoded_[index])
return true;
123 const int64_t initial_domain_size = domains_[index].Size();
124 if (equality_by_var_[index].size() < initial_domain_size)
return false;
133 const auto& ref = equality_by_var_[index];
135 for (
const int64_t v : domains_[index].Values()) {
136 if (
i < ref.size() && v == ref[
i].value) {
140 if (
i == ref.size()) {
141 is_fully_encoded_[index] =
true;
143 return is_fully_encoded_[index];
147 IntegerVariable var)
const {
153 IntegerVariable var)
const {
155 if (index >= equality_by_var_.size()) {
156 partial_encoding_.clear();
157 return partial_encoding_;
161 partial_encoding_.assign(equality_by_var_[index].begin(),
162 equality_by_var_[index].end());
163 for (
int i = 0;
i < partial_encoding_.size(); ++
i) {
165 if (sat_solver_->Assignment().LiteralIsFalse(pair.
literal))
continue;
166 if (sat_solver_->Assignment().LiteralIsTrue(pair.
literal)) {
167 partial_encoding_.clear();
168 partial_encoding_.push_back(pair);
172 partial_encoding_[new_size++] = pair;
174 partial_encoding_.resize(new_size);
175 std::sort(partial_encoding_.begin(), partial_encoding_.end(),
178 if (trail_->CurrentDecisionLevel() == 0) {
180 equality_by_var_[index].assign(partial_encoding_.begin(),
181 partial_encoding_.end());
185 std::reverse(partial_encoding_.begin(), partial_encoding_.end());
188 return partial_encoding_;
194void IntegerEncoder::AddImplications(
195 const absl::btree_map<IntegerValue, Literal>& map,
196 absl::btree_map<IntegerValue, Literal>::const_iterator it,
198 if (!add_implications_)
return;
199 DCHECK_EQ(it->second, associated_lit);
204 if (it != map.begin()) {
207 before_index = before_it->second.Index();
213 if (after_it != map.end()) after_index = after_it->second.Index();
219 {Literal(after_index).Negated(), associated_lit});
222 sat_solver_->AddClauseDuringSearch(
223 {associated_lit.
Negated(), Literal(before_index)});
228 CHECK_EQ(0, sat_solver_->CurrentDecisionLevel());
229 add_implications_ =
true;
233 const int num_vars = encoding_by_var_.size();
234 for (PositiveOnlyIndex index(0); index < num_vars; ++index) {
236 const IntegerVariable var(2 * index.value());
240 sat_solver_->AddBinaryClause(literal.Negated(),
Literal(previous));
242 previous = literal.Index();
250 if (!positive) i_lit = i_lit.
Negated();
252 const IntegerVariable var(i_lit.
var);
254 IntegerValue after(i_lit.
bound);
255 IntegerValue before(i_lit.
bound - 1);
256 DCHECK_GE(before, domains_[index].Min());
257 DCHECK_LE(after, domains_[index].Max());
258 int64_t previous = std::numeric_limits<int64_t>::min();
260 if (before > previous && before < interval.start) before = previous;
261 if (after > previous && after < interval.start) after = interval.start;
262 if (after <= interval.end)
break;
263 previous = interval.end;
282 const IntegerValue bound = -i_lit.
bound;
298 ++num_created_variables_;
299 const Literal literal(sat_solver_->NewBooleanVariable(),
true);
304 if (sat_solver_->Assignment().LiteralIsAssigned(literal)) {
305 VLOG(1) <<
"Created a fixed literal for no reason!";
311std::pair<PositiveOnlyIndex, IntegerValue> PositiveVarKey(IntegerVariable var,
312 IntegerValue value) {
319 IntegerVariable var, IntegerValue value)
const {
321 equality_to_associated_literal_.find(PositiveVarKey(var, value));
322 if (it != equality_to_associated_literal_.end()) {
323 return it->second.Index();
329 IntegerVariable var, IntegerValue value) {
332 equality_to_associated_literal_.find(PositiveVarKey(var, value));
333 if (it != equality_to_associated_literal_.end()) {
350 ++num_created_variables_;
351 const Literal literal(sat_solver_->NewBooleanVariable(),
true);
359 if (sat_solver_->Assignment().LiteralIsAssigned(literal)) {
360 VLOG(1) <<
"Created a fixed literal for no reason!";
374 const Domain& domain = domains_[index];
375 const IntegerValue min(domain.
Min());
376 const IntegerValue max(domain.
Max());
377 if (i_lit.
bound <= min) {
378 return (
void)sat_solver_->AddUnitClause(literal);
380 if (i_lit.
bound > max) {
381 return (
void)sat_solver_->AddUnitClause(literal.
Negated());
384 if (index >= encoding_by_var_.size()) {
385 encoding_by_var_.resize(index.value() + 1);
387 auto& var_encoding = encoding_by_var_[index];
392 const auto [it, inserted] =
393 var_encoding.insert({canonical_pair.first.bound, literal});
395 const Literal associated(it->second);
396 if (associated != literal) {
397 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
398 sat_solver_->AddClauseDuringSearch({literal, associated.
Negated()});
399 sat_solver_->AddClauseDuringSearch({literal.
Negated(), associated});
403 AddImplications(var_encoding, it, literal);
406 if (sat_solver_->CurrentDecisionLevel() == 0) {
407 if (sat_solver_->Assignment().LiteralIsTrue(literal)) {
408 delayed_to_fix_->integer_literal_to_fix.push_back(canonical_pair.first);
410 if (sat_solver_->Assignment().LiteralIsFalse(literal)) {
411 delayed_to_fix_->integer_literal_to_fix.push_back(canonical_pair.second);
418 if (new_size > reverse_encoding_.size()) {
419 reverse_encoding_.resize(new_size);
421 reverse_encoding_[literal].push_back(canonical_pair.first);
422 reverse_encoding_[literal.
NegatedIndex()].push_back(canonical_pair.second);
427 if (canonical_pair.first.bound == max) {
430 if (-canonical_pair.second.bound == min) {
437 IntegerValue value) {
448 const Domain& domain = domains_[index];
449 if (value == 1 && domain.
Min() >= 0 && domain.
Max() <= 1) {
450 if (literal.
Index() >= literal_view_.size()) {
452 literal_view_[literal] = var;
454 literal_view_[literal] = var;
457 if (value == -1 && domain.
Min() >= -1 && domain.
Max() <= 0) {
458 if (literal.
Index() >= literal_view_.size()) {
468 const auto insert_result = equality_to_associated_literal_.insert(
469 {PositiveVarKey(var, value), literal});
470 if (!insert_result.second) {
472 const Literal representative = insert_result.first->second;
473 if (representative != literal) {
474 sat_solver_->AddClauseDuringSearch({literal, representative.
Negated()});
475 sat_solver_->AddClauseDuringSearch({literal.
Negated(), representative});
481 if (!domain.
Contains(value.value())) {
482 return (
void)sat_solver_->AddUnitClause(literal.
Negated());
488 if (index >= equality_by_var_.size()) {
489 equality_by_var_.resize(index.value() + 1);
490 is_fully_encoded_.resize(index.value() + 1);
492 equality_by_var_[index].push_back({value, literal});
496 return (
void)sat_solver_->AddUnitClause(literal);
503 if (value == domain.
Min()) {
512 if (value == domain.
Max()) {
520 sat_solver_->AddClauseDuringSearch({a, literal.
Negated()});
521 sat_solver_->AddClauseDuringSearch({
b, literal.
Negated()});
522 sat_solver_->AddClauseDuringSearch({a.
Negated(),
b.Negated(), literal});
525 const int new_size = 1 + literal.
Index().value();
526 if (new_size > reverse_equality_encoding_.size()) {
527 reverse_equality_encoding_.resize(new_size);
529 reverse_equality_encoding_[literal].push_back({var, value});
535 if (i_lit.
bound <= domains_[index].Min())
return true;
536 if (i_lit.
bound > domains_[index].Max())
return true;
544 const LiteralIndex result =
559 const auto& encoding = encoding_by_var_[index];
564 auto after_it = encoding.upper_bound(i_lit.
bound);
567 *bound = after_it->first;
568 return after_it->second.Index();
572 auto after_it = encoding.upper_bound(-i_lit.
bound);
576 const Domain& domain = domains_[index];
579 return after_it->second.NegatedIndex();
584 Literal lit, IntegerVariable* view,
bool* view_is_direct)
const {
591 if (view !=
nullptr) *view = direct_var;
592 if (view_is_direct !=
nullptr) *view_is_direct =
true;
596 if (view !=
nullptr) *view = opposite_var;
597 if (view_is_direct !=
nullptr) *view_is_direct =
false;
604 IntegerVariable var)
const {
605 std::vector<ValueLiteralPair> result;
607 if (index >= encoding_by_var_.size())
return result;
609 for (
const auto [value, literal] : encoding_by_var_[index]) {
610 result.push_back({value, literal});
616 const Domain& domain = domains_[index];
617 if (domain.
IsEmpty())
return result;
621 for (
const auto [value, literal] : encoding_by_var_[index]) {
622 while (value > domain[
i].end) {
623 previous = domain[
i].
end;
625 if (
i == num_intervals)
break;
627 if (
i == num_intervals)
break;
628 if (value <= domain[
i].start) {
629 if (
i == 0)
continue;
630 result.push_back({-previous, literal.Negated()});
632 result.push_back({-value + 1, literal.Negated()});
635 std::reverse(result.begin(), result.end());
643 if (index >= encoding_by_var_.size())
return true;
649 tmp_encoding_.clear();
650 for (
const auto [value, literal] : encoding_by_var_[index]) {
654 if (trail_->Assignment().LiteralIsTrue(literal))
return false;
655 if (trail_->Assignment().LiteralIsFalse(literal))
continue;
657 trail_->EnqueueWithUnitReason(literal.Negated());
660 if (
i == 0 && value <= domain[0].start) {
662 if (trail_->Assignment().LiteralIsTrue(literal))
continue;
663 if (trail_->Assignment().LiteralIsFalse(literal))
return false;
665 trail_->EnqueueWithUnitReason(literal);
670 tmp_encoding_.push_back(
671 {std::max<IntegerValue>(value, domain[
i].start), literal});
673 encoding_by_var_[index].clear();
674 for (
const auto [value, literal] : tmp_encoding_) {
675 encoding_by_var_[index].insert({value, literal});
684 if (trail_->Assignment().LiteralIsTrue(pair.literal))
return false;
685 if (trail_->Assignment().LiteralIsFalse(pair.literal))
continue;
687 trail_->EnqueueWithUnitReason(pair.literal.Negated());
692 VLOG(1) <<
"Domain intersection fixed " << num_fixed
693 <<
" encoding literals";
700 if (parameters_.log_search_progress() && num_decisions_to_break_loop_ > 0) {
701 VLOG(1) <<
"Num decisions to break propagation loop: "
702 << num_decisions_to_break_loop_;
713 if (level > integer_search_levels_.size()) {
714 integer_search_levels_.push_back(integer_trail_.size());
715 lazy_reason_decision_levels_.push_back(lazy_reasons_.size());
716 reason_decision_levels_.push_back(literals_reason_starts_.size());
717 CHECK_EQ(level, integer_search_levels_.size());
729 for (
const IntegerLiteral i_lit : delayed_to_fix_->integer_literal_to_fix) {
733 const IntegerValue lb =
737 sat_solver_->NotifyThatModelIsUnsat();
741 delayed_to_fix_->integer_literal_to_fix.clear();
743 for (
const Literal lit : delayed_to_fix_->literal_to_fix) {
744 if (trail_->Assignment().LiteralIsFalse(lit)) {
745 sat_solver_->NotifyThatModelIsUnsat();
748 if (trail_->Assignment().LiteralIsTrue(lit))
continue;
749 trail_->EnqueueWithUnitReason(lit);
751 delayed_to_fix_->literal_to_fix.clear();
758 for (
const IntegerLiteral i_lit : encoder_->GetIntegerLiterals(literal)) {
760 if (!EnqueueAssociatedIntegerLiteral(i_lit, literal)) {
771 conditional_lbs_.clear();
776 if (level < first_level_without_full_propagation_) {
777 first_level_without_full_propagation_ = -1;
782 if (level >= integer_search_levels_.size())
return;
783 const int target = integer_search_levels_[level];
784 integer_search_levels_.resize(level);
785 CHECK_GE(target, var_lbs_.size());
786 CHECK_LE(target, integer_trail_.size());
788 for (
int index = integer_trail_.size() - 1; index >= target; --index) {
789 const TrailEntry& entry = integer_trail_[index];
790 if (entry.var < 0)
continue;
791 var_trail_index_[entry.var] = entry.prev_trail_index;
792 var_lbs_[entry.var] = integer_trail_[entry.prev_trail_index].bound;
794 integer_trail_.resize(target);
797 lazy_reasons_.resize(lazy_reason_decision_levels_[level]);
798 lazy_reason_decision_levels_.resize(level);
801 const int old_size = reason_decision_levels_[level];
802 reason_decision_levels_.resize(level);
803 if (old_size < literals_reason_starts_.size()) {
804 literals_reason_buffer_.resize(literals_reason_starts_[old_size]);
806 const int bound_start = bounds_reason_starts_[old_size];
807 bounds_reason_buffer_.resize(bound_start);
808 if (bound_start < trail_index_reason_buffer_.size()) {
809 trail_index_reason_buffer_.resize(bound_start);
812 literals_reason_starts_.resize(old_size);
813 bounds_reason_starts_.resize(old_size);
814 cached_sizes_.resize(old_size);
824 domains_->reserve(num_vars);
825 encoder_->ReserveSpaceForNumVariables(num_vars);
828 const int size = 2 * num_vars;
829 var_lbs_.reserve(size);
830 var_trail_index_.reserve(size);
831 integer_trail_.reserve(size);
832 var_trail_index_cache_.reserve(size);
833 tmp_var_to_trail_index_in_queue_.reserve(size);
835 var_to_trail_index_at_lower_level_.reserve(size);
839 IntegerValue upper_bound) {
841 DCHECK_LE(lower_bound, upper_bound);
843 DCHECK(lower_bound >= 0 ||
844 lower_bound + std::numeric_limits<int64_t>::max() >= upper_bound);
845 DCHECK(integer_search_levels_.empty());
846 DCHECK_EQ(var_lbs_.size(), integer_trail_.size());
848 const IntegerVariable
i(var_lbs_.size());
849 var_lbs_.push_back(lower_bound);
850 var_trail_index_.push_back(integer_trail_.size());
851 integer_trail_.push_back({lower_bound,
i});
852 domains_->push_back(
Domain(lower_bound.value(), upper_bound.value()));
855 var_lbs_.push_back(-upper_bound);
856 var_trail_index_.push_back(integer_trail_.size());
857 integer_trail_.push_back({-upper_bound,
NegationOf(
i)});
859 var_trail_index_cache_.resize(var_lbs_.size(), integer_trail_.size());
860 tmp_var_to_trail_index_in_queue_.resize(var_lbs_.size(), 0);
861 var_to_trail_index_at_lower_level_.resize(var_lbs_.size(), 0);
872 IntegerValue(domain.
Max()));
880 temp_domain_ = (*domains_)[index].Negation();
888 CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
895 const Domain& old_domain = (*domains_)[index];
897 if (old_domain == domain)
return true;
899 if (domain.
IsEmpty())
return false;
900 const bool lb_changed = domain.
Min() > old_domain.
Min();
901 const bool ub_changed = domain.
Max() < old_domain.
Max();
902 (*domains_)[index] = domain;
911 var_lbs_[var] = domain.
Min();
912 integer_trail_[var.value()].bound = domain.
Min();
914 integer_trail_[
NegationOf(var).value()].bound = -domain.
Max();
918 if (lb_changed) bitset->Set(var);
923 return encoder_->UpdateEncodingOnInitialDomainChange(var, domain);
927 IntegerValue value) {
931 insert.first->second = new_var;
934 CHECK(constant_map_.emplace(-value,
NegationOf(new_var)).second);
938 return insert.first->second;
944 return (constant_map_.size() + 1) / 2;
948 int threshold)
const {
956 const int index_in_queue = tmp_var_to_trail_index_in_queue_[var];
957 if (threshold <= index_in_queue) {
960 const int last_decision_index =
961 integer_search_levels_.empty() ? 0 : integer_search_levels_.back();
962 if (index_in_queue >= last_decision_index) {
963 info_is_valid_on_subsequent_last_level_expansion_ =
false;
968 DCHECK_GE(threshold, var_lbs_.size());
969 int trail_index = var_trail_index_[var];
972 if (trail_index > threshold) {
973 const int cached_index = var_trail_index_cache_[var];
974 if (cached_index >= threshold && cached_index < trail_index &&
975 integer_trail_[cached_index].var == var) {
976 trail_index = cached_index;
980 while (trail_index >= threshold) {
981 trail_index = integer_trail_[trail_index].prev_trail_index;
982 if (trail_index >= var_trail_index_cache_threshold_) {
983 var_trail_index_cache_[var] = trail_index;
987 const int num_vars = var_lbs_.size();
988 return trail_index < num_vars ? -1 : trail_index;
991int IntegerTrail::FindLowestTrailIndexThatExplainBound(
993 DCHECK_LE(i_lit.
bound, var_lbs_[i_lit.
var]);
995 int trail_index = var_trail_index_[i_lit.
var];
1003 const int cached_index = var_trail_index_cache_[i_lit.
var];
1004 if (cached_index < trail_index) {
1005 const TrailEntry& entry = integer_trail_[cached_index];
1006 if (entry.var == i_lit.
var && entry.bound >= i_lit.
bound) {
1007 trail_index = cached_index;
1012 int prev_trail_index = trail_index;
1014 if (trail_index >= var_trail_index_cache_threshold_) {
1015 var_trail_index_cache_[i_lit.
var] = trail_index;
1017 const TrailEntry& entry = integer_trail_[trail_index];
1018 if (entry.bound == i_lit.
bound)
return trail_index;
1019 if (entry.bound < i_lit.
bound)
return prev_trail_index;
1020 prev_trail_index = trail_index;
1021 trail_index = entry.prev_trail_index;
1027 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
1028 std::vector<IntegerLiteral>* reason)
const {
1030 if (slack == 0)
return;
1031 const int size = reason->size();
1032 tmp_indices_.resize(size);
1033 for (
int i = 0;
i < size; ++
i) {
1034 CHECK_EQ((*reason)[
i].bound,
LowerBound((*reason)[
i].var));
1035 CHECK_GE(coeffs[
i], 0);
1036 tmp_indices_[
i] = var_trail_index_[(*reason)[
i].var];
1042 for (
const int i : tmp_indices_) {
1044 integer_trail_[
i].bound));
1049 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
1050 absl::Span<const IntegerVariable> vars,
1051 std::vector<IntegerLiteral>* reason)
const {
1052 tmp_indices_.clear();
1053 for (
const IntegerVariable var : vars) {
1054 tmp_indices_.push_back(var_trail_index_[var]);
1057 for (
const int i : tmp_indices_) {
1059 integer_trail_[
i].bound));
1064 absl::Span<const IntegerValue> coeffs,
1065 std::vector<int>* trail_indices)
const {
1066 DCHECK_GT(slack, 0);
1067 DCHECK(relax_heap_.empty());
1074 const int size = coeffs.size();
1075 const int num_vars = var_lbs_.size();
1076 for (
int i = 0;
i < size; ++
i) {
1077 const int index = (*trail_indices)[
i];
1080 if (index < num_vars)
continue;
1083 const IntegerValue coeff = coeffs[
i];
1084 if (coeff > slack) {
1085 (*trail_indices)[new_size++] = index;
1092 const TrailEntry& entry = integer_trail_[index];
1094 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
1095 (*trail_indices)[new_size++] = index;
1100 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
1101 const int64_t diff =
1102 CapProd(coeff.value(), (entry.bound - previous_entry.bound).value());
1104 (*trail_indices)[new_size++] = index;
1108 relax_heap_.push_back({index, coeff, diff});
1110 trail_indices->resize(new_size);
1111 std::make_heap(relax_heap_.begin(), relax_heap_.end());
1113 while (slack > 0 && !relax_heap_.empty()) {
1114 const RelaxHeapEntry heap_entry = relax_heap_.front();
1115 std::pop_heap(relax_heap_.begin(), relax_heap_.end());
1116 relax_heap_.pop_back();
1119 if (heap_entry.diff > slack) {
1120 trail_indices->push_back(heap_entry.index);
1125 slack -= heap_entry.diff;
1126 const int index = integer_trail_[heap_entry.index].prev_trail_index;
1129 if (index < num_vars)
continue;
1130 if (heap_entry.coeff > slack) {
1131 trail_indices->push_back(index);
1134 const TrailEntry& entry = integer_trail_[index];
1136 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
1137 trail_indices->push_back(index);
1141 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
1142 const int64_t diff =
CapProd(heap_entry.coeff.value(),
1143 (entry.bound - previous_entry.bound).value());
1145 trail_indices->push_back(index);
1148 relax_heap_.push_back({index, heap_entry.coeff, diff});
1149 std::push_heap(relax_heap_.begin(), relax_heap_.end());
1154 for (
const RelaxHeapEntry& entry : relax_heap_) {
1155 trail_indices->push_back(entry.index);
1157 relax_heap_.clear();
1161 std::vector<IntegerLiteral>* reason)
const {
1165 (*reason)[new_size++] = literal;
1167 reason->resize(new_size);
1170std::vector<Literal>* IntegerTrail::InitializeConflict(
1172 absl::Span<const Literal> literals_reason,
1173 absl::Span<const IntegerLiteral> bounds_reason) {
1174 DCHECK(tmp_queue_.empty());
1176 if (use_lazy_reason) {
1179 lazy_reasons_.back().Explain(conflict, &tmp_queue_);
1181 conflict->assign(literals_reason.begin(), literals_reason.end());
1182 const int num_vars = var_lbs_.size();
1183 for (
const IntegerLiteral& literal : bounds_reason) {
1184 const int trail_index = FindLowestTrailIndexThatExplainBound(literal);
1185 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
1193std::string ReasonDebugString(absl::Span<const Literal> literal_reason,
1194 absl::Span<const IntegerLiteral> integer_reason) {
1195 std::string result =
"literals:{";
1196 for (
const Literal l : literal_reason) {
1197 if (result.back() !=
'{') result +=
",";
1198 result += l.DebugString();
1200 result +=
"} bounds:{";
1202 if (result.back() !=
'{') result +=
",";
1203 result += l.DebugString();
1211std::string IntegerTrail::DebugString() {
1212 std::string result =
"trail:{";
1213 const int num_vars = var_lbs_.size();
1215 std::min(num_vars + 30,
static_cast<int>(integer_trail_.size()));
1216 for (
int i = num_vars;
i < limit; ++
i) {
1217 if (result.back() !=
'{') result +=
",";
1220 integer_trail_[
i].bound)
1223 if (limit < integer_trail_.size()) {
1231 DCHECK(ReasonIsValid(i_lit, {}, {}));
1234 sat_solver_->NotifyThatModelIsUnsat();
1237 if (trail_->CurrentDecisionLevel() == 0) {
1238 if (!
Enqueue(i_lit, {}, {})) {
1239 sat_solver_->NotifyThatModelIsUnsat();
1248 integer_trail_[i_lit.
var.value()].bound = i_lit.
bound;
1249 delayed_to_fix_->integer_literal_to_fix.push_back(i_lit);
1254 IntegerLiteral i_lit, absl::Span<const IntegerLiteral> integer_reason) {
1261 tmp_cleaned_reason_.clear();
1263 DCHECK(!lit.IsAlwaysFalse());
1264 if (lit.IsAlwaysTrue())
continue;
1265 tmp_cleaned_reason_.push_back(lit);
1267 return Enqueue(i_lit, {}, tmp_cleaned_reason_);
1272 std::vector<IntegerLiteral>* integer_reason) {
1277 literal_reason->push_back(lit.
Negated());
1278 return Enqueue(i_lit, *literal_reason, *integer_reason);
1282 integer_reason->push_back(
1298 const auto [it, inserted] =
1299 conditional_lbs_.insert({{lit.
Index(), i_lit.
var}, i_lit.
bound});
1301 it->second = std::max(it->second, i_lit.
bound);
1307bool IntegerTrail::ReasonIsValid(
1308 absl::Span<const Literal> literal_reason,
1309 absl::Span<const IntegerLiteral> integer_reason) {
1311 for (
const Literal lit : literal_reason) {
1314 for (
const IntegerLiteral i_lit : integer_reason) {
1317 LOG(INFO) <<
"Reason has a constant false literal!";
1320 if (i_lit.
bound > var_lbs_[i_lit.
var]) {
1321 LOG(INFO) <<
"Reason " << i_lit <<
" is not true!"
1322 <<
" non-optional variable:" << i_lit.
var
1323 <<
" current_lb:" << var_lbs_[i_lit.
var];
1330 if (!integer_search_levels_.empty()) {
1331 int num_literal_assigned_after_root_node = 0;
1332 for (
const Literal lit : literal_reason) {
1333 if (trail_->Info(lit.Variable()).level > 0) {
1334 num_literal_assigned_after_root_node++;
1337 for (
const IntegerLiteral i_lit : integer_reason) {
1340 num_literal_assigned_after_root_node++;
1343 if (num_literal_assigned_after_root_node == 0) {
1344 VLOG(2) <<
"Propagating a literal with no reason at a positive level!\n"
1345 <<
"level:" << integer_search_levels_.size() <<
" "
1346 << ReasonDebugString(literal_reason, integer_reason) <<
"\n"
1354bool IntegerTrail::ReasonIsValid(
1356 absl::Span<const IntegerLiteral> integer_reason) {
1357 if (!ReasonIsValid(literal_reason, integer_reason))
return false;
1358 if (debug_checker_ ==
nullptr)
return true;
1360 std::vector<Literal> clause;
1361 clause.assign(literal_reason.begin(), literal_reason.end());
1362 std::vector<IntegerLiteral> lits;
1363 lits.assign(integer_reason.begin(), integer_reason.end());
1365 if (!debug_checker_(clause, {i_lit})) {
1366 LOG(INFO) <<
"Invalid reason for loaded solution: " << i_lit <<
" "
1367 << literal_reason <<
" " << integer_reason;
1373bool IntegerTrail::ReasonIsValid(
1374 Literal lit, absl::Span<const Literal> literal_reason,
1375 absl::Span<const IntegerLiteral> integer_reason) {
1376 if (!ReasonIsValid(literal_reason, integer_reason))
return false;
1377 if (debug_checker_ ==
nullptr)
return true;
1379 std::vector<Literal> clause;
1380 clause.assign(literal_reason.begin(), literal_reason.end());
1381 clause.push_back(lit);
1382 std::vector<IntegerLiteral> lits;
1383 lits.assign(integer_reason.begin(), integer_reason.end());
1385 if (!debug_checker_(clause, {})) {
1386 LOG(INFO) <<
"Invalid reason for loaded solution: " << lit <<
" "
1387 << literal_reason <<
" " << integer_reason;
1394 Literal literal, absl::Span<const Literal> literal_reason,
1395 absl::Span<const IntegerLiteral> integer_reason) {
1396 EnqueueLiteralInternal(literal,
false, literal_reason, integer_reason);
1399void IntegerTrail::EnqueueLiteralInternal(
1400 Literal literal,
bool use_lazy_reason,
1401 absl::Span<const Literal> literal_reason,
1402 absl::Span<const IntegerLiteral> integer_reason) {
1404 DCHECK(!use_lazy_reason ||
1405 ReasonIsValid(literal, literal_reason, integer_reason));
1406 if (integer_search_levels_.empty()) {
1413 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1414 literal_reason.empty() && !use_lazy_reason) {
1418 const int trail_index = trail_->
Index();
1419 if (trail_index >= boolean_trail_index_to_reason_index_.size()) {
1420 boolean_trail_index_to_reason_index_.resize(trail_index + 1);
1423 const int reason_index =
1425 ? -
static_cast<int>(lazy_reasons_.size())
1426 : AppendReasonToInternalBuffers(literal_reason, integer_reason);
1427 boolean_trail_index_to_reason_index_[trail_index] = reason_index;
1436 if (parameters_.propagation_loop_detection_factor() == 0.0)
return false;
1438 !integer_search_levels_.empty() &&
1439 integer_trail_.size() - integer_search_levels_.back() >
1440 std::max(10000.0, parameters_.propagation_loop_detection_factor() *
1441 static_cast<double>(var_lbs_.size())) &&
1442 parameters_.search_branching() != SatParameters::FIXED_SEARCH);
1446 if (first_level_without_full_propagation_ == -1) {
1447 first_level_without_full_propagation_ = trail_->CurrentDecisionLevel();
1455 ++num_decisions_to_break_loop_;
1456 std::vector<IntegerVariable> vars;
1457 for (
int i = integer_search_levels_.back();
i < integer_trail_.size(); ++
i) {
1458 const IntegerVariable var = integer_trail_[
i].var;
1461 vars.push_back(var);
1464 std::sort(vars.begin(), vars.end());
1465 IntegerVariable best_var = vars[0];
1468 for (
int i = 1;
i < vars.size(); ++
i) {
1469 if (vars[
i] != vars[
i - 1]) {
1473 if (count > best_count) {
1483 return first_level_without_full_propagation_ != -1;
1487 for (IntegerVariable var(0); var < var_lbs_.size(); var += 2) {
1488 if (!
IsFixed(var))
return var;
1493void IntegerTrail::CanonicalizeLiteralIfNeeded(
IntegerLiteral* i_lit) {
1495 const Domain& domain = (*domains_)[index];
1504int IntegerTrail::AppendReasonToInternalBuffers(
1505 absl::Span<const Literal> literal_reason,
1506 absl::Span<const IntegerLiteral> integer_reason) {
1507 const int reason_index = literals_reason_starts_.size();
1508 DCHECK_EQ(reason_index, bounds_reason_starts_.size());
1509 DCHECK_EQ(reason_index, cached_sizes_.size());
1511 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1512 if (!literal_reason.empty()) {
1513 literals_reason_buffer_.insert(literals_reason_buffer_.end(),
1514 literal_reason.begin(),
1515 literal_reason.end());
1518 cached_sizes_.push_back(-1);
1519 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1520 if (!integer_reason.empty()) {
1521 bounds_reason_buffer_.insert(bounds_reason_buffer_.end(),
1522 integer_reason.begin(), integer_reason.end());
1525 return reason_index;
1528int64_t IntegerTrail::NextConflictId() {
1529 return sat_solver_->num_failures() + 1;
1532bool IntegerTrail::EnqueueInternal(
1534 absl::Span<const Literal> literal_reason,
1535 absl::Span<const IntegerLiteral> integer_reason,
1536 int trail_index_with_same_reason) {
1537 DCHECK(use_lazy_reason ||
1538 ReasonIsValid(i_lit, literal_reason, integer_reason));
1539 const IntegerVariable var(i_lit.var);
1544 if (i_lit.bound <= var_lbs_[var])
return true;
1553 CanonicalizeLiteralIfNeeded(&i_lit);
1562 auto* conflict = InitializeConflict(i_lit, use_lazy_reason, literal_reason,
1565 const int trail_index = FindLowestTrailIndexThatExplainBound(ub_reason);
1566 if (trail_index >= 0) tmp_queue_.push_back(trail_index);
1568 MergeReasonIntoInternal(conflict, NextConflictId());
1584 const IntegerValue lb =
LowerBound(i_lit.var);
1585 const IntegerValue ub =
UpperBound(i_lit.var);
1586 if (i_lit.bound - lb < (ub - lb) / 2) {
1587 if (first_level_without_full_propagation_ == -1) {
1588 first_level_without_full_propagation_ = trail_->CurrentDecisionLevel();
1595 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1596 bitset->Set(i_lit.var);
1612 const LiteralIndex literal_index =
1613 encoder_->SearchForLiteralAtOrBefore(i_lit, &bound);
1614 int bool_index = -1;
1616 const Literal to_enqueue = Literal(literal_index);
1617 if (trail_->Assignment().LiteralIsFalse(to_enqueue)) {
1618 auto* conflict = InitializeConflict(i_lit, use_lazy_reason,
1619 literal_reason, integer_reason);
1620 conflict->push_back(to_enqueue);
1621 MergeReasonIntoInternal(conflict, NextConflictId());
1629 if (bound >= i_lit.bound) {
1630 DCHECK_EQ(bound, i_lit.bound);
1631 if (!trail_->Assignment().LiteralIsTrue(to_enqueue)) {
1632 EnqueueLiteralInternal(to_enqueue, use_lazy_reason, literal_reason,
1635 return EnqueueAssociatedIntegerLiteral(i_lit, to_enqueue);
1638 if (!trail_->Assignment().LiteralIsTrue(to_enqueue)) {
1639 if (integer_search_levels_.empty()) {
1640 trail_->EnqueueWithUnitReason(to_enqueue);
1645 bool_index = trail_->Index();
1652 if (integer_search_levels_.empty()) {
1653 ++num_level_zero_enqueues_;
1654 var_lbs_[i_lit.var] = i_lit.bound;
1655 integer_trail_[i_lit.var.value()].bound = i_lit.bound;
1659 trail_->MutableConflict()->clear();
1664 DCHECK_GT(trail_->CurrentDecisionLevel(), 0);
1668 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1669 literal_reason.empty() && !use_lazy_reason) {
1674 if (use_lazy_reason) {
1675 reason_index = -
static_cast<int>(lazy_reasons_.size());
1676 }
else if (trail_index_with_same_reason >= integer_trail_.size()) {
1678 AppendReasonToInternalBuffers(literal_reason, integer_reason);
1680 reason_index = integer_trail_[trail_index_with_same_reason].reason_index;
1682 if (bool_index >= 0) {
1683 if (bool_index >= boolean_trail_index_to_reason_index_.size()) {
1684 boolean_trail_index_to_reason_index_.resize(bool_index + 1);
1686 boolean_trail_index_to_reason_index_[bool_index] = reason_index;
1689 const int prev_trail_index = var_trail_index_[i_lit.var];
1690 var_lbs_[i_lit.var] = i_lit.bound;
1691 var_trail_index_[i_lit.var] = integer_trail_.size();
1692 integer_trail_.push_back({i_lit.bound,
1700bool IntegerTrail::EnqueueAssociatedIntegerLiteral(
IntegerLiteral i_lit,
1702 DCHECK(ReasonIsValid(i_lit, {literal_reason.Negated()}, {}));
1705 if (i_lit.bound <= var_lbs_[i_lit.var])
return true;
1709 CanonicalizeLiteralIfNeeded(&i_lit);
1716 return Enqueue(i_lit, {literal_reason.Negated()}, {});
1720 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1721 bitset->Set(i_lit.var);
1725 if (integer_search_levels_.empty()) {
1726 var_lbs_[i_lit.var] = i_lit.bound;
1727 integer_trail_[i_lit.var.value()].bound = i_lit.bound;
1731 trail_->MutableConflict()->clear();
1736 DCHECK_GT(trail_->CurrentDecisionLevel(), 0);
1738 const int reason_index =
1739 AppendReasonToInternalBuffers({literal_reason.Negated()}, {});
1740 const int prev_trail_index = var_trail_index_[i_lit.var];
1741 var_lbs_[i_lit.var] = i_lit.bound;
1742 var_trail_index_[i_lit.var] = integer_trail_.size();
1743 integer_trail_.push_back({i_lit.bound,
1751void IntegerTrail::ComputeLazyReasonIfNeeded(
int reason_index)
const {
1752 if (reason_index < 0) {
1753 lazy_reasons_[-reason_index - 1].Explain(&lazy_reason_literals_,
1754 &lazy_reason_trail_indices_);
1758absl::Span<const int> IntegerTrail::Dependencies(
int reason_index)
const {
1759 if (reason_index < 0) {
1760 return absl::Span<const int>(lazy_reason_trail_indices_);
1763 const int cached_size = cached_sizes_[reason_index];
1764 if (cached_size == 0)
return {};
1766 const int start = bounds_reason_starts_[reason_index];
1767 if (cached_size > 0) {
1768 return absl::MakeSpan(&trail_index_reason_buffer_[start], cached_size);
1772 DCHECK_EQ(cached_size, -1);
1773 const int end = reason_index + 1 < bounds_reason_starts_.size()
1774 ? bounds_reason_starts_[reason_index + 1]
1775 : bounds_reason_buffer_.size();
1776 if (end > trail_index_reason_buffer_.size()) {
1777 trail_index_reason_buffer_.resize(end);
1781 int* data = trail_index_reason_buffer_.data() + start;
1782 const int num_vars = var_lbs_.size();
1783 for (
int i = start;
i < end; ++
i) {
1785 FindLowestTrailIndexThatExplainBound(bounds_reason_buffer_[
i]);
1786 if (dep >= num_vars) {
1787 data[new_size++] = dep;
1790 cached_sizes_[reason_index] = new_size;
1791 if (new_size == 0)
return {};
1792 return absl::MakeSpan(data, new_size);
1795void IntegerTrail::AppendLiteralsReason(
int reason_index,
1796 std::vector<Literal>* output)
const {
1797 if (reason_index < 0) {
1798 for (
const Literal l : lazy_reason_literals_) {
1799 if (!added_variables_[l.Variable()]) {
1800 added_variables_.Set(l.Variable());
1801 output->push_back(l);
1807 const int start = literals_reason_starts_[reason_index];
1808 const int end = reason_index + 1 < literals_reason_starts_.size()
1809 ? literals_reason_starts_[reason_index + 1]
1810 : literals_reason_buffer_.size();
1811 for (
int i = start;
i < end; ++
i) {
1812 const Literal l = literals_reason_buffer_[
i];
1813 if (!added_variables_[l.Variable()]) {
1814 added_variables_.Set(l.Variable());
1815 output->push_back(l);
1821 std::vector<Literal> reason;
1827 std::vector<Literal>* output)
const {
1828 DCHECK(tmp_queue_.empty());
1829 const int num_vars = var_lbs_.size();
1831 if (literal.IsAlwaysTrue())
continue;
1832 const int trail_index = FindLowestTrailIndexThatExplainBound(literal);
1836 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
1838 return MergeReasonIntoInternal(output, -1);
1843void IntegerTrail::MergeReasonIntoInternal(std::vector<Literal>* output,
1844 int64_t conflict_id)
const {
1847 DCHECK(std::all_of(tmp_var_to_trail_index_in_queue_.begin(),
1848 tmp_var_to_trail_index_in_queue_.end(),
1849 [](
int v) { return v == 0; }));
1850 DCHECK(tmp_to_clear_.empty());
1852 info_is_valid_on_subsequent_last_level_expansion_ =
true;
1853 if (conflict_id == -1 || last_conflict_id_ != conflict_id) {
1856 last_conflict_id_ = conflict_id;
1857 for (
const IntegerVariable var : to_clear_for_lower_level_) {
1858 var_to_trail_index_at_lower_level_[var] = 0;
1860 to_clear_for_lower_level_.clear();
1863 const int last_decision_index =
1864 integer_search_levels_.empty() || conflict_id == -1
1866 : integer_search_levels_.back();
1869 for (
const Literal l : *output) {
1870 added_variables_.
Set(l.Variable());
1875 for (
const int trail_index : tmp_queue_) {
1876 DCHECK_GE(trail_index, var_lbs_.size());
1877 DCHECK_LT(trail_index, integer_trail_.size());
1878 const TrailEntry& entry = integer_trail_[trail_index];
1879 tmp_var_to_trail_index_in_queue_[entry.var] =
1880 std::max(tmp_var_to_trail_index_in_queue_[entry.var], trail_index);
1885 std::make_heap(tmp_queue_.begin(), tmp_queue_.end());
1890 int64_t work_done = 0;
1891 while (!tmp_queue_.empty()) {
1893 const int trail_index = tmp_queue_.front();
1894 const TrailEntry& entry = integer_trail_[trail_index];
1895 std::pop_heap(tmp_queue_.begin(), tmp_queue_.end());
1896 tmp_queue_.pop_back();
1901 if (tmp_var_to_trail_index_in_queue_[entry.var] != trail_index) {
1916 tmp_var_to_trail_index_in_queue_[entry.var] = 0;
1917 has_dependency_ =
false;
1921 if (var_to_trail_index_at_lower_level_[entry.var] >= trail_index) {
1930 if (trail_index < last_decision_index) {
1931 tmp_seen_.push_back(trail_index);
1937 var_trail_index_cache_threshold_ = trail_index;
1942 const LiteralIndex associated_lit =
1944 IntegerVariable(entry.var), entry.bound));
1947 const int reason_index = integer_trail_[trail_index].reason_index;
1948 CHECK_GE(reason_index, 0);
1950 const int start = literals_reason_starts_[reason_index];
1951 const int end = reason_index + 1 < literals_reason_starts_.size()
1952 ? literals_reason_starts_[reason_index + 1]
1953 : literals_reason_buffer_.size();
1954 CHECK_EQ(start + 1, end);
1961 CHECK_EQ(literals_reason_buffer_[start],
1962 Literal(associated_lit).Negated());
1966 const int start = bounds_reason_starts_[reason_index];
1967 const int end = reason_index + 1 < bounds_reason_starts_.size()
1968 ? bounds_reason_starts_[reason_index + 1]
1969 : bounds_reason_buffer_.size();
1970 CHECK_EQ(start, end);
1975 ComputeLazyReasonIfNeeded(entry.reason_index);
1976 AppendLiteralsReason(entry.reason_index, output);
1977 const auto dependencies = Dependencies(entry.reason_index);
1978 work_done += dependencies.size();
1979 for (
const int next_trail_index : dependencies) {
1980 DCHECK_LT(next_trail_index, trail_index);
1981 const TrailEntry& next_entry = integer_trail_[next_trail_index];
1987 const int index_in_queue =
1988 tmp_var_to_trail_index_in_queue_[next_entry.var];
1992 if (index_in_queue >= trail_index) {
1995 if (index_in_queue >= last_decision_index) {
1996 info_is_valid_on_subsequent_last_level_expansion_ =
false;
2001 if (next_trail_index <=
2002 var_to_trail_index_at_lower_level_[next_entry.var]) {
2006 has_dependency_ =
true;
2007 if (next_trail_index > index_in_queue) {
2008 tmp_var_to_trail_index_in_queue_[next_entry.var] = next_trail_index;
2009 tmp_queue_.push_back(next_trail_index);
2010 std::push_heap(tmp_queue_.begin(), tmp_queue_.end());
2016 if (!has_dependency_) {
2017 tmp_to_clear_.push_back(entry.var);
2018 tmp_var_to_trail_index_in_queue_[entry.var] = trail_index;
2023 if (info_is_valid_on_subsequent_last_level_expansion_) {
2024 for (
const int trail_index : tmp_seen_) {
2025 if (trail_index == 0)
continue;
2026 const TrailEntry& entry = integer_trail_[trail_index];
2027 const int old = var_to_trail_index_at_lower_level_[entry.var];
2029 to_clear_for_lower_level_.push_back(entry.var);
2031 var_to_trail_index_at_lower_level_[entry.var] =
2032 std::max(old, trail_index);
2038 for (
const IntegerVariable var : tmp_to_clear_) {
2039 tmp_var_to_trail_index_in_queue_[var] = 0;
2041 tmp_to_clear_.clear();
2043 time_limit_->AdvanceDeterministicTime(work_done * 5e-9);
2050 int64_t conflict_id)
const {
2052 added_variables_.ClearAndResize(BooleanVariable(trail_->NumVariables()));
2054 const int reason_index = boolean_trail_index_to_reason_index_[trail_index];
2055 ComputeLazyReasonIfNeeded(reason_index);
2056 AppendLiteralsReason(reason_index, reason);
2057 DCHECK(tmp_queue_.empty());
2058 for (
const int prev_trail_index : Dependencies(reason_index)) {
2059 DCHECK_GE(prev_trail_index, var_lbs_.size());
2060 tmp_queue_.push_back(prev_trail_index);
2062 MergeReasonIntoInternal(reason, conflict_id);
2073 int base_index, std::vector<IntegerLiteral>* output)
const {
2074 tmp_marked_.ClearAndResize(IntegerVariable(var_lbs_.size()));
2077 CHECK_GE(base_index, var_lbs_.size());
2078 for (
int i = integer_trail_.size(); --
i >= base_index;) {
2079 const TrailEntry& entry = integer_trail_[
i];
2081 if (tmp_marked_[entry.var])
continue;
2083 tmp_marked_.Set(entry.var);
2090 time_limit_(model->GetOrCreate<
TimeLimit>()),
2099 integer_trail_->RegisterReversibleClass(
2100 &id_to_greatest_common_level_since_last_call_);
2101 integer_trail_->RegisterWatcher(&modified_vars_);
2102 queue_by_priority_.resize(2);
2106 var_to_watcher_.reserve(2 * num_vars);
2110 if (in_queue_[
id])
return;
2111 in_queue_[id] =
true;
2112 queue_by_priority_[id_to_priority_[id]].push_back(
id);
2115void GenericLiteralWatcher::UpdateCallingNeeds(
Trail* trail) {
2117 const int literal_limit = literal_to_watcher_.size();
2120 if (literal.
Index() >= literal_limit)
continue;
2121 for (
const auto entry : literal_to_watcher_[literal]) {
2123 if (entry.watch_index >= 0) {
2124 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
2130 const int var_limit = var_to_watcher_.size();
2131 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
2132 if (var.value() >= var_limit)
continue;
2133 for (
const auto entry : var_to_watcher_[var]) {
2135 if (entry.watch_index >= 0) {
2136 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
2142 !level_zero_modified_variable_callback_.empty()) {
2143 modified_vars_for_callback_.Resize(modified_vars_.size());
2144 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
2145 modified_vars_for_callback_.Set(var);
2149 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
2157 for (
const int id : propagator_ids_to_call_at_level_zero_) {
2162 UpdateCallingNeeds(trail);
2166 int64_t num_propagate_calls = 0;
2167 const int64_t old_enqueue = integer_trail_->num_enqueues();
2169 ::absl::MakeCleanup([&num_propagate_calls, old_enqueue,
this]() {
2170 const int64_t diff = integer_trail_->num_enqueues() - old_enqueue;
2171 time_limit_->AdvanceDeterministicTime(1e-8 * num_propagate_calls +
2178 for (
int priority = 0; priority < queue_by_priority_.size(); ++priority) {
2186 if (test_limit > 100) {
2188 if (time_limit_->LimitReached())
break;
2190 if (stop_propagation_callback_ !=
nullptr && stop_propagation_callback_()) {
2191 integer_trail_->NotifyThatPropagationWasAborted();
2195 std::deque<int>& queue = queue_by_priority_[priority];
2196 while (!queue.empty()) {
2197 const int id = queue.front();
2203 if (id_need_reversible_support_[
id]) {
2205 id_to_greatest_common_level_since_last_call_[IdType(
id)];
2206 const int high = id_to_level_at_last_call_[id];
2207 if (low < high || level > low) {
2208 id_to_level_at_last_call_[id] = level;
2209 id_to_greatest_common_level_since_last_call_.MutableRef(IdType(
id)) =
2212 if (low < high) rev->SetLevel(low);
2213 if (level > low) rev->SetLevel(level);
2215 for (
int* rev_int : id_to_reversible_ints_[
id]) {
2216 rev_int_repository_->SaveState(rev_int);
2222 const int64_t old_integer_timestamp = integer_trail_->num_enqueues();
2223 const int64_t old_boolean_timestamp = trail->
Index();
2226 ++num_propagate_calls;
2228 id_to_watch_indices_[id].empty()
2229 ? watchers_[id]->Propagate()
2230 : watchers_[id]->IncrementalPropagate(id_to_watch_indices_[
id]);
2232 id_to_watch_indices_[id].clear();
2233 in_queue_[id] =
false;
2239 if (id_to_idempotence_[
id]) {
2243 UpdateCallingNeeds(trail);
2244 id_to_watch_indices_[id].clear();
2245 in_queue_[id] =
false;
2250 id_to_watch_indices_[id].clear();
2251 in_queue_[id] =
false;
2252 UpdateCallingNeeds(trail);
2258 if (trail->
Index() > old_boolean_timestamp) {
2270 if (integer_trail_->num_enqueues() > old_integer_timestamp) {
2280 const std::vector<IntegerVariable>& modified_vars =
2281 modified_vars_for_callback_.PositionsSetAtLeastOnce();
2282 for (
const auto& callback : level_zero_modified_variable_callback_) {
2283 callback(modified_vars);
2285 modified_vars_for_callback_.ClearAndResize(
2286 integer_trail_->NumIntegerVariables());
2297 if (time_limit_->LimitReached())
return;
2307 for (
bool* to_reset : bool_to_reset_on_backtrack_) *to_reset =
false;
2308 bool_to_reset_on_backtrack_.clear();
2311 for (std::deque<int>& queue : queue_by_priority_) {
2312 for (
const int id : queue) {
2313 id_to_watch_indices_[id].clear();
2322 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
2323 in_queue_.assign(watchers_.size(),
false);
2328 const int id = watchers_.size();
2329 watchers_.push_back(propagator);
2331 id_need_reversible_support_.push_back(
false);
2332 id_to_level_at_last_call_.push_back(0);
2333 id_to_greatest_common_level_since_last_call_.GrowByOne();
2334 id_to_reversible_classes_.push_back(std::vector<ReversibleInterface*>());
2335 id_to_reversible_ints_.push_back(std::vector<int*>());
2337 id_to_watch_indices_.push_back(std::vector<int>());
2338 id_to_priority_.push_back(1);
2339 id_to_idempotence_.push_back(
true);
2348 in_queue_.push_back(
true);
2349 queue_by_priority_[1].push_back(
id);
2354 id_to_priority_[id] = priority;
2355 if (priority >= queue_by_priority_.size()) {
2356 queue_by_priority_.resize(priority + 1);
2362 id_to_idempotence_[id] =
false;
2366 propagator_ids_to_call_at_level_zero_.push_back(
id);
2371 id_need_reversible_support_[id] =
true;
2372 id_to_reversible_classes_[id].push_back(rev);
2376 id_need_reversible_support_[id] =
true;
2377 id_to_reversible_ints_[id].push_back(rev);
int64_t ValueAtOrAfter(int64_t input) const
Domain IntersectionWith(const Domain &domain) const
bool Contains(int64_t value) const
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
int64_t ValueAtOrBefore(int64_t input) const
void ClearAndResize(IntegerType size)
void Set(IntegerType index)
bool Propagate(Trail *trail) final
void RegisterReversibleInt(int id, int *rev)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void Untrail(const Trail &trail, int literal_trail_index) final
void SetPropagatorPriority(int id, int priority)
void RegisterReversibleClass(int id, ReversibleInterface *rev)
void ReserveSpaceForNumVariables(int num_vars)
Memory optimization: you can call this before registering watchers.
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
void CallOnNextPropagate(int id)
Add the given propagator to its queue.
GenericLiteralWatcher(Model *model)
void AlwaysCallAtLevelZero(int id)
const std::vector< ValueLiteralPair > & FullDomainEncoding(IntegerVariable var) const
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
void AssociateToIntegerLiteral(Literal literal, IntegerLiteral i_lit)
IntegerVariable GetLiteralView(Literal lit) const
void AddAllImplicationsBetweenAssociatedLiterals()
Literal GetFalseLiteral()
void FullyEncodeVariable(IntegerVariable var)
Literal GetTrueLiteral()
Gets the literal always set to true, make it if it does not exist.
bool VariableIsFullyEncoded(IntegerVariable var) const
bool IsFixedOrHasAssociatedLiteral(IntegerLiteral i_lit) const
const std::vector< ValueLiteralPair > & PartialDomainEncoding(IntegerVariable var) const
LiteralIndex GetAssociatedEqualityLiteral(IntegerVariable var, IntegerValue value) const
LiteralIndex SearchForLiteralAtOrBefore(IntegerLiteral i_lit, IntegerValue *bound) const
void ReserveSpaceForNumVariables(int num_vars)
Memory optimization: you can call this before encoding variables.
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
void AssociateToIntegerEqualValue(Literal literal, IntegerVariable var, IntegerValue value)
ABSL_MUST_USE_RESULT bool LiteralOrNegationHasView(Literal lit, IntegerVariable *view=nullptr, bool *view_is_direct=nullptr) const
std::vector< ValueLiteralPair > PartialGreaterThanEncoding(IntegerVariable var) const
LiteralIndex GetAssociatedLiteral(IntegerLiteral i_lit) const
bool UpdateEncodingOnInitialDomainChange(IntegerVariable var, Domain domain)
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
void NotifyThatPropagationWasAborted()
ABSL_MUST_USE_RESULT bool ConditionalEnqueue(Literal lit, IntegerLiteral i_lit, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
int NumConstantVariables() const
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue UpperBound(IntegerVariable i) const
void AppendNewBoundsFrom(int base_index, std::vector< IntegerLiteral > *output) const
bool IntegerLiteralIsFalse(IntegerLiteral l) const
IntegerVariable AddIntegerVariable()
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
Same as above but take in IntegerVariables instead of IntegerLiterals.
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
bool InPropagationLoop() const
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
bool IsFixed(IntegerVariable i) const
Checks if the variable is fixed.
IntegerVariable NumIntegerVariables() const
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
ABSL_MUST_USE_RESULT bool RootLevelEnqueue(IntegerLiteral i_lit)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
void MergeReasonInto(absl::Span< const IntegerLiteral > literals, std::vector< Literal > *output) const
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
const Domain & InitialVariableDomain(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
void Untrail(const Trail &trail, int literal_trail_index) final
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
IntegerVariable FirstUnassignedVariable() const
void ReserveSpaceForNumVariables(int num_vars)
bool Propagate(Trail *trail) final
IntegerVariable GetOrCreateConstantIntegerVariable(IntegerValue value)
std::vector< Literal > ReasonFor(IntegerLiteral literal) const
bool CurrentBranchHadAnIncompletePropagation()
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
Base class for CP like propagators.
SatPropagator(const std::string &name)
int propagation_trail_index_
bool AddClauseDuringSearch(absl::Span< const Literal > literals)
void AddLastPropagator(SatPropagator *propagator)
int NumVariables() const
Getters.
std::vector< Literal > * MutableConflict()
void EnqueueWithUnitReason(Literal true_literal)
Specific Enqueue() version for a fixed variable.
int CurrentDecisionLevel() const
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
const VariablesAssignment & Assignment() const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Returns the vector of the negated variables.
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
bool VariableIsPositive(IntegerVariable i)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapProd(int64_t x, int64_t y)
trees with all degrees equal w the current value of w
std::vector< Literal > literal_to_fix
bool IsAlwaysFalse() const
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerLiteral Negated() const
The negation of x >= bound is x <= bound - 1.
std::string DebugString() const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
bool IsAlwaysTrue() const
std::string DebugString() const