27#include "absl/base/attributes.h"
28#include "absl/cleanup/cleanup.h"
29#include "absl/container/btree_map.h"
30#include "absl/container/flat_hash_map.h"
31#include "absl/container/inlined_vector.h"
32#include "absl/log/check.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.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;
460 const auto insert_result = equality_to_associated_literal_.insert(
461 {PositiveVarKey(var, value), literal});
462 if (!insert_result.second) {
464 const Literal representative = insert_result.first->second;
465 if (representative != literal) {
466 sat_solver_->AddClauseDuringSearch({literal, representative.
Negated()});
467 sat_solver_->AddClauseDuringSearch({literal.
Negated(), representative});
473 if (!domain.
Contains(value.value())) {
474 return (
void)sat_solver_->AddUnitClause(literal.
Negated());
480 if (index >= equality_by_var_.size()) {
481 equality_by_var_.resize(index.value() + 1);
482 is_fully_encoded_.resize(index.value() + 1);
484 equality_by_var_[index].push_back({value, literal});
488 return (
void)sat_solver_->AddUnitClause(literal);
495 if (value == domain.
Min()) {
504 if (value == domain.
Max()) {
512 sat_solver_->AddClauseDuringSearch({a, literal.
Negated()});
513 sat_solver_->AddClauseDuringSearch({
b, literal.
Negated()});
514 sat_solver_->AddClauseDuringSearch({a.
Negated(),
b.Negated(), literal});
517 const int new_size = 1 + literal.
Index().value();
518 if (new_size > reverse_equality_encoding_.size()) {
519 reverse_equality_encoding_.resize(new_size);
521 reverse_equality_encoding_[literal].push_back({var, value});
527 if (i_lit.
bound <= domains_[index].Min())
return true;
528 if (i_lit.
bound > domains_[index].Max())
return true;
536 const LiteralIndex result =
551 const auto& encoding = encoding_by_var_[index];
556 auto after_it = encoding.upper_bound(i_lit.
bound);
559 *bound = after_it->first;
560 return after_it->second.Index();
564 auto after_it = encoding.upper_bound(-i_lit.
bound);
568 const Domain& domain = domains_[index];
571 return after_it->second.NegatedIndex();
579 const auto& encoding = encoding_by_var_[index];
581 const auto it = encoding.lower_bound(i_lit.
bound);
584 return it->second.Index();
588 auto after_it = encoding.upper_bound(-i_lit.
bound);
593 const Domain& domain = domains_[index];
596 return after_it->second.NegatedIndex();
601 Literal lit, IntegerVariable* view,
bool* view_is_direct)
const {
608 if (view !=
nullptr) *view = direct_var;
609 if (view_is_direct !=
nullptr) *view_is_direct =
true;
613 if (view !=
nullptr) *view = opposite_var;
614 if (view_is_direct !=
nullptr) *view_is_direct =
false;
621 IntegerVariable var)
const {
622 std::vector<ValueLiteralPair> result;
624 if (index >= encoding_by_var_.size())
return result;
626 for (
const auto [value, literal] : encoding_by_var_[index]) {
627 result.push_back({value, literal});
633 const Domain& domain = domains_[index];
634 if (domain.
IsEmpty())
return result;
638 for (
const auto [value, literal] : encoding_by_var_[index]) {
639 while (value > domain[
i].
end) {
640 previous = domain[
i].
end;
642 if (
i == num_intervals)
break;
644 if (
i == num_intervals)
break;
645 if (value <= domain[
i].start) {
646 if (
i == 0)
continue;
647 result.push_back({-previous, literal.Negated()});
649 result.push_back({-value + 1, literal.Negated()});
652 std::reverse(result.begin(), result.end());
660 if (index >= encoding_by_var_.size())
return true;
666 tmp_encoding_.clear();
667 for (
const auto [value, literal] : encoding_by_var_[index]) {
671 if (trail_->Assignment().LiteralIsTrue(literal))
return false;
672 if (trail_->Assignment().LiteralIsFalse(literal))
continue;
674 trail_->EnqueueWithUnitReason(literal.Negated());
677 if (
i == 0 && value <= domain[0].start) {
679 if (trail_->Assignment().LiteralIsTrue(literal))
continue;
680 if (trail_->Assignment().LiteralIsFalse(literal))
return false;
682 trail_->EnqueueWithUnitReason(literal);
687 tmp_encoding_.push_back(
688 {std::max<IntegerValue>(value, domain[
i].start), literal});
690 encoding_by_var_[index].clear();
691 for (
const auto [value, literal] : tmp_encoding_) {
692 encoding_by_var_[index].insert({value, literal});
701 if (trail_->Assignment().LiteralIsTrue(pair.literal))
return false;
702 if (trail_->Assignment().LiteralIsFalse(pair.literal))
continue;
704 trail_->EnqueueWithUnitReason(pair.literal.Negated());
709 VLOG(1) <<
"Domain intersection fixed " << num_fixed
710 <<
" encoding literals";
717 if (parameters_.log_search_progress() && num_decisions_to_break_loop_ > 0) {
718 VLOG(1) <<
"Num decisions to break propagation loop: "
719 << num_decisions_to_break_loop_;
730 if (level > integer_search_levels_.size()) {
731 integer_search_levels_.push_back(integer_trail_.size());
732 lazy_reason_decision_levels_.push_back(lazy_reasons_.size());
733 reason_decision_levels_.push_back(
734 ReasonIndex(literals_reason_starts_.size()));
735 CHECK_EQ(level, integer_search_levels_.size());
747 for (
const IntegerLiteral i_lit : delayed_to_fix_->integer_literal_to_fix) {
751 const IntegerValue lb =
755 sat_solver_->NotifyThatModelIsUnsat();
759 delayed_to_fix_->integer_literal_to_fix.clear();
761 for (
const Literal lit : delayed_to_fix_->literal_to_fix) {
762 if (trail_->Assignment().LiteralIsFalse(lit)) {
763 sat_solver_->NotifyThatModelIsUnsat();
766 if (trail_->Assignment().LiteralIsTrue(lit))
continue;
767 trail_->EnqueueWithUnitReason(lit);
769 delayed_to_fix_->literal_to_fix.clear();
776 for (
const IntegerLiteral i_lit : encoder_->GetIntegerLiterals(literal)) {
778 if (!EnqueueAssociatedIntegerLiteral(i_lit, literal)) {
789 conditional_lbs_.clear();
794 if (level < first_level_without_full_propagation_) {
795 first_level_without_full_propagation_ = -1;
800 if (level >= integer_search_levels_.size())
return;
801 const int target = integer_search_levels_[level];
802 integer_search_levels_.resize(level);
803 CHECK_GE(target, var_lbs_.size());
804 CHECK_LE(target, integer_trail_.size());
806 for (
int index = integer_trail_.size() - 1; index >= target; --index) {
807 const TrailEntry& entry = integer_trail_[index];
808 if (entry.var < 0)
continue;
809 var_trail_index_[entry.var] = entry.prev_trail_index;
810 var_lbs_[entry.var] = integer_trail_[entry.prev_trail_index].bound;
812 integer_trail_.resize(target);
815 lazy_reasons_.resize(lazy_reason_decision_levels_[level]);
816 lazy_reason_decision_levels_.resize(level);
819 const ReasonIndex old_size = reason_decision_levels_[level];
820 reason_decision_levels_.resize(level);
821 if (old_size < literals_reason_starts_.size()) {
822 literals_reason_buffer_.resize(literals_reason_starts_[old_size]);
824 const int bound_start = bounds_reason_starts_[old_size];
825 bounds_reason_buffer_.resize(bound_start);
826 if (bound_start < trail_index_reason_buffer_.size()) {
827 trail_index_reason_buffer_.resize(bound_start);
830 literals_reason_starts_.resize(old_size);
831 bounds_reason_starts_.resize(old_size);
832 cached_sizes_.resize(old_size);
842 domains_->reserve(num_vars);
843 encoder_->ReserveSpaceForNumVariables(num_vars);
846 const int size = 2 * num_vars;
847 var_lbs_.reserve(size);
848 var_trail_index_.reserve(size);
849 integer_trail_.reserve(size);
850 var_trail_index_cache_.reserve(size);
851 tmp_var_to_trail_index_in_queue_.reserve(size);
853 var_to_trail_index_at_lower_level_.reserve(size);
857 IntegerValue upper_bound) {
859 DCHECK_LE(lower_bound, upper_bound);
861 DCHECK(lower_bound >= 0 ||
862 lower_bound + std::numeric_limits<int64_t>::max() >= upper_bound);
863 DCHECK(integer_search_levels_.empty());
864 DCHECK_EQ(var_lbs_.size(), integer_trail_.size());
866 const IntegerVariable
i(var_lbs_.size());
867 var_lbs_.push_back(lower_bound);
868 var_trail_index_.push_back(integer_trail_.size());
869 integer_trail_.push_back({lower_bound,
i});
870 domains_->push_back(
Domain(lower_bound.value(), upper_bound.value()));
873 var_lbs_.push_back(-upper_bound);
874 var_trail_index_.push_back(integer_trail_.size());
875 integer_trail_.push_back({-upper_bound,
NegationOf(
i)});
877 var_trail_index_cache_.resize(var_lbs_.size(), integer_trail_.size());
878 tmp_var_to_trail_index_in_queue_.resize(var_lbs_.size(), 0);
879 var_to_trail_index_at_lower_level_.resize(var_lbs_.size(), 0);
890 IntegerValue(domain.
Max()));
898 temp_domain_ = (*domains_)[index].Negation();
906 CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
913 const Domain& old_domain = (*domains_)[index];
915 if (old_domain == domain)
return true;
917 if (domain.
IsEmpty())
return false;
918 const bool lb_changed = domain.
Min() > old_domain.
Min();
919 const bool ub_changed = domain.
Max() < old_domain.
Max();
920 (*domains_)[index] = domain;
929 var_lbs_[var] = domain.
Min();
930 integer_trail_[var.value()].bound = domain.
Min();
932 integer_trail_[
NegationOf(var).value()].bound = -domain.
Max();
936 if (lb_changed) bitset->Set(var);
941 return encoder_->UpdateEncodingOnInitialDomainChange(var, domain);
945 IntegerValue value) {
949 insert.first->second = new_var;
952 CHECK(constant_map_.emplace(-value,
NegationOf(new_var)).second);
956 return insert.first->second;
962 return (constant_map_.size() + 1) / 2;
966 int threshold)
const {
974 const int index_in_queue = tmp_var_to_trail_index_in_queue_[var];
975 if (threshold <= index_in_queue) {
978 const int last_decision_index =
979 integer_search_levels_.empty() ? 0 : integer_search_levels_.back();
980 if (index_in_queue >= last_decision_index) {
981 info_is_valid_on_subsequent_last_level_expansion_ =
false;
986 DCHECK_GE(threshold, var_lbs_.size());
987 int trail_index = var_trail_index_[var];
990 if (trail_index > threshold) {
991 const int cached_index = var_trail_index_cache_[var];
992 if (cached_index >= threshold && cached_index < trail_index &&
993 integer_trail_[cached_index].var == var) {
994 trail_index = cached_index;
998 while (trail_index >= threshold) {
999 trail_index = integer_trail_[trail_index].prev_trail_index;
1000 if (trail_index >= var_trail_index_cache_threshold_) {
1001 var_trail_index_cache_[var] = trail_index;
1005 const int num_vars = var_lbs_.size();
1006 return trail_index < num_vars ? -1 : trail_index;
1009int IntegerTrail::FindLowestTrailIndexThatExplainBound(
1011 DCHECK_LE(i_lit.
bound, var_lbs_[i_lit.
var]);
1014 int trail_index = var_trail_index_[i_lit.
var];
1022 const int cached_index = var_trail_index_cache_[i_lit.
var];
1023 if (cached_index < trail_index) {
1024 const TrailEntry& entry = integer_trail_[cached_index];
1025 if (entry.var == i_lit.
var && entry.bound >= i_lit.
bound) {
1026 trail_index = cached_index;
1031 int prev_trail_index = trail_index;
1033 ++work_done_in_find_indices_;
1034 if (trail_index >= var_trail_index_cache_threshold_) {
1035 var_trail_index_cache_[i_lit.
var] = trail_index;
1037 const TrailEntry& entry = integer_trail_[trail_index];
1038 if (entry.bound == i_lit.
bound)
return trail_index;
1039 if (entry.bound < i_lit.
bound)
return prev_trail_index;
1040 prev_trail_index = trail_index;
1041 trail_index = entry.prev_trail_index;
1047 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
1048 std::vector<IntegerLiteral>* reason)
const {
1050 if (slack == 0)
return;
1051 const int size = reason->size();
1052 tmp_indices_.resize(size);
1053 for (
int i = 0;
i < size; ++
i) {
1054 CHECK_EQ((*reason)[
i].bound,
LowerBound((*reason)[
i].var));
1055 CHECK_GE(coeffs[
i], 0);
1056 tmp_indices_[
i] = var_trail_index_[(*reason)[
i].var];
1062 for (
const int i : tmp_indices_) {
1064 integer_trail_[
i].bound));
1069 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
1070 absl::Span<const IntegerVariable> vars,
1071 std::vector<IntegerLiteral>* reason)
const {
1072 tmp_indices_.clear();
1073 for (
const IntegerVariable var : vars) {
1074 tmp_indices_.push_back(var_trail_index_[var]);
1077 for (
const int i : tmp_indices_) {
1079 integer_trail_[
i].bound));
1084 absl::Span<const IntegerValue> coeffs,
1085 std::vector<int>* trail_indices)
const {
1086 DCHECK_GT(slack, 0);
1087 DCHECK(relax_heap_.empty());
1094 const int size = coeffs.size();
1095 const int num_vars = var_lbs_.size();
1096 for (
int i = 0;
i < size; ++
i) {
1097 const int index = (*trail_indices)[
i];
1100 if (index < num_vars)
continue;
1103 const IntegerValue coeff = coeffs[
i];
1104 if (coeff > slack) {
1105 (*trail_indices)[new_size++] = index;
1112 const TrailEntry& entry = integer_trail_[index];
1114 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
1115 (*trail_indices)[new_size++] = index;
1120 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
1121 const int64_t diff =
1122 CapProd(coeff.value(), (entry.bound - previous_entry.bound).value());
1124 (*trail_indices)[new_size++] = index;
1128 relax_heap_.push_back({index, coeff, diff});
1130 trail_indices->resize(new_size);
1131 std::make_heap(relax_heap_.begin(), relax_heap_.end());
1133 while (slack > 0 && !relax_heap_.empty()) {
1134 const RelaxHeapEntry heap_entry = relax_heap_.front();
1135 std::pop_heap(relax_heap_.begin(), relax_heap_.end());
1136 relax_heap_.pop_back();
1139 if (heap_entry.diff > slack) {
1140 trail_indices->push_back(heap_entry.index);
1145 slack -= heap_entry.diff;
1146 const int index = integer_trail_[heap_entry.index].prev_trail_index;
1149 if (index < num_vars)
continue;
1150 if (heap_entry.coeff > slack) {
1151 trail_indices->push_back(index);
1154 const TrailEntry& entry = integer_trail_[index];
1156 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
1157 trail_indices->push_back(index);
1161 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
1162 const int64_t diff =
CapProd(heap_entry.coeff.value(),
1163 (entry.bound - previous_entry.bound).value());
1165 trail_indices->push_back(index);
1168 relax_heap_.push_back({index, heap_entry.coeff, diff});
1169 std::push_heap(relax_heap_.begin(), relax_heap_.end());
1174 for (
const RelaxHeapEntry& entry : relax_heap_) {
1175 trail_indices->push_back(entry.index);
1177 relax_heap_.clear();
1181 std::vector<IntegerLiteral>* reason)
const {
1185 (*reason)[new_size++] = literal;
1187 reason->resize(new_size);
1190std::vector<Literal>* IntegerTrail::InitializeConflict(
1192 absl::Span<const Literal> literals_reason,
1193 absl::Span<const IntegerLiteral> bounds_reason) {
1194 DCHECK(tmp_queue_.empty());
1196 if (use_lazy_reason) {
1199 ComputeLazyReasonIfNeeded(-ReasonIndex(lazy_reasons_.size()));
1200 *conflict = lazy_reason_literals_;
1201 tmp_queue_ = lazy_reason_trail_indices_;
1203 conflict->assign(literals_reason.begin(), literals_reason.end());
1204 for (
const IntegerLiteral& literal : bounds_reason) {
1206 tmp_queue_.push_back(FindLowestTrailIndexThatExplainBound(literal));
1210 if (new_conflict_resolution_) {
1211 tmp_reason_.clear();
1212 global_id_conflict_timestamp_ = trail_->conflict_timestamp();
1213 if (use_lazy_reason) {
1218 tmp_boolean_literals_.assign(
1219 tmp_lazy_reason_.boolean_literals_at_false.begin(),
1220 tmp_lazy_reason_.boolean_literals_at_false.end());
1221 for (
const Literal lit : tmp_lazy_reason_.boolean_literals_at_true) {
1222 tmp_boolean_literals_.push_back(lit.Negated());
1224 tmp_integer_literals_.assign(tmp_lazy_reason_.integer_literals.begin(),
1225 tmp_lazy_reason_.integer_literals.end());
1228 tmp_reason_.index_at_propagation = tmp_lazy_reason_.index_at_propagation;
1229 tmp_reason_.slack = tmp_lazy_reason_.slack;
1230 tmp_reason_.propagated_i_lit = tmp_lazy_reason_.propagated_i_lit;
1231 tmp_reason_.vars = tmp_lazy_reason_.vars;
1232 tmp_reason_.coeffs = tmp_lazy_reason_.coeffs;
1234 tmp_boolean_literals_.assign(literals_reason.begin(),
1235 literals_reason.end());
1236 tmp_integer_literals_.assign(bounds_reason.begin(), bounds_reason.end());
1239 tmp_reason_.boolean_literals_at_false = tmp_boolean_literals_;
1240 tmp_reason_.integer_literals = tmp_integer_literals_;
1248std::string ReasonDebugString(absl::Span<const Literal> literal_reason,
1249 absl::Span<const IntegerLiteral> integer_reason) {
1250 std::string result =
"literals:{";
1251 for (
const Literal l : literal_reason) {
1252 if (result.back() !=
'{') result +=
",";
1253 result += l.DebugString();
1255 result +=
"} bounds:{";
1257 if (result.back() !=
'{') result +=
",";
1258 result += l.DebugString();
1266std::string IntegerTrail::DebugString() {
1267 std::string result =
"trail:{";
1268 const int num_vars = var_lbs_.size();
1270 std::min(num_vars + 30,
static_cast<int>(integer_trail_.size()));
1271 for (
int i = num_vars;
i < limit; ++
i) {
1272 if (result.back() !=
'{') result +=
",";
1275 integer_trail_[
i].bound)
1278 if (limit < integer_trail_.size()) {
1286 DCHECK(ReasonIsValid(i_lit, {}, {}));
1289 sat_solver_->NotifyThatModelIsUnsat();
1292 if (trail_->CurrentDecisionLevel() == 0) {
1293 if (!
Enqueue(i_lit, {}, {})) {
1294 sat_solver_->NotifyThatModelIsUnsat();
1306 if (i_lit.
bound >= var_lbs_[i_lit.
var]) {
1307 int index = var_trail_index_[i_lit.
var];
1308 const int num_vars = var_lbs_.size();
1309 while (index >= num_vars) {
1311 index = integer_trail_[index].prev_trail_index;
1313 DCHECK_EQ(index, i_lit.
var.value());
1316 DCHECK_GE(i_lit.
bound, var_lbs_[i_lit.
var]);
1318 var_trail_index_[i_lit.
var] = index;
1327 integer_trail_[i_lit.
var.value()].bound = i_lit.
bound;
1331 delayed_to_fix_->integer_literal_to_fix.push_back(i_lit);
1337 IntegerLiteral i_lit, absl::Span<const IntegerLiteral> integer_reason) {
1343 absl::Span<const IntegerLiteral> integer_reason) {
1352 tmp_cleaned_reason_.clear();
1354 DCHECK(!lit.IsAlwaysFalse());
1355 if (lit.IsAlwaysTrue())
continue;
1356 tmp_cleaned_reason_.push_back(lit);
1358 return Enqueue(i_lit, literal_reason, tmp_cleaned_reason_);
1363 std::vector<IntegerLiteral>* integer_reason) {
1368 literal_reason->push_back(lit.
Negated());
1369 return Enqueue(i_lit, *literal_reason, *integer_reason);
1373 integer_reason->push_back(
1388 const auto [it, inserted] =
1389 conditional_lbs_.insert({{lit.
Index(), i_lit.
var}, i_lit.
bound});
1391 it->second = std::max(it->second, i_lit.
bound);
1397bool IntegerTrail::ReasonIsValid(
1398 absl::Span<const Literal> literal_reason,
1399 absl::Span<const IntegerLiteral> integer_reason) {
1401 for (
const Literal lit : literal_reason) {
1404 for (
const IntegerLiteral i_lit : integer_reason) {
1407 LOG(INFO) <<
"Reason has a constant false literal!";
1410 if (i_lit.
bound > var_lbs_[i_lit.
var]) {
1411 LOG(INFO) <<
"Reason " << i_lit <<
" is not true!"
1412 <<
" non-optional variable:" << i_lit.
var
1413 <<
" current_lb:" << var_lbs_[i_lit.
var];
1420 if (!integer_search_levels_.empty()) {
1421 int num_literal_assigned_after_root_node = 0;
1422 for (
const Literal lit : literal_reason) {
1423 if (trail_->Info(lit.Variable()).level > 0) {
1424 num_literal_assigned_after_root_node++;
1427 for (
const IntegerLiteral i_lit : integer_reason) {
1430 num_literal_assigned_after_root_node++;
1433 if (num_literal_assigned_after_root_node == 0) {
1434 VLOG(2) <<
"Propagating a literal with no reason at a positive level!\n"
1435 <<
"level:" << integer_search_levels_.size() <<
" "
1436 << ReasonDebugString(literal_reason, integer_reason) <<
"\n"
1444bool IntegerTrail::ReasonIsValid(
1446 absl::Span<const IntegerLiteral> integer_reason) {
1447 if (!ReasonIsValid(literal_reason, integer_reason))
return false;
1448 if (debug_checker_ ==
nullptr)
return true;
1450 std::vector<Literal> clause;
1451 clause.assign(literal_reason.begin(), literal_reason.end());
1452 std::vector<IntegerLiteral> lits = {integer_reason.begin(),
1453 integer_reason.end()};
1455 const IntegerLiteral negated_i_lit =
1457 lits.push_back(negated_i_lit);
1458 if (!debug_checker_(clause, lits)) {
1459 LOG(INFO) <<
"Invalid reason for loaded solution: " << i_lit <<
" "
1460 << literal_reason <<
" " << integer_reason;
1466 if (!debug_checker_(clause, {negated_i_lit})) {
1467 LOG(INFO) <<
"Invalid reason for loaded solution after merging: " << i_lit
1468 <<
" " << literal_reason <<
" " << integer_reason;
1474bool IntegerTrail::ReasonIsValid(
1475 Literal lit, absl::Span<const Literal> literal_reason,
1476 absl::Span<const IntegerLiteral> integer_reason) {
1477 if (!ReasonIsValid(literal_reason, integer_reason))
return false;
1478 if (debug_checker_ ==
nullptr)
return true;
1480 std::vector<Literal> clause;
1481 clause.assign(literal_reason.begin(), literal_reason.end());
1482 clause.push_back(lit);
1483 if (!debug_checker_(clause, integer_reason)) {
1484 LOG(INFO) <<
"Invalid reason for loaded solution: " << lit <<
" "
1485 << literal_reason <<
" " << integer_reason;
1490 if (!debug_checker_(clause, {})) {
1491 LOG(INFO) <<
"Invalid reason for loaded solution after merging: " << lit
1492 <<
" " << literal_reason <<
" " << integer_reason;
1499 Literal literal, absl::Span<const Literal> literal_reason,
1500 absl::Span<const IntegerLiteral> integer_reason) {
1501 return EnqueueLiteralInternal(literal,
false, literal_reason, integer_reason);
1505 Literal literal, absl::Span<const Literal> literal_reason,
1506 absl::Span<const IntegerLiteral> integer_reason) {
1507 if (trail_->Assignment().LiteralIsTrue(literal)) {
1509 }
else if (trail_->Assignment().LiteralIsFalse(literal)) {
1515 tmp_cleaned_reason_.clear();
1517 DCHECK(!lit.IsAlwaysFalse());
1518 if (lit.IsAlwaysTrue())
continue;
1519 tmp_cleaned_reason_.push_back(lit);
1521 return EnqueueLiteralInternal(literal,
false, literal_reason,
1522 tmp_cleaned_reason_);
1525bool IntegerTrail::EnqueueLiteralInternal(
1526 Literal literal,
bool use_lazy_reason,
1527 absl::Span<const Literal> literal_reason,
1528 absl::Span<const IntegerLiteral> integer_reason) {
1530 DCHECK(use_lazy_reason ||
1531 ReasonIsValid(literal, literal_reason, integer_reason));
1532 if (integer_search_levels_.empty()) {
1537 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1538 literal_reason.empty() && !use_lazy_reason) {
1542 const int trail_index = trail_->
Index();
1543 if (trail_index >= boolean_trail_index_to_reason_index_.size()) {
1544 boolean_trail_index_to_reason_index_.resize(trail_index + 1);
1547 const ReasonIndex reason_index =
1549 ? -ReasonIndex(lazy_reasons_.size())
1551 boolean_trail_index_to_reason_index_[trail_index] = reason_index;
1563 for (
const IntegerLiteral i_lit : encoder_->GetIntegerLiterals(literal)) {
1565 if (!EnqueueAssociatedIntegerLiteral(i_lit, literal)) {
1578 if (parameters_.propagation_loop_detection_factor() == 0.0)
return false;
1580 !integer_search_levels_.empty() &&
1581 integer_trail_.size() - integer_search_levels_.back() >
1582 std::max(10000.0, parameters_.propagation_loop_detection_factor() *
1583 static_cast<double>(var_lbs_.size())) &&
1588 if (first_level_without_full_propagation_ == -1) {
1589 first_level_without_full_propagation_ = trail_->CurrentDecisionLevel();
1597 ++num_decisions_to_break_loop_;
1598 std::vector<IntegerVariable> vars;
1599 for (
int i = integer_search_levels_.back();
i < integer_trail_.size(); ++
i) {
1600 const IntegerVariable var = integer_trail_[
i].var;
1603 vars.push_back(var);
1606 std::sort(vars.begin(), vars.end());
1607 IntegerVariable best_var = vars[0];
1610 for (
int i = 1;
i < vars.size(); ++
i) {
1611 if (vars[
i] != vars[
i - 1]) {
1615 if (count > best_count) {
1625 return first_level_without_full_propagation_ != -1;
1629 for (IntegerVariable var(0); var < var_lbs_.size(); var += 2) {
1630 if (!
IsFixed(var))
return var;
1635void IntegerTrail::CanonicalizeLiteralIfNeeded(
IntegerLiteral* i_lit) {
1637 const Domain& domain = (*domains_)[index];
1647 absl::Span<const Literal> literal_reason,
1648 absl::Span<const IntegerLiteral> integer_reason) {
1649 const ReasonIndex reason_index(literals_reason_starts_.size());
1650 DCHECK_EQ(reason_index, bounds_reason_starts_.size());
1651 DCHECK_EQ(reason_index, cached_sizes_.size());
1653 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1654 if (!literal_reason.empty()) {
1655 literals_reason_buffer_.insert(literals_reason_buffer_.end(),
1656 literal_reason.begin(),
1657 literal_reason.end());
1660 cached_sizes_.push_back(-1);
1661 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1662 if (!integer_reason.empty()) {
1663 bounds_reason_buffer_.insert(bounds_reason_buffer_.end(),
1664 integer_reason.begin(), integer_reason.end());
1667 return reason_index;
1670bool IntegerTrail::EnqueueInternal(
1672 absl::Span<const Literal> literal_reason,
1673 absl::Span<const IntegerLiteral> integer_reason,
1674 int trail_index_with_same_reason) {
1675 DCHECK(use_lazy_reason ||
1676 ReasonIsValid(i_lit, literal_reason, integer_reason));
1677 const IntegerVariable var(i_lit.
var);
1682 if (i_lit.
bound <= var_lbs_[var])
return true;
1691 CanonicalizeLiteralIfNeeded(&i_lit);
1692 DCHECK_EQ(var, i_lit.
var);
1701 auto* conflict = InitializeConflict(i_lit, use_lazy_reason, literal_reason,
1704 tmp_queue_.push_back(FindLowestTrailIndexThatExplainBound(ub_reason));
1707 if (new_conflict_resolution_) {
1710 tmp_integer_literals_.push_back(ub_reason);
1721 MergeReasonIntoInternal(conflict, -1);
1745 if (i_lit.
bound - lb < (ub - lb) / 2) {
1746 if (first_level_without_full_propagation_ == -1) {
1747 first_level_without_full_propagation_ = trail_->CurrentDecisionLevel();
1754 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1770 IntegerValue to_enqueue_bound;
1771 const LiteralIndex literal_index =
1772 encoder_->SearchForLiteralAtOrBefore(i_lit, &to_enqueue_bound);
1774 CHECK_LE(to_enqueue_bound, i_lit.
bound);
1776 const Literal to_enqueue = Literal(literal_index);
1777 if (trail_->Assignment().LiteralIsFalse(to_enqueue)) {
1778 auto* conflict = InitializeConflict(i_lit, use_lazy_reason,
1779 literal_reason, integer_reason);
1780 conflict->push_back(to_enqueue);
1782 if (new_conflict_resolution_) {
1784 tmp_boolean_literals_.push_back(to_enqueue);
1785 tmp_reason_.boolean_literals_at_false = tmp_boolean_literals_;
1794 MergeReasonIntoInternal(conflict, -1);
1799 if (!trail_->Assignment().LiteralIsTrue(to_enqueue)) {
1800 if (!EnqueueLiteralInternal(to_enqueue, use_lazy_reason, literal_reason,
1817 if (new_conflict_resolution_) {
1820 if (to_enqueue_bound == i_lit.
bound) {
1821 return EnqueueAssociatedIntegerLiteral(
1825 if (to_enqueue_bound > var_lbs_[var]) {
1826 if (!EnqueueAssociatedIntegerLiteral(
1835 if (var_lbs_[var] >= i_lit.
bound) {
1842 if (integer_search_levels_.empty()) {
1843 ++num_level_zero_enqueues_;
1844 var_lbs_[var] = i_lit.
bound;
1845 integer_trail_[var.value()].bound = i_lit.
bound;
1849 trail_->MutableConflict()->clear();
1853 DCHECK_GT(trail_->CurrentDecisionLevel(), 0);
1857 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1858 literal_reason.empty() && !use_lazy_reason) {
1862 ReasonIndex reason_index;
1863 if (use_lazy_reason) {
1864 reason_index = -
static_cast<int>(lazy_reasons_.size());
1865 }
else if (trail_index_with_same_reason >= integer_trail_.size()) {
1869 reason_index = integer_trail_[trail_index_with_same_reason].reason_index;
1872 const int prev_trail_index = var_trail_index_[var];
1873 var_lbs_[var] = i_lit.
bound;
1874 var_trail_index_[var] = integer_trail_.size();
1875 PushOnTrail(i_lit, prev_trail_index, trail_->Index(), reason_index,
1876 trail_->CurrentDecisionLevel());
1880void IntegerTrail::PushOnTrail(
IntegerLiteral i_lit,
int prev_trail_index,
1881 int bool_trail_index, ReasonIndex reason_index,
1882 int assignment_level) {
1883 const int i = integer_trail_.size();
1884 integer_trail_.push_back({i_lit.bound,
1889 if (!new_conflict_resolution_)
return;
1890 if (extra_trail_info_.size() < integer_trail_.size()) {
1891 extra_trail_info_.resize(integer_trail_.size());
1894 extra_trail_info_[
i] = {assignment_level,
1898bool IntegerTrail::EnqueueAssociatedIntegerLiteral(
IntegerLiteral i_lit,
1900 DCHECK(ReasonIsValid(i_lit, {literal_reason.Negated()}, {}));
1903 if (i_lit.bound <= var_lbs_[i_lit.var])
return true;
1907 CanonicalizeLiteralIfNeeded(&i_lit);
1914 return Enqueue(i_lit, {literal_reason.Negated()}, {});
1918 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1919 bitset->Set(i_lit.var);
1926 if (integer_search_levels_.empty()) {
1927 var_lbs_[i_lit.var] = i_lit.bound;
1928 integer_trail_[i_lit.var.value()].bound = i_lit.bound;
1932 trail_->MutableConflict()->clear();
1937 if (trail_->AssignmentLevel(literal_reason) == 0) {
1940 DCHECK_GT(trail_->CurrentDecisionLevel(), 0);
1942 const ReasonIndex reason_index =
1944 const int prev_trail_index = var_trail_index_[i_lit.var];
1945 var_lbs_[i_lit.var] = i_lit.bound;
1946 var_trail_index_[i_lit.var] = integer_trail_.size();
1950 PushOnTrail(i_lit, prev_trail_index,
1951 trail_->Info(literal_reason.Variable()).trail_index + 1,
1952 reason_index, trail_->AssignmentLevel(literal_reason));
1956void IntegerTrail::ComputeLazyReasonIfNeeded(ReasonIndex index)
const {
1957 tmp_lazy_reason_.clear();
1958 if (index >= 0)
return;
1959 lazy_reasons_[-index - 1].Explain(&tmp_lazy_reason_);
1961 constexpr int64_t check_period = 1e6;
1962 int64_t limit_check = work_done_in_find_indices_ + check_period;
1964 lazy_reason_literals_.assign(
1965 tmp_lazy_reason_.boolean_literals_at_false.begin(),
1966 tmp_lazy_reason_.boolean_literals_at_false.end());
1967 for (
const Literal lit : tmp_lazy_reason_.boolean_literals_at_true) {
1968 lazy_reason_literals_.push_back(lit.Negated());
1971 lazy_reason_trail_indices_.clear();
1972 for (
const IntegerLiteral i_lit : tmp_lazy_reason_.integer_literals) {
1973 DCHECK(!i_lit.IsAlwaysFalse());
1974 if (i_lit.IsAlwaysTrue())
continue;
1981 const int index = tmp_var_to_trail_index_in_queue_[i_lit.var];
1982 if (index == std::numeric_limits<int>::max())
continue;
1983 if (index > 0 && integer_trail_[index].bound >= i_lit.bound) {
1984 has_dependency_ =
true;
1989 lazy_reason_trail_indices_.push_back(
1990 FindLowestTrailIndexThatExplainBound(i_lit));
1993 if (work_done_in_find_indices_ > limit_check) {
1994 limit_check = work_done_in_find_indices_ + check_period;
1995 if (time_limit_->LimitReached())
return;
2000 if (!tmp_lazy_reason_.vars.empty()) {
2001 tmp_indices_.clear();
2002 tmp_coeffs_.clear();
2004 const IntegerVariable to_ignore =
2006 for (
int i = 0;
i < tmp_lazy_reason_.vars.size(); ++
i) {
2007 const IntegerVariable var = tmp_lazy_reason_.vars[
i];
2013 tmp_indices_.push_back(index);
2014 if (tmp_lazy_reason_.slack > 0) {
2015 tmp_coeffs_.push_back(tmp_lazy_reason_.coeffs[
i]);
2019 if (tmp_lazy_reason_.slack > 0) {
2020 DCHECK_EQ(tmp_lazy_reason_.vars.size(), tmp_lazy_reason_.coeffs.size());
2023 lazy_reason_trail_indices_.insert(lazy_reason_trail_indices_.end(),
2024 tmp_indices_.begin(), tmp_indices_.end());
2029 ReasonIndex index)
const {
2031 const int start = bounds_reason_starts_[index];
2032 const int end = index + 1 < bounds_reason_starts_.size()
2033 ? bounds_reason_starts_[index + 1]
2034 : bounds_reason_buffer_.size();
2035 return absl::MakeSpan(bounds_reason_buffer_.data() + start,
end - start);
2038absl::Span<const int> IntegerTrail::Dependencies(ReasonIndex index)
const {
2040 return absl::Span<const int>(lazy_reason_trail_indices_);
2043 const int cached_size = cached_sizes_[index];
2044 if (cached_size == 0)
return {};
2046 const int start = bounds_reason_starts_[index];
2047 if (cached_size > 0) {
2048 return absl::MakeSpan(&trail_index_reason_buffer_[start], cached_size);
2052 DCHECK_EQ(cached_size, -1);
2053 const int end = index + 1 < bounds_reason_starts_.size()
2054 ? bounds_reason_starts_[index + 1]
2055 : bounds_reason_buffer_.size();
2056 if (
end > trail_index_reason_buffer_.size()) {
2057 trail_index_reason_buffer_.resize(
end);
2061 int* data = trail_index_reason_buffer_.data() + start;
2062 for (
int i = start;
i <
end; ++
i) {
2063 const IntegerLiteral to_explain = bounds_reason_buffer_[
i];
2065 data[new_size++] = FindLowestTrailIndexThatExplainBound(to_explain);
2068 cached_sizes_[index] = new_size;
2069 if (new_size == 0)
return {};
2070 return absl::MakeSpan(data, new_size);
2074 ReasonIndex index)
const {
2076 return lazy_reason_literals_;
2079 const int start = literals_reason_starts_[index];
2080 const int end = index + 1 < literals_reason_starts_.size()
2081 ? literals_reason_starts_[index + 1]
2082 : literals_reason_buffer_.size();
2083 return absl::MakeSpan(literals_reason_buffer_.data() + start,
end - start);
2086void IntegerTrail::AppendLiteralsReason(ReasonIndex index,
2087 std::vector<Literal>* output)
const {
2089 if (!added_variables_[lit.Variable()]) {
2090 added_variables_.
Set(lit.Variable());
2091 output->push_back(lit);
2097 std::vector<Literal> reason;
2103 std::vector<Literal>* output)
const {
2104 DCHECK(tmp_queue_.empty());
2106 if (literal.IsAlwaysTrue())
continue;
2108 tmp_queue_.push_back(FindLowestTrailIndexThatExplainBound(literal));
2110 return MergeReasonIntoInternal(output, -1);
2115void IntegerTrail::MergeReasonIntoInternal(std::vector<Literal>* output,
2116 int64_t conflict_id)
const {
2119 DCHECK(std::all_of(tmp_var_to_trail_index_in_queue_.begin(),
2120 tmp_var_to_trail_index_in_queue_.end(),
2121 [](
int v) { return v == 0; }));
2122 DCHECK(tmp_to_clear_.empty());
2124 info_is_valid_on_subsequent_last_level_expansion_ =
true;
2125 if (conflict_id == -1 || last_conflict_id_ != conflict_id) {
2128 last_conflict_id_ = conflict_id;
2129 for (
const IntegerVariable var : to_clear_for_lower_level_) {
2130 var_to_trail_index_at_lower_level_[var] = 0;
2132 to_clear_for_lower_level_.clear();
2135 const int last_decision_index =
2136 integer_search_levels_.empty() || conflict_id == -1
2138 : integer_search_levels_.back();
2141 for (
const Literal l : *output) {
2142 added_variables_.
Set(l.Variable());
2147 for (
const int trail_index : tmp_queue_) {
2148 DCHECK_GE(trail_index, var_lbs_.size());
2149 DCHECK_LT(trail_index, integer_trail_.size());
2150 const TrailEntry& entry = integer_trail_[trail_index];
2152 tmp_var_to_trail_index_in_queue_[entry.var] =
2153 std::max(tmp_var_to_trail_index_in_queue_[entry.var], trail_index);
2158 std::make_heap(tmp_queue_.begin(), tmp_queue_.end());
2163 int64_t work_done = 0;
2164 while (!tmp_queue_.empty()) {
2166 const int trail_index = tmp_queue_.front();
2167 const TrailEntry& entry = integer_trail_[trail_index];
2168 std::pop_heap(tmp_queue_.begin(), tmp_queue_.end());
2169 tmp_queue_.pop_back();
2175 if (tmp_var_to_trail_index_in_queue_[entry.var] != trail_index) {
2190 tmp_var_to_trail_index_in_queue_[entry.var] = 0;
2191 has_dependency_ =
false;
2195 if (var_to_trail_index_at_lower_level_[entry.var] >= trail_index) {
2204 if (trail_index < last_decision_index) {
2205 tmp_seen_.push_back(trail_index);
2211 var_trail_index_cache_threshold_ = trail_index;
2216 const LiteralIndex associated_lit =
2218 IntegerVariable(entry.var), entry.bound));
2221 const ReasonIndex reason_index =
2222 integer_trail_[trail_index].reason_index;
2223 CHECK_GE(reason_index, 0);
2225 const int start = literals_reason_starts_[reason_index];
2226 const int end = reason_index + 1 < literals_reason_starts_.size()
2227 ? literals_reason_starts_[reason_index + 1]
2228 : literals_reason_buffer_.size();
2229 CHECK_EQ(start + 1,
end);
2236 CHECK_EQ(literals_reason_buffer_[start],
2237 Literal(associated_lit).Negated());
2241 const int start = bounds_reason_starts_[reason_index];
2242 const int end = reason_index + 1 < bounds_reason_starts_.size()
2243 ? bounds_reason_starts_[reason_index + 1]
2244 : bounds_reason_buffer_.size();
2245 CHECK_EQ(start,
end);
2250 ComputeLazyReasonIfNeeded(entry.reason_index);
2251 AppendLiteralsReason(entry.reason_index, output);
2252 const auto dependencies = Dependencies(entry.reason_index);
2253 work_done += dependencies.size();
2254 for (
const int next_trail_index : dependencies) {
2255 DCHECK_LT(next_trail_index, trail_index);
2256 const TrailEntry& next_entry = integer_trail_[next_trail_index];
2267 const int index_in_queue =
2268 tmp_var_to_trail_index_in_queue_[next_entry.var];
2272 if (index_in_queue >= trail_index) {
2275 if (index_in_queue >= last_decision_index) {
2276 info_is_valid_on_subsequent_last_level_expansion_ =
false;
2281 if (next_trail_index <=
2282 var_to_trail_index_at_lower_level_[next_entry.var]) {
2286 has_dependency_ =
true;
2287 if (next_trail_index > index_in_queue) {
2288 tmp_var_to_trail_index_in_queue_[next_entry.var] = next_trail_index;
2289 tmp_queue_.push_back(next_trail_index);
2290 std::push_heap(tmp_queue_.begin(), tmp_queue_.end());
2296 if (!has_dependency_) {
2297 tmp_to_clear_.push_back(entry.var);
2298 tmp_var_to_trail_index_in_queue_[entry.var] = trail_index;
2303 if (info_is_valid_on_subsequent_last_level_expansion_) {
2304 for (
const int trail_index : tmp_seen_) {
2305 if (trail_index == 0)
continue;
2306 const TrailEntry& entry = integer_trail_[trail_index];
2307 const int old = var_to_trail_index_at_lower_level_[entry.var];
2309 to_clear_for_lower_level_.push_back(entry.var);
2311 var_to_trail_index_at_lower_level_[entry.var] =
2312 std::max(old, trail_index);
2318 for (
const IntegerVariable var : tmp_to_clear_) {
2319 tmp_var_to_trail_index_in_queue_[var] = 0;
2321 tmp_to_clear_.clear();
2323 time_limit_->AdvanceDeterministicTime(work_done * 5e-9);
2329 const Trail& trail,
int trail_index, int64_t )
const {
2331 added_variables_.ClearAndResize(BooleanVariable(trail_->NumVariables()));
2333 const ReasonIndex reason_index =
2334 boolean_trail_index_to_reason_index_[trail_index];
2335 ComputeLazyReasonIfNeeded(reason_index);
2336 AppendLiteralsReason(reason_index, reason);
2337 DCHECK(tmp_queue_.empty());
2338 for (
const int prev_trail_index : Dependencies(reason_index)) {
2339 DCHECK_GE(prev_trail_index, var_lbs_.size());
2344 const TrailEntry& next_entry = integer_trail_[prev_trail_index];
2347 tmp_queue_.push_back(prev_trail_index);
2353 MergeReasonIntoInternal(reason, -1);
2355 if (
DEBUG_MODE && debug_checker_ !=
nullptr) {
2356 reason->push_back(trail[trail_index]);
2357 CHECK(debug_checker_(*reason, {}))
2358 << (reason_index >= 0 ?
""
2359 : absl::StrCat(
"lazy reason for propagator ",
2360 lazy_reasons_[-reason_index - 1]
2361 .explainer->LazyReasonName()));
2369 tmp_reason_.
clear();
2371 ReasonIndex reason_index;
2377 if (trail_->AssignmentType((*trail_)[index.
bool_index].Variable()) ==
2379 reason_index = boolean_trail_index_to_reason_index_[index.
bool_index];
2382 const BooleanVariable
b = (*trail_)[index.
bool_index].Variable();
2383 tmp_reason_.boolean_literals_at_false = trail_->Reason(
b);
2387 reason_index = integer_trail_[index.
integer_index].reason_index;
2390 if (reason_index < 0) {
2392 if (needed_bound != std::nullopt) {
2393 lazy_reasons_[-reason_index - 1].Explain(*needed_bound, &tmp_reason_);
2395 lazy_reasons_[-reason_index - 1].Explain(&tmp_reason_);
2413 int base_index, std::vector<IntegerLiteral>* output)
const {
2414 tmp_marked_.ClearAndResize(IntegerVariable(var_lbs_.size()));
2417 CHECK_GE(base_index, var_lbs_.size());
2418 for (
int i = integer_trail_.size(); --
i >= base_index;) {
2419 const TrailEntry& entry = integer_trail_[
i];
2421 if (tmp_marked_[entry.var])
continue;
2423 tmp_marked_.Set(entry.var);
2430 time_limit_(model->GetOrCreate<
TimeLimit>()),
2439 integer_trail_->RegisterReversibleClass(
2440 &id_to_greatest_common_level_since_last_call_);
2441 integer_trail_->RegisterWatcher(&modified_vars_);
2442 queue_by_priority_.resize(2);
2446 var_to_watcher_.reserve(2 * num_vars);
2450 if (in_queue_[
id])
return;
2451 in_queue_[id] =
true;
2452 queue_by_priority_[id_to_priority_[id]].push_back(
id);
2455void GenericLiteralWatcher::UpdateCallingNeeds(
Trail* trail) {
2457 const int literal_limit = literal_to_watcher_.size();
2460 if (literal.
Index() >= literal_limit)
continue;
2461 for (
const auto entry : literal_to_watcher_[literal]) {
2463 if (entry.watch_index >= 0) {
2464 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
2470 const int var_limit = var_to_watcher_.size();
2471 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
2472 if (var.value() >= var_limit)
continue;
2473 for (
const auto entry : var_to_watcher_[var]) {
2475 if (entry.watch_index >= 0) {
2476 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
2482 !level_zero_modified_variable_callback_.empty()) {
2483 modified_vars_for_callback_.Resize(modified_vars_.size());
2484 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
2485 modified_vars_for_callback_.Set(var);
2489 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
2497 for (
const int id : propagator_ids_to_call_at_level_zero_) {
2502 UpdateCallingNeeds(trail);
2506 int64_t num_propagate_calls = 0;
2507 const int64_t old_enqueue = integer_trail_->num_enqueues();
2509 ::absl::MakeCleanup([&num_propagate_calls, old_enqueue,
this]() {
2510 const int64_t diff = integer_trail_->num_enqueues() - old_enqueue;
2511 time_limit_->AdvanceDeterministicTime(1e-8 * num_propagate_calls +
2518 for (
int priority = 0; priority < queue_by_priority_.size(); ++priority) {
2526 if (test_limit > 100) {
2528 if (time_limit_->LimitReached())
break;
2530 if (stop_propagation_callback_ !=
nullptr && stop_propagation_callback_()) {
2531 integer_trail_->NotifyThatPropagationWasAborted();
2535 std::deque<int>& queue = queue_by_priority_[priority];
2536 while (!queue.empty()) {
2537 const int id = queue.front();
2542 if (id_need_reversible_support_[
id]) {
2544 id_to_greatest_common_level_since_last_call_[IdType(
id)];
2545 const int high = id_to_level_at_last_call_[id];
2546 if (low < high || level > low) {
2547 id_to_level_at_last_call_[id] = level;
2548 id_to_greatest_common_level_since_last_call_.MutableRef(IdType(
id)) =
2551 if (low < high) rev->SetLevel(low);
2552 if (level > low) rev->SetLevel(level);
2554 for (
int* rev_int : id_to_reversible_ints_[
id]) {
2555 rev_int_repository_->SaveState(rev_int);
2561 const int64_t old_integer_timestamp = integer_trail_->num_enqueues();
2562 const int64_t old_boolean_timestamp = trail->
Index();
2566 call_again_ =
false;
2569 ++num_propagate_calls;
2571 id_to_watch_indices_[id].empty()
2572 ? watchers_[id]->Propagate()
2573 : watchers_[id]->IncrementalPropagate(id_to_watch_indices_[
id]);
2575 id_to_watch_indices_[id].clear();
2576 in_queue_[id] =
false;
2582 if (id_to_idempotence_[
id]) {
2586 UpdateCallingNeeds(trail);
2587 id_to_watch_indices_[id].clear();
2588 in_queue_[id] =
false;
2593 id_to_watch_indices_[id].clear();
2594 in_queue_[id] =
false;
2595 UpdateCallingNeeds(trail);
2605 if (trail->
Index() > old_boolean_timestamp) {
2617 if (integer_trail_->num_enqueues() > old_integer_timestamp) {
2627 const std::vector<IntegerVariable>& modified_vars =
2628 modified_vars_for_callback_.PositionsSetAtLeastOnce();
2629 for (
const auto& callback : level_zero_modified_variable_callback_) {
2630 callback(modified_vars);
2632 modified_vars_for_callback_.ClearAndResize(
2633 integer_trail_->NumIntegerVariables());
2644 if (time_limit_->LimitReached())
return;
2654 for (
bool* to_reset : bool_to_reset_on_backtrack_) *to_reset =
false;
2655 bool_to_reset_on_backtrack_.clear();
2658 for (std::deque<int>& queue : queue_by_priority_) {
2659 for (
const int id : queue) {
2660 id_to_watch_indices_[id].clear();
2669 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
2670 in_queue_.assign(watchers_.size(),
false);
2675 const int id = watchers_.size();
2676 watchers_.push_back(propagator);
2678 id_need_reversible_support_.push_back(
false);
2679 id_to_level_at_last_call_.push_back(0);
2680 id_to_greatest_common_level_since_last_call_.GrowByOne();
2681 id_to_reversible_classes_.push_back(std::vector<ReversibleInterface*>());
2682 id_to_reversible_ints_.push_back(std::vector<int*>());
2684 id_to_watch_indices_.push_back(std::vector<int>());
2685 id_to_priority_.push_back(1);
2686 id_to_idempotence_.push_back(
true);
2695 in_queue_.push_back(
true);
2696 queue_by_priority_[1].push_back(
id);
2701 id_to_priority_[id] = priority;
2702 if (priority >= queue_by_priority_.size()) {
2703 queue_by_priority_.resize(priority + 1);
2709 id_to_idempotence_[id] =
false;
2713 propagator_ids_to_call_at_level_zero_.push_back(
id);
2718 id_need_reversible_support_[id] =
true;
2719 id_to_reversible_classes_[id].push_back(rev);
2723 id_need_reversible_support_[id] =
true;
2724 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)
int Register(PropagatorInterface *propagator)
void CallOnNextPropagate(int id)
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)
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)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
LiteralIndex SearchForLiteralAtOrAfter(IntegerLiteral i_lit, IntegerValue *bound) const
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
absl::Span< const IntegerLiteral > IntegerReasonAsSpan(ReasonIndex index) const
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
absl::Span< const Literal > LiteralReasonAsSpan(ReasonIndex index) 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)
const IntegerReason & GetIntegerReason(GlobalTrailIndex index, std::optional< IntegerValue > needed_bound)
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
bool IsTrueAtLevelZero(IntegerLiteral l) const
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
bool InPropagationLoop() const
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
bool IsFixed(IntegerVariable i) const
IntegerVariable NumIntegerVariables() const
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
ABSL_MUST_USE_RESULT bool SafeEnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
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
ABSL_MUST_USE_RESULT bool 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
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()
ReasonIndex AppendReasonToInternalBuffers(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
static constexpr SearchBranching FIXED_SEARCH
SatPropagator(const std::string &name)
int propagation_trail_index_
bool AddClauseDuringSearch(absl::Span< const Literal > literals)
void AddLastPropagator(SatPropagator *propagator)
std::vector< Literal > * MutableConflict()
void EnqueueWithUnitReason(Literal true_literal)
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)
IntType IntTypeAbs(IntType t)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
bool VariableIsPositive(IntegerVariable i)
ClosedInterval::Iterator end(ClosedInterval interval)
int64_t CapProd(int64_t x, int64_t y)
ClosedInterval::Iterator begin(ClosedInterval interval)
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)
static IntegerLiteral TrueLiteral()
IntegerLiteral Negated() const
std::string DebugString() const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
bool IsAlwaysTrue() const
absl::Span< const IntegerLiteral > integer_literals
std::string DebugString() const