20 #include <type_traits> 30 const std::vector<IntegerVariable>& vars) {
31 std::vector<IntegerVariable> result(vars.size());
32 for (
int i = 0; i < vars.size(); ++i) {
73 <<
"Domain too large for full encoding.";
84 for (
const int64_t v : (*domains_)[
var].Values()) {
85 tmp_values_.push_back(IntegerValue(v));
87 for (
const IntegerValue v : tmp_values_) {
99 if (
index >= is_fully_encoded_.
size())
return false;
102 if (is_fully_encoded_[
index])
return true;
109 const int64_t initial_domain_size = (*domains_)[
var].Size();
110 if (equality_by_var_[
index].size() < initial_domain_size)
return false;
119 const auto& ref = equality_by_var_[
index];
121 for (
const int64_t v : (*domains_)[
var].Values()) {
122 if (i < ref.size() && v == ref[i].value) {
126 if (i == ref.size()) {
127 is_fully_encoded_[
index] =
true;
129 return is_fully_encoded_[
index];
132 std::vector<IntegerEncoder::ValueLiteralPair>
138 std::vector<IntegerEncoder::ValueLiteralPair>
142 if (
index >= equality_by_var_.size())
return {};
145 std::vector<ValueLiteralPair>& ref = equality_by_var_[
index];
146 for (
int i = 0; i < ref.size(); ++i) {
155 ref[new_size++] = pair;
157 ref.resize(new_size);
158 std::sort(ref.begin(), ref.end());
160 std::vector<IntegerEncoder::ValueLiteralPair> result = ref;
162 std::reverse(result.begin(), result.end());
169 IntegerVariable
var)
const {
172 if (
index >= equality_by_var_.size())
return {};
174 return equality_by_var_[
index];
180 void IntegerEncoder::AddImplications(
181 const std::map<IntegerValue, Literal>& map,
182 std::map<IntegerValue, Literal>::const_iterator it,
184 if (!add_implications_)
return;
190 if (after_it != map.end()) {
192 {after_it->second.Negated(), associated_lit});
196 if (it != map.begin()) {
200 {associated_lit.
Negated(), before_it->second});
206 add_implications_ =
true;
207 for (
const std::map<IntegerValue, Literal>& encoding : encoding_by_var_) {
209 for (
const auto value_literal : encoding) {
210 const Literal lit = value_literal.second;
215 previous = lit.
Index();
222 const IntegerVariable
var(i_lit.
var);
223 IntegerValue after(i_lit.
bound);
224 IntegerValue before(i_lit.
bound - 1);
229 if (before > previous && before <
interval.start) before = previous;
239 if (i_lit.
bound <= (*domains_)[i_lit.
var].Min()) {
242 if (i_lit.
bound > (*domains_)[i_lit.
var].Max()) {
254 ++num_created_variables_;
261 VLOG(1) <<
"Created a fixed literal for no reason!";
267 std::pair<PositiveOnlyIndex, IntegerValue> PositiveVarKey(IntegerVariable
var,
268 IntegerValue
value) {
275 IntegerVariable
var, IntegerValue
value)
const {
277 equality_to_associated_literal_.find(PositiveVarKey(
var,
value));
278 if (it != equality_to_associated_literal_.end()) {
279 return it->second.Index();
285 IntegerVariable
var, IntegerValue
value) {
288 equality_to_associated_literal_.find(PositiveVarKey(
var,
value));
289 if (it != equality_to_associated_literal_.end()) {
303 ++num_created_variables_;
313 VLOG(1) <<
"Created a fixed literal for no reason!";
320 const auto& domain = (*domains_)[i_lit.
var];
321 const IntegerValue
min(domain.Min());
322 const IntegerValue
max(domain.Max());
329 HalfAssociateGivenLiteral(pair.first,
literal);
330 HalfAssociateGivenLiteral(pair.second,
literal.Negated());
335 if (pair.first.bound ==
max) {
338 if (-pair.second.bound ==
min) {
346 IntegerValue
value) {
351 if (
value == 1 && domain.
Min() >= 0 && domain.
Max() <= 1) {
359 if (
value == -1 && domain.
Min() >= -1 && domain.
Max() <= 0) {
370 const auto insert_result = equality_to_associated_literal_.insert(
372 if (!insert_result.second) {
393 if (
index >= equality_by_var_.size()) {
394 equality_by_var_.resize(
index.value() + 1);
397 equality_by_var_[
index].push_back(
432 const int new_size = 1 +
literal.Index().value();
433 if (new_size > full_reverse_encoding_.
size()) {
434 full_reverse_encoding_.
resize(new_size);
444 void IntegerEncoder::HalfAssociateGivenLiteral(
IntegerLiteral i_lit,
447 const int new_size = 1 +
literal.Index().value();
448 if (new_size > reverse_encoding_.
size()) {
449 reverse_encoding_.
resize(new_size);
451 if (new_size > full_reverse_encoding_.
size()) {
452 full_reverse_encoding_.
resize(new_size);
456 if (i_lit.
var >= encoding_by_var_.size()) {
457 encoding_by_var_.resize(i_lit.
var.value() + 1);
459 auto& var_encoding = encoding_by_var_[i_lit.
var];
460 auto insert_result = var_encoding.insert({i_lit.
bound,
literal});
461 if (insert_result.second) {
462 AddImplications(var_encoding, insert_result.first,
literal);
465 newly_fixed_integer_literals_.push_back(i_lit);
473 const Literal associated(insert_result.first->second);
483 if (i.
var >= encoding_by_var_.size())
return false;
484 const std::map<IntegerValue, Literal>& encoding = encoding_by_var_[i.
var];
485 return encoding.find(i.
bound) != encoding.end();
490 const std::map<IntegerValue, Literal>& encoding = encoding_by_var_[i.
var];
491 const auto result = encoding.find(i.
bound);
493 return result->second.Index();
501 const std::map<IntegerValue, Literal>& encoding = encoding_by_var_[i.
var];
502 auto after_it = encoding.upper_bound(i.
bound);
505 *
bound = after_it->first;
506 return after_it->second.Index();
511 VLOG(1) <<
"Num decisions to break propagation loop: " 512 << num_decisions_to_break_loop_;
523 if (level > integer_search_levels_.size()) {
524 integer_search_levels_.push_back(integer_trail_.size());
525 reason_decision_levels_.push_back(literals_reason_starts_.size());
531 var_to_current_lb_interval_index_.
SetLevel(level);
544 if (!
Enqueue(i_lit, {}, {}))
return false;
550 if (!
Enqueue(i_lit, {}, {}))
return false;
552 integer_literal_to_fix_.clear();
554 for (
const Literal lit : literal_to_fix_) {
559 literal_to_fix_.clear();
564 while (propagation_trail_index_ < trail->
Index()) {
570 if (!EnqueueAssociatedIntegerLiteral(i_lit,
literal)) {
581 conditional_lbs_.clear();
583 var_to_current_lb_interval_index_.
SetLevel(level);
587 if (level < first_level_without_full_propagation_) {
588 first_level_without_full_propagation_ = -1;
593 if (level >= integer_search_levels_.size())
return;
594 const int target = integer_search_levels_[level];
595 integer_search_levels_.resize(level);
597 CHECK_LE(target, integer_trail_.size());
599 for (
int index = integer_trail_.size() - 1;
index >= target; --
index) {
600 const TrailEntry& entry = integer_trail_[
index];
601 if (entry.var < 0)
continue;
602 vars_[entry.var].current_trail_index = entry.prev_trail_index;
603 vars_[entry.var].current_bound =
604 integer_trail_[entry.prev_trail_index].bound;
606 integer_trail_.resize(target);
609 const int old_size = reason_decision_levels_[level];
610 reason_decision_levels_.resize(level);
611 if (old_size < literals_reason_starts_.size()) {
612 literals_reason_buffer_.resize(literals_reason_starts_[old_size]);
614 const int bound_start = bounds_reason_starts_[old_size];
615 bounds_reason_buffer_.resize(bound_start);
616 if (bound_start < trail_index_reason_buffer_.size()) {
617 trail_index_reason_buffer_.resize(bound_start);
620 literals_reason_starts_.resize(old_size);
621 bounds_reason_starts_.resize(old_size);
631 const int size = 2 * num_vars;
633 is_ignored_literals_.
reserve(size);
634 integer_trail_.reserve(size);
636 var_trail_index_cache_.
reserve(size);
637 tmp_var_to_trail_index_in_queue_.
reserve(size);
647 DCHECK(integer_search_levels_.empty());
650 const IntegerVariable i(vars_.
size());
665 var_trail_index_cache_.
resize(vars_.
size(), integer_trail_.size());
666 tmp_var_to_trail_index_in_queue_.
resize(vars_.
size(), 0);
677 IntegerValue(domain.
Max()));
683 return (*domains_)[
var];
691 if (old_domain == domain)
return true;
693 if (domain.
IsEmpty())
return false;
694 (*domains_)[
var] = domain;
697 var_to_current_lb_interval_index_.
Set(
var, 0);
716 if (i == domain.
NumIntervals() || pair.value < domain[i].start) {
726 <<
"Domain intersection fixed " << num_fixed
727 <<
" equality literal corresponding to values outside the new domain.";
734 IntegerValue
value) {
738 insert.first->second = new_var;
745 return insert.first->second;
751 return (constant_map_.size() + 1) / 2;
755 int threshold)
const {
759 const int index_in_queue = tmp_var_to_trail_index_in_queue_[
var];
760 if (threshold <= index_in_queue) {
762 has_dependency_ =
true;
767 int trail_index = vars_[
var].current_trail_index;
770 if (trail_index > threshold) {
771 const int cached_index = var_trail_index_cache_[
var];
772 if (cached_index >= threshold && cached_index < trail_index &&
773 integer_trail_[cached_index].
var ==
var) {
774 trail_index = cached_index;
778 while (trail_index >= threshold) {
779 trail_index = integer_trail_[trail_index].prev_trail_index;
780 if (trail_index >= var_trail_index_cache_threshold_) {
781 var_trail_index_cache_[
var] = trail_index;
785 const int num_vars = vars_.
size();
786 return trail_index < num_vars ? -1 : trail_index;
789 int IntegerTrail::FindLowestTrailIndexThatExplainBound(
793 int trail_index = vars_[i_lit.
var].current_trail_index;
801 const int cached_index = var_trail_index_cache_[i_lit.
var];
802 if (cached_index < trail_index) {
803 const TrailEntry& entry = integer_trail_[cached_index];
804 if (entry.var == i_lit.
var && entry.bound >= i_lit.
bound) {
805 trail_index = cached_index;
810 int prev_trail_index = trail_index;
812 if (trail_index >= var_trail_index_cache_threshold_) {
813 var_trail_index_cache_[i_lit.
var] = trail_index;
815 const TrailEntry& entry = integer_trail_[trail_index];
816 if (entry.bound == i_lit.
bound)
return trail_index;
817 if (entry.bound < i_lit.
bound)
return prev_trail_index;
818 prev_trail_index = trail_index;
819 trail_index = entry.prev_trail_index;
825 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
826 std::vector<IntegerLiteral>* reason)
const {
828 if (slack == 0)
return;
829 const int size = reason->size();
830 tmp_indices_.resize(size);
831 for (
int i = 0; i < size; ++i) {
834 tmp_indices_[i] = vars_[(*reason)[i].var].current_trail_index;
840 for (
const int i : tmp_indices_) {
842 integer_trail_[i].
bound));
847 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
848 absl::Span<const IntegerVariable> vars,
849 std::vector<IntegerLiteral>* reason)
const {
850 tmp_indices_.clear();
851 for (
const IntegerVariable
var : vars) {
852 tmp_indices_.push_back(vars_[
var].current_trail_index);
855 for (
const int i : tmp_indices_) {
857 integer_trail_[i].
bound));
862 absl::Span<const IntegerValue> coeffs,
863 std::vector<int>* trail_indices)
const {
865 DCHECK(relax_heap_.empty());
872 const int size = coeffs.size();
873 const int num_vars = vars_.
size();
874 for (
int i = 0; i < size; ++i) {
875 const int index = (*trail_indices)[i];
878 if (
index < num_vars)
continue;
881 const IntegerValue coeff = coeffs[i];
883 (*trail_indices)[new_size++] =
index;
890 const TrailEntry& entry = integer_trail_[
index];
892 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
893 (*trail_indices)[new_size++] =
index;
898 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
900 CapProd(coeff.value(), (entry.bound - previous_entry.bound).
value());
902 (*trail_indices)[new_size++] =
index;
906 relax_heap_.push_back({
index, coeff, diff});
908 trail_indices->resize(new_size);
909 std::make_heap(relax_heap_.begin(), relax_heap_.end());
911 while (slack > 0 && !relax_heap_.empty()) {
912 const RelaxHeapEntry heap_entry = relax_heap_.front();
913 std::pop_heap(relax_heap_.begin(), relax_heap_.end());
914 relax_heap_.pop_back();
917 if (heap_entry.diff > slack) {
918 trail_indices->push_back(heap_entry.index);
923 slack -= heap_entry.diff;
924 const int index = integer_trail_[heap_entry.index].prev_trail_index;
927 if (
index < num_vars)
continue;
928 if (heap_entry.coeff > slack) {
929 trail_indices->push_back(
index);
932 const TrailEntry& entry = integer_trail_[
index];
934 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
935 trail_indices->push_back(
index);
939 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
940 const int64_t diff =
CapProd(heap_entry.coeff.value(),
941 (entry.bound - previous_entry.bound).
value());
943 trail_indices->push_back(
index);
946 relax_heap_.push_back({
index, heap_entry.coeff, diff});
947 std::push_heap(relax_heap_.begin(), relax_heap_.end());
952 for (
const RelaxHeapEntry& entry : relax_heap_) {
953 trail_indices->push_back(entry.index);
959 std::vector<IntegerLiteral>* reason)
const {
963 (*reason)[new_size++] =
literal;
965 reason->resize(new_size);
968 std::vector<Literal>* IntegerTrail::InitializeConflict(
969 IntegerLiteral integer_literal,
const LazyReasonFunction& lazy_reason,
970 absl::Span<const Literal> literals_reason,
971 absl::Span<const IntegerLiteral> bounds_reason) {
972 DCHECK(tmp_queue_.empty());
974 if (lazy_reason ==
nullptr) {
975 conflict->assign(literals_reason.begin(), literals_reason.end());
976 const int num_vars = vars_.
size();
978 const int trail_index = FindLowestTrailIndexThatExplainBound(
literal);
979 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
984 lazy_reason(integer_literal, integer_trail_.size(), conflict, &tmp_queue_);
991 std::string ReasonDebugString(absl::Span<const Literal> literal_reason,
992 absl::Span<const IntegerLiteral> integer_reason) {
993 std::string result =
"literals:{";
994 for (
const Literal l : literal_reason) {
995 if (result.back() !=
'{') result +=
",";
996 result += l.DebugString();
998 result +=
"} bounds:{";
999 for (
const IntegerLiteral l : integer_reason) {
1000 if (result.back() !=
'{') result +=
",";
1001 result += l.DebugString();
1009 std::string IntegerTrail::DebugString() {
1010 std::string result =
"trail:{";
1011 const int num_vars = vars_.
size();
1013 std::min(num_vars + 30, static_cast<int>(integer_trail_.size()));
1014 for (
int i = num_vars; i < limit; ++i) {
1015 if (result.back() !=
'{') result +=
",";
1018 integer_trail_[i].
bound)
1021 if (limit < integer_trail_.size()) {
1029 absl::Span<const Literal> literal_reason,
1030 absl::Span<const IntegerLiteral> integer_reason) {
1031 return EnqueueInternal(i_lit,
nullptr, literal_reason, integer_reason,
1032 integer_trail_.size());
1037 std::vector<IntegerLiteral>* integer_reason) {
1045 return Enqueue(i_lit, *literal_reason, *integer_reason);
1049 literal_reason->push_back(lit.
Negated());
1050 return Enqueue(i_lit, *literal_reason, *integer_reason);
1054 integer_reason->push_back(
1070 const auto [it, inserted] =
1071 conditional_lbs_.insert({{lit.
Index(), i_lit.
var}, i_lit.
bound});
1080 absl::Span<const Literal> literal_reason,
1081 absl::Span<const IntegerLiteral> integer_reason,
1082 int trail_index_with_same_reason) {
1083 return EnqueueInternal(i_lit,
nullptr, literal_reason, integer_reason,
1084 trail_index_with_same_reason);
1089 return EnqueueInternal(i_lit, lazy_reason, {}, {}, integer_trail_.size());
1092 bool IntegerTrail::ReasonIsValid(
1093 absl::Span<const Literal> literal_reason,
1094 absl::Span<const IntegerLiteral> integer_reason) {
1096 for (
const Literal lit : literal_reason) {
1099 for (
const IntegerLiteral i_lit : integer_reason) {
1100 if (i_lit.
bound > vars_[i_lit.
var].current_bound) {
1103 LOG(
INFO) <<
"Reason " << i_lit <<
" is not true!" 1104 <<
" optional variable:" << i_lit.
var 1107 <<
" current_lb:" << vars_[i_lit.
var].current_bound;
1109 LOG(
INFO) <<
"Reason " << i_lit <<
" is not true!" 1110 <<
" non-optional variable:" << i_lit.
var 1111 <<
" current_lb:" << vars_[i_lit.
var].current_bound;
1119 if (!integer_search_levels_.empty()) {
1120 int num_literal_assigned_after_root_node = 0;
1121 for (
const Literal lit : literal_reason) {
1122 if (trail_->
Info(lit.Variable()).level > 0) {
1123 num_literal_assigned_after_root_node++;
1126 for (
const IntegerLiteral i_lit : integer_reason) {
1128 num_literal_assigned_after_root_node++;
1131 if (num_literal_assigned_after_root_node == 0) {
1132 VLOG(2) <<
"Propagating a literal with no reason at a positive level!\n" 1133 <<
"level:" << integer_search_levels_.size() <<
" " 1134 << ReasonDebugString(literal_reason, integer_reason) <<
"\n" 1144 absl::Span<const IntegerLiteral> integer_reason) {
1145 EnqueueLiteralInternal(
literal,
nullptr, literal_reason, integer_reason);
1148 void IntegerTrail::EnqueueLiteralInternal(
1150 absl::Span<const Literal> literal_reason,
1151 absl::Span<const IntegerLiteral> integer_reason) {
1153 DCHECK(lazy_reason !=
nullptr ||
1154 ReasonIsValid(literal_reason, integer_reason));
1155 if (integer_search_levels_.empty()) {
1162 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1163 literal_reason.empty() && lazy_reason ==
nullptr) {
1164 literal_to_fix_.push_back(
literal);
1167 const int trail_index = trail_->
Index();
1168 if (trail_index >= boolean_trail_index_to_integer_one_.size()) {
1169 boolean_trail_index_to_integer_one_.resize(trail_index + 1);
1171 boolean_trail_index_to_integer_one_[trail_index] = integer_trail_.size();
1173 int reason_index = literals_reason_starts_.size();
1174 if (lazy_reason !=
nullptr) {
1175 if (integer_trail_.size() >= lazy_reasons_.size()) {
1176 lazy_reasons_.resize(integer_trail_.size() + 1,
nullptr);
1178 lazy_reasons_[integer_trail_.size()] = lazy_reason;
1182 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1183 literals_reason_buffer_.insert(literals_reason_buffer_.end(),
1184 literal_reason.begin(),
1185 literal_reason.end());
1186 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1187 bounds_reason_buffer_.insert(bounds_reason_buffer_.end(),
1188 integer_reason.begin(), integer_reason.end());
1191 integer_trail_.push_back({IntegerValue(0),
1203 const int num_vars = vars_.
size();
1204 return (!integer_search_levels_.empty() &&
1205 integer_trail_.size() - integer_search_levels_.back() >
1214 ++num_decisions_to_break_loop_;
1215 std::vector<IntegerVariable> vars;
1216 for (
int i = integer_search_levels_.back(); i < integer_trail_.size(); ++i) {
1217 const IntegerVariable
var = integer_trail_[i].var;
1220 vars.push_back(
var);
1223 std::sort(vars.begin(), vars.end());
1224 IntegerVariable best_var = vars[0];
1227 for (
int i = 1; i < vars.size(); ++i) {
1228 if (vars[i] != vars[i - 1]) {
1232 if (count > best_count) {
1242 return first_level_without_full_propagation_ != -1;
1246 for (IntegerVariable
var(0);
var < vars_.
size();
var += 2) {
1253 bool IntegerTrail::EnqueueInternal(
1255 absl::Span<const Literal> literal_reason,
1256 absl::Span<const IntegerLiteral> integer_reason,
1257 int trail_index_with_same_reason) {
1258 DCHECK(lazy_reason !=
nullptr ||
1259 ReasonIsValid(literal_reason, integer_reason));
1261 const IntegerVariable
var(i_lit.
var);
1269 if (i_lit.
bound <= vars_[
var].current_bound)
return true;
1278 if ((*domains_)[
var].NumIntervals() > 1) {
1279 const auto& domain = (*domains_)[
var];
1281 const int size = domain.NumIntervals();
1282 while (index < size && i_lit.bound > domain[
index].end) {
1285 if (
index == size) {
1288 var_to_current_lb_interval_index_.
Set(
var,
index);
1299 Literal(is_ignored_literals_[
var]))) {
1302 auto* conflict = InitializeConflict(i_lit, lazy_reason, literal_reason,
1305 conflict->push_back(Literal(is_ignored_literals_[
var]));
1308 const int trail_index = FindLowestTrailIndexThatExplainBound(ub_reason);
1309 const int num_vars = vars_.
size();
1310 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
1312 MergeReasonIntoInternal(conflict);
1319 const Literal is_ignored = Literal(is_ignored_literals_[
var]);
1320 if (integer_search_levels_.empty()) {
1327 if (lazy_reason !=
nullptr) {
1328 lazy_reason(i_lit, integer_trail_.size(), &lazy_reason_literals_,
1329 &lazy_reason_trail_indices_);
1330 std::vector<IntegerLiteral> temp;
1331 for (
const int trail_index : lazy_reason_trail_indices_) {
1332 const TrailEntry& entry = integer_trail_[trail_index];
1333 temp.push_back(IntegerLiteral(entry.var, entry.bound));
1341 bounds_reason_buffer_.push_back(ub_reason);
1361 if (i_lit.
bound - lb < (ub - lb) / 2) {
1362 if (first_level_without_full_propagation_ == -1) {
1370 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1371 bitset->Set(i_lit.
var);
1374 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1375 literal_reason.empty() && lazy_reason ==
nullptr &&
1376 trail_index_with_same_reason >= integer_trail_.size()) {
1377 integer_literal_to_fix_.push_back(i_lit);
1393 const LiteralIndex literal_index =
1396 const Literal to_enqueue = Literal(literal_index);
1398 auto* conflict = InitializeConflict(i_lit, lazy_reason, literal_reason,
1400 conflict->push_back(to_enqueue);
1401 MergeReasonIntoInternal(conflict);
1411 EnqueueLiteralInternal(to_enqueue, lazy_reason, literal_reason,
1414 return EnqueueAssociatedIntegerLiteral(i_lit, to_enqueue);
1418 if (integer_search_levels_.empty()) {
1424 const int trail_index = trail_->
Index();
1425 if (trail_index >= boolean_trail_index_to_integer_one_.size()) {
1426 boolean_trail_index_to_integer_one_.resize(trail_index + 1);
1428 boolean_trail_index_to_integer_one_[trail_index] =
1429 trail_index_with_same_reason;
1436 if (integer_search_levels_.empty()) {
1437 ++num_level_zero_enqueues_;
1438 vars_[i_lit.
var].current_bound = i_lit.
bound;
1439 integer_trail_[i_lit.
var.value()].bound = i_lit.
bound;
1450 int reason_index = literals_reason_starts_.size();
1451 if (lazy_reason !=
nullptr) {
1452 if (integer_trail_.size() >= lazy_reasons_.size()) {
1453 lazy_reasons_.resize(integer_trail_.size() + 1,
nullptr);
1455 lazy_reasons_[integer_trail_.size()] = lazy_reason;
1457 }
else if (trail_index_with_same_reason >= integer_trail_.size()) {
1459 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1460 if (!literal_reason.empty()) {
1461 literals_reason_buffer_.insert(literals_reason_buffer_.end(),
1462 literal_reason.begin(),
1463 literal_reason.end());
1465 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1466 if (!integer_reason.empty()) {
1467 bounds_reason_buffer_.insert(bounds_reason_buffer_.end(),
1468 integer_reason.begin(),
1469 integer_reason.end());
1472 reason_index = integer_trail_[trail_index_with_same_reason].reason_index;
1475 const int prev_trail_index = vars_[i_lit.
var].current_trail_index;
1476 integer_trail_.push_back({i_lit.
bound,
1481 vars_[i_lit.
var].current_bound = i_lit.
bound;
1482 vars_[i_lit.
var].current_trail_index = integer_trail_.size() - 1;
1486 bool IntegerTrail::EnqueueAssociatedIntegerLiteral(IntegerLiteral i_lit,
1487 Literal literal_reason) {
1488 DCHECK(ReasonIsValid({literal_reason.Negated()}, {}));
1492 if (i_lit.bound <= vars_[i_lit.var].current_bound)
return true;
1500 return Enqueue(i_lit, {literal_reason.Negated()}, {});
1504 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1505 bitset->Set(i_lit.var);
1509 if (integer_search_levels_.empty()) {
1510 vars_[i_lit.var].current_bound = i_lit.bound;
1511 integer_trail_[i_lit.var.value()].bound = i_lit.bound;
1522 const int reason_index = literals_reason_starts_.size();
1523 CHECK_EQ(reason_index, bounds_reason_starts_.size());
1524 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1525 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1526 literals_reason_buffer_.push_back(literal_reason.Negated());
1528 const int prev_trail_index = vars_[i_lit.var].current_trail_index;
1529 integer_trail_.push_back({i_lit.bound,
1534 vars_[i_lit.var].current_bound = i_lit.bound;
1535 vars_[i_lit.var].current_trail_index = integer_trail_.size() - 1;
1539 void IntegerTrail::ComputeLazyReasonIfNeeded(
int trail_index)
const {
1540 const int reason_index = integer_trail_[trail_index].reason_index;
1541 if (reason_index == -1) {
1542 const TrailEntry& entry = integer_trail_[trail_index];
1543 const IntegerLiteral
literal(entry.var, entry.bound);
1544 lazy_reasons_[trail_index](
literal, trail_index, &lazy_reason_literals_,
1545 &lazy_reason_trail_indices_);
1549 absl::Span<const int> IntegerTrail::Dependencies(
int trail_index)
const {
1550 const int reason_index = integer_trail_[trail_index].reason_index;
1551 if (reason_index == -1) {
1552 return absl::Span<const int>(lazy_reason_trail_indices_);
1555 const int start = bounds_reason_starts_[reason_index];
1556 const int end = reason_index + 1 < bounds_reason_starts_.size()
1557 ? bounds_reason_starts_[reason_index + 1]
1558 : bounds_reason_buffer_.size();
1559 if (start == end)
return {};
1566 if (end > trail_index_reason_buffer_.size()) {
1567 trail_index_reason_buffer_.resize(end, -1);
1569 if (trail_index_reason_buffer_[start] == -1) {
1570 int new_end = start;
1571 const int num_vars = vars_.
size();
1572 for (
int i = start; i < end; ++i) {
1574 FindLowestTrailIndexThatExplainBound(bounds_reason_buffer_[i]);
1575 if (dep >= num_vars) {
1576 trail_index_reason_buffer_[new_end++] = dep;
1579 return absl::Span<const int>(&trail_index_reason_buffer_[start],
1585 return absl::Span<const int>(&trail_index_reason_buffer_[start],
1590 void IntegerTrail::AppendLiteralsReason(
int trail_index,
1591 std::vector<Literal>* output)
const {
1593 const int reason_index = integer_trail_[trail_index].reason_index;
1594 if (reason_index == -1) {
1595 for (
const Literal l : lazy_reason_literals_) {
1596 if (!added_variables_[l.Variable()]) {
1597 added_variables_.
Set(l.Variable());
1598 output->push_back(l);
1604 const int start = literals_reason_starts_[reason_index];
1605 const int end = reason_index + 1 < literals_reason_starts_.size()
1606 ? literals_reason_starts_[reason_index + 1]
1607 : literals_reason_buffer_.size();
1608 for (
int i = start; i < end; ++i) {
1609 const Literal l = literals_reason_buffer_[i];
1610 if (!added_variables_[l.Variable()]) {
1611 added_variables_.
Set(l.Variable());
1612 output->push_back(l);
1618 std::vector<Literal> reason;
1626 std::vector<Literal>* output)
const {
1627 DCHECK(tmp_queue_.empty());
1628 const int num_vars = vars_.
size();
1630 const int trail_index = FindLowestTrailIndexThatExplainBound(
literal);
1634 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
1636 return MergeReasonIntoInternal(output);
1641 void IntegerTrail::MergeReasonIntoInternal(std::vector<Literal>* output)
const {
1644 DCHECK(std::all_of(tmp_var_to_trail_index_in_queue_.
begin(),
1645 tmp_var_to_trail_index_in_queue_.
end(),
1646 [](
int v) {
return v == 0; }));
1649 for (
const Literal l : *output) {
1650 added_variables_.
Set(l.Variable());
1655 for (
const int trail_index : tmp_queue_) {
1657 DCHECK_LT(trail_index, integer_trail_.size());
1658 const TrailEntry& entry = integer_trail_[trail_index];
1659 tmp_var_to_trail_index_in_queue_[entry.var] =
1660 std::max(tmp_var_to_trail_index_in_queue_[entry.var], trail_index);
1665 std::make_heap(tmp_queue_.begin(), tmp_queue_.end());
1670 tmp_to_clear_.clear();
1671 while (!tmp_queue_.empty()) {
1672 const int trail_index = tmp_queue_.front();
1673 const TrailEntry& entry = integer_trail_[trail_index];
1674 std::pop_heap(tmp_queue_.begin(), tmp_queue_.end());
1675 tmp_queue_.pop_back();
1680 if (tmp_var_to_trail_index_in_queue_[entry.var] != trail_index) {
1687 var_trail_index_cache_threshold_ = trail_index;
1692 const LiteralIndex associated_lit =
1694 IntegerVariable(entry.var), entry.bound));
1697 const int reason_index = integer_trail_[trail_index].reason_index;
1700 const int start = literals_reason_starts_[reason_index];
1701 const int end = reason_index + 1 < literals_reason_starts_.size()
1702 ? literals_reason_starts_[reason_index + 1]
1703 : literals_reason_buffer_.size();
1705 CHECK_EQ(literals_reason_buffer_[start],
1706 Literal(associated_lit).Negated());
1709 const int start = bounds_reason_starts_[reason_index];
1710 const int end = reason_index + 1 < bounds_reason_starts_.size()
1711 ? bounds_reason_starts_[reason_index + 1]
1712 : bounds_reason_buffer_.size();
1726 tmp_var_to_trail_index_in_queue_[entry.var] = 0;
1727 has_dependency_ =
false;
1729 ComputeLazyReasonIfNeeded(trail_index);
1730 AppendLiteralsReason(trail_index, output);
1731 for (
const int next_trail_index : Dependencies(trail_index)) {
1732 if (next_trail_index < 0)
break;
1733 DCHECK_LT(next_trail_index, trail_index);
1734 const TrailEntry& next_entry = integer_trail_[next_trail_index];
1740 const int index_in_queue =
1741 tmp_var_to_trail_index_in_queue_[next_entry.var];
1743 has_dependency_ =
true;
1744 if (next_trail_index > index_in_queue) {
1745 tmp_var_to_trail_index_in_queue_[next_entry.var] = next_trail_index;
1747 std::push_heap(tmp_queue_.begin(), tmp_queue_.end());
1752 if (!has_dependency_) {
1753 tmp_to_clear_.push_back(entry.var);
1754 tmp_var_to_trail_index_in_queue_[entry.var] =
1760 for (
const IntegerVariable
var : tmp_to_clear_) {
1761 tmp_var_to_trail_index_in_queue_[
var] = 0;
1766 int trail_index)
const {
1767 const int index = boolean_trail_index_to_integer_one_[trail_index];
1771 ComputeLazyReasonIfNeeded(
index);
1772 AppendLiteralsReason(
index, reason);
1773 DCHECK(tmp_queue_.empty());
1774 for (
const int prev_trail_index : Dependencies(
index)) {
1775 if (prev_trail_index < 0)
break;
1777 tmp_queue_.push_back(prev_trail_index);
1779 MergeReasonIntoInternal(reason);
1789 const int end = vars_.
size();
1790 for (
int i = integer_trail_.size(); --i >= end;) {
1791 const TrailEntry& entry = integer_trail_[i];
1793 if (tmp_marked_[entry.var])
continue;
1795 tmp_marked_.
Set(entry.var);
1812 &id_to_greatest_common_level_since_last_call_);
1814 queue_by_priority_.resize(2);
1817 void GenericLiteralWatcher::UpdateCallingNeeds(
Trail* trail) {
1819 while (propagation_trail_index_ < trail->
Index()) {
1821 if (
literal.Index() >= literal_to_watcher_.
size())
continue;
1822 for (
const auto entry : literal_to_watcher_[
literal.Index()]) {
1823 if (!in_queue_[entry.id]) {
1824 in_queue_[entry.id] =
true;
1825 queue_by_priority_[id_to_priority_[entry.id]].push_back(entry.id);
1827 if (entry.watch_index >= 0) {
1828 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
1835 if (
var.value() >= var_to_watcher_.
size())
continue;
1836 for (
const auto entry : var_to_watcher_[
var]) {
1837 if (!in_queue_[entry.id]) {
1838 in_queue_[entry.id] =
true;
1839 queue_by_priority_[id_to_priority_[entry.id]].push_back(entry.id);
1841 if (entry.watch_index >= 0) {
1842 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
1848 const std::vector<IntegerVariable>& modified_vars =
1850 for (
const auto&
callback : level_zero_modified_variable_callback_) {
1863 for (
const int id : propagator_ids_to_call_at_level_zero_) {
1864 if (in_queue_[
id])
continue;
1865 in_queue_[id] =
true;
1866 queue_by_priority_[id_to_priority_[id]].push_back(
id);
1870 UpdateCallingNeeds(trail);
1875 for (
int priority = 0; priority < queue_by_priority_.size(); ++priority) {
1882 if (test_limit > 100) {
1887 std::deque<int>& queue = queue_by_priority_[priority];
1888 while (!queue.empty()) {
1889 const int id = queue.front();
1897 id_to_greatest_common_level_since_last_call_[IdType(
id)];
1898 const int high = id_to_level_at_last_call_[id];
1899 if (low < high || level > low) {
1900 id_to_level_at_last_call_[id] = level;
1901 id_to_greatest_common_level_since_last_call_.
MutableRef(IdType(
id)) =
1904 if (low < high) rev->SetLevel(low);
1905 if (level > low) rev->SetLevel(level);
1907 for (
int* rev_int : id_to_reversible_ints_[
id]) {
1908 rev_int_repository_->
SaveState(rev_int);
1914 const int64_t old_integer_timestamp = integer_trail_->
num_enqueues();
1915 const int64_t old_boolean_timestamp = trail->
Index();
1918 std::vector<int>& watch_indices_ref = id_to_watch_indices_[id];
1920 watch_indices_ref.empty()
1921 ? watchers_[id]->Propagate()
1922 : watchers_[id]->IncrementalPropagate(watch_indices_ref);
1924 watch_indices_ref.clear();
1925 in_queue_[id] =
false;
1931 if (id_to_idempotence_[
id]) {
1935 UpdateCallingNeeds(trail);
1936 watch_indices_ref.clear();
1937 in_queue_[id] =
false;
1942 watch_indices_ref.clear();
1943 in_queue_[id] =
false;
1944 UpdateCallingNeeds(trail);
1950 if (trail->
Index() > old_boolean_timestamp) {
1962 if (integer_trail_->
num_enqueues() > old_integer_timestamp) {
1980 for (std::deque<int>& queue : queue_by_priority_) {
1981 for (
const int id : queue) {
1982 id_to_watch_indices_[id].clear();
1992 in_queue_.assign(watchers_.size(),
false);
1997 const int id = watchers_.size();
1998 watchers_.push_back(propagator);
1999 id_to_level_at_last_call_.push_back(0);
2000 id_to_greatest_common_level_since_last_call_.
GrowByOne();
2001 id_to_reversible_classes_.push_back(std::vector<ReversibleInterface*>());
2002 id_to_reversible_ints_.push_back(std::vector<int*>());
2003 id_to_watch_indices_.push_back(std::vector<int>());
2004 id_to_priority_.push_back(1);
2005 id_to_idempotence_.push_back(
true);
2014 in_queue_.push_back(
true);
2015 queue_by_priority_[1].push_back(
id);
2020 id_to_priority_[id] = priority;
2021 if (priority >= queue_by_priority_.size()) {
2022 queue_by_priority_.resize(priority + 1);
2028 id_to_idempotence_[id] =
false;
2032 propagator_ids_to_call_at_level_zero_.push_back(
id);
2037 id_to_reversible_classes_[id].push_back(rev);
2041 id_to_reversible_ints_[id].push_back(rev);
2045 std::function<void(
Model*)>
2053 std::vector<Literal> clause_to_exclude_solution;
2054 clause_to_exclude_solution.reserve(current_level);
2055 for (
int i = 0; i < current_level; ++i) {
2056 bool include_decision =
true;
2062 encoder->GetIntegerLiterals(decision);
2064 if (integer_trail->IsCurrentlyIgnored(
bound.var)) {
2070 clause_to_exclude_solution.push_back(
2071 integer_trail->IsIgnoredLiteral(
bound.var).Negated());
2072 include_decision =
false;
2076 if (include_decision) {
2077 clause_to_exclude_solution.push_back(decision.
Negated());
std::function< void(IntegerLiteral literal_to_explain, int trail_index_of_literal, std::vector< Literal > *literals, std::vector< int > *dependencies)> LazyReasonFunction
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
void SetLevel(int level) final
LiteralIndex SearchForLiteralAtOrBefore(IntegerLiteral i, IntegerValue *bound) const
void Set(IntegerType index)
#define CHECK_GE(val1, val2)
LiteralIndex OptionalLiteralIndex(IntegerVariable i) const
void RegisterWatcher(SparseBitset< IntegerVariable > *p)
Class that owns everything related to a particular optimization model.
IntegerVariable NumIntegerVariables() const
bool IntegerLiteralIsFalse(IntegerLiteral l) const
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
bool VariableIsFullyEncoded(IntegerVariable var) const
const mapped_type & FindOrDie(key_type key) const
const Domain & InitialVariableDomain(IntegerVariable var) const
T & MutableRef(IndexType index)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< Literal > * MutableConflict()
#define VLOG(verboselevel)
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue LowerBound(IntegerVariable i) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
LiteralIndex Index() const
Represents a closed interval [start, end].
int64_t CapProd(int64_t x, int64_t y)
int64_t num_enqueues() const
::operations_research::sat::SatParameters_SearchBranching search_branching() const
bool AddUnitClause(Literal true_literal)
#define DCHECK_GT(val1, val2)
const std::vector< Decision > & Decisions() const
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
bool AddBinaryClause(Literal a, Literal b)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
int64_t Max() const
Returns the max value of the domain.
std::vector< Literal > ReasonFor(IntegerLiteral literal) const
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
bool CurrentBranchHadAnIncompletePropagation()
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
#define CHECK_LT(val1, val2)
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
void Enqueue(Literal true_literal, int propagator_id)
IntegerVariable PositiveVariable(IntegerVariable i)
void EnqueueWithUnitReason(Literal true_literal)
Literal GetFalseLiteral()
void AssociateToIntegerEqualValue(Literal literal, IntegerVariable var, IntegerValue value)
void FullyEncodeVariable(IntegerVariable var)
void Set(key_type key, mapped_type value)
void resize(size_type new_size)
LiteralIndex GetAssociatedLiteral(IntegerLiteral i_lit) const
int64_t Min() const
Returns the min value of the domain.
absl::InlinedVector< IntegerLiteral, 2 > InlinedIntegerLiteralVector
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
#define CHECK_LE(val1, val2)
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
void RegisterReversibleClass(int id, ReversibleInterface *rev)
int NumConstantVariables() const
IntegerVariable AddIntegerVariable()
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
void SetPropagatorPriority(int id, int priority)
bool LiteralIsAssigned(Literal literal) const
void AlwaysCallAtLevelZero(int id)
void push_back(const value_type &x)
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
bool IsFixed(IntegerTrail *integer_trail) const
bool VariableIsPositive(IntegerVariable i)
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
bool InPropagationLoop() const
void Backtrack(int target_level)
void MergeReasonInto(absl::Span< const IntegerLiteral > literals, std::vector< Literal > *output) const
bool AddClauseDuringSearch(absl::Span< const Literal > literals)
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
#define DCHECK_GE(val1, val2)
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
bool IsOptional(IntegerVariable i) const
#define CHECK_EQ(val1, val2)
LiteralIndex GetAssociatedEqualityLiteral(IntegerVariable var, IntegerValue value) const
ABSL_MUST_USE_RESULT bool ConditionalEnqueue(Literal lit, IntegerLiteral i_lit, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason)
void ClearNewlyFixedIntegerLiterals()
bool Propagate(Trail *trail) final
BooleanVariable NewBooleanVariable()
IntegerVariable GetOrCreateConstantIntegerVariable(IntegerValue value)
std::function< void(Model *)> ExcludeCurrentSolutionWithoutIgnoredVariableAndBacktrack()
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
const VariablesAssignment & Assignment() const
#define DCHECK(condition)
We call domain any subset of Int64 = [kint64min, kint64max].
void RegisterReversibleClass(ReversibleInterface *rev)
bool Contains(int64_t value) const
Returns true iff value is in Domain.
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
std::string DebugString() const
#define DCHECK_EQ(val1, val2)
void ClearAndResize(IntegerType size)
bool IsCurrentlyIgnored(IntegerVariable i) const
bool log_search_progress() const
bool LiteralIsAssociated(IntegerLiteral i_lit) const
#define DCHECK_LE(val1, val2)
int Register(PropagatorInterface *propagator)
void Untrail(const Trail &trail, int literal_trail_index) final
IntegerValue UpperBound(IntegerVariable i) const
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
int CurrentDecisionLevel() const
static constexpr SearchBranching FIXED_SEARCH
std::vector< ValueLiteralPair > RawDomainEncoding(IntegerVariable var) const
Collection of objects used to extend the Constraint Solver library.
std::vector< ValueLiteralPair > PartialDomainEncoding(IntegerVariable var) const
void AddAllImplicationsBetweenAssociatedLiterals()
const IntegerVariable kNoIntegerVariable(-1)
Literal IsIgnoredLiteral(IntegerVariable i) const
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
void SaveState(T *object)
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
void reserve(size_type n)
const LiteralIndex kNoLiteralIndex(-1)
const VariablesAssignment & Assignment() const
int propagation_trail_index_
void ReserveSpaceForNumVariables(int num_vars)
IntegerVariable FirstUnassignedVariable() const
void Untrail(const Trail &trail, int literal_trail_index) final
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
IntegerValue Max(IntegerTrail *integer_trail) const
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
IntegerValue Min(IntegerTrail *integer_trail) const
bool IsEmpty() const
Returns true if this is the empty set.
int CurrentDecisionLevel() const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
GenericLiteralWatcher(Model *model)
bool Propagate(Trail *trail) final
#define CHECK_NE(val1, val2)
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
const std::vector< IntegerLiteral > NewlyFixedIntegerLiterals() const
bool IsFixed(IntegerVariable i) const
const AssignmentInfo & Info(BooleanVariable var) const
void AssociateToIntegerLiteral(Literal literal, IntegerLiteral i_lit)
#define DCHECK_LT(val1, val2)
void RegisterReversibleInt(int id, int *rev)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)