OR-Tools  9.3
integer.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#include "ortools/sat/integer.h"
15
16#include <algorithm>
17#include <cstdint>
18#include <deque>
19#include <functional>
20#include <limits>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/container/btree_map.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/meta/type_traits.h"
28#include "absl/strings/str_cat.h"
29#include "absl/types/span.h"
33#include "ortools/sat/model.h"
35#include "ortools/sat/sat_parameters.pb.h"
37#include "ortools/util/bitset.h"
38#include "ortools/util/rev.h"
43
44namespace operations_research {
45namespace sat {
46
47std::vector<IntegerVariable> NegationOf(
48 const std::vector<IntegerVariable>& vars) {
49 std::vector<IntegerVariable> result(vars.size());
50 for (int i = 0; i < vars.size(); ++i) {
51 result[i] = NegationOf(vars[i]);
52 }
53 return result;
54}
55
56std::string ValueLiteralPair::DebugString() const {
57 return absl::StrCat("(literal = ", literal.DebugString(),
58 ", value = ", value.value(), ")");
59}
60
61std::ostream& operator<<(std::ostream& os, const ValueLiteralPair& p) {
62 os << p.DebugString();
63 return os;
64}
65
67 if (VariableIsFullyEncoded(var)) return;
68
69 CHECK_EQ(0, sat_solver_->CurrentDecisionLevel());
70 CHECK(!(*domains_)[var].IsEmpty()); // UNSAT. We don't deal with that here.
71 CHECK_LT((*domains_)[var].Size(), 100000)
72 << "Domain too large for full encoding.";
73
74 // TODO(user): Maybe we can optimize the literal creation order and their
75 // polarity as our default SAT heuristics initially depends on this.
76 //
77 // TODO(user): Currently, in some corner cases,
78 // GetOrCreateLiteralAssociatedToEquality() might trigger some propagation
79 // that update the domain of var, so we need to cache the values to not read
80 // garbage. Note that it is okay to call the function on values no longer
81 // reachable, as this will just do nothing.
82 tmp_values_.clear();
83 for (const int64_t v : (*domains_)[var].Values()) {
84 tmp_values_.push_back(IntegerValue(v));
85 }
86 for (const IntegerValue v : tmp_values_) {
88 }
89
90 // Mark var and Negation(var) as fully encoded.
91 CHECK_LT(GetPositiveOnlyIndex(var), is_fully_encoded_.size());
92 CHECK(!equality_by_var_[GetPositiveOnlyIndex(var)].empty());
93 is_fully_encoded_[GetPositiveOnlyIndex(var)] = true;
94}
95
96bool IntegerEncoder::VariableIsFullyEncoded(IntegerVariable var) const {
97 const PositiveOnlyIndex index = GetPositiveOnlyIndex(var);
98 if (index >= is_fully_encoded_.size()) return false;
99
100 // Once fully encoded, the status never changes.
101 if (is_fully_encoded_[index]) return true;
103
104 // TODO(user): Cache result as long as equality_by_var_[index] is unchanged?
105 // It might not be needed since if the variable is not fully encoded, then
106 // PartialDomainEncoding() will filter unreachable values, and so the size
107 // check will be false until further value have been encoded.
108 const int64_t initial_domain_size = (*domains_)[var].Size();
109 if (equality_by_var_[index].size() < initial_domain_size) return false;
110
111 // This cleans equality_by_var_[index] as a side effect and in particular,
112 // sorts it by values.
114
115 // TODO(user): Comparing the size might be enough, but we want to be always
116 // valid even if either (*domains_[var]) or PartialDomainEncoding(var) are
117 // not properly synced because the propagation is not finished.
118 const auto& ref = equality_by_var_[index];
119 int i = 0;
120 for (const int64_t v : (*domains_)[var].Values()) {
121 if (i < ref.size() && v == ref[i].value) {
122 i++;
123 }
124 }
125 if (i == ref.size()) {
126 is_fully_encoded_[index] = true;
127 }
128 return is_fully_encoded_[index];
129}
130
131std::vector<ValueLiteralPair> IntegerEncoder::FullDomainEncoding(
132 IntegerVariable var) const {
135}
136
137std::vector<ValueLiteralPair> IntegerEncoder::PartialDomainEncoding(
138 IntegerVariable var) const {
139 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
140 const PositiveOnlyIndex index = GetPositiveOnlyIndex(var);
141 if (index >= equality_by_var_.size()) return {};
142
143 int new_size = 0;
144 std::vector<ValueLiteralPair>& ref = equality_by_var_[index];
145 for (int i = 0; i < ref.size(); ++i) {
146 const ValueLiteralPair pair = ref[i];
147 if (sat_solver_->Assignment().LiteralIsFalse(pair.literal)) continue;
148 if (sat_solver_->Assignment().LiteralIsTrue(pair.literal)) {
149 ref.clear();
150 ref.push_back(pair);
151 new_size = 1;
152 break;
153 }
154 ref[new_size++] = pair;
155 }
156 ref.resize(new_size);
157 std::sort(ref.begin(), ref.end(), ValueLiteralPair::CompareByValue());
158
159 std::vector<ValueLiteralPair> result = ref;
160 if (!VariableIsPositive(var)) {
161 std::reverse(result.begin(), result.end());
162 for (ValueLiteralPair& ref : result) ref.value = -ref.value;
163 }
164 return result;
165}
166
167std::vector<ValueLiteralPair> IntegerEncoder::RawDomainEncoding(
168 IntegerVariable var) const {
170 const PositiveOnlyIndex index = GetPositiveOnlyIndex(var);
171 if (index >= equality_by_var_.size()) return {};
172
173 return equality_by_var_[index];
174}
175
176// Note that by not inserting the literal in "order" we can in the worst case
177// use twice as much implication (2 by literals) instead of only one between
178// consecutive literals.
179void IntegerEncoder::AddImplications(
180 const absl::btree_map<IntegerValue, Literal>& map,
181 absl::btree_map<IntegerValue, Literal>::const_iterator it,
182 Literal associated_lit) {
183 if (!add_implications_) return;
184 DCHECK_EQ(it->second, associated_lit);
185
186 // Literal(after) => associated_lit
187 auto after_it = it;
188 ++after_it;
189 if (after_it != map.end()) {
190 sat_solver_->AddClauseDuringSearch(
191 {after_it->second.Negated(), associated_lit});
192 }
193
194 // associated_lit => Literal(before)
195 if (it != map.begin()) {
196 auto before_it = it;
197 --before_it;
198 sat_solver_->AddClauseDuringSearch(
199 {associated_lit.Negated(), before_it->second});
200 }
201}
202
204 CHECK_EQ(0, sat_solver_->CurrentDecisionLevel());
205 add_implications_ = true;
206 for (const absl::btree_map<IntegerValue, Literal>& encoding :
207 encoding_by_var_) {
208 LiteralIndex previous = kNoLiteralIndex;
209 for (const auto value_literal : encoding) {
210 const Literal lit = value_literal.second;
211 if (previous != kNoLiteralIndex) {
212 // lit => previous.
213 sat_solver_->AddBinaryClause(lit.Negated(), Literal(previous));
214 }
215 previous = lit.Index();
216 }
217 }
218}
219
220std::pair<IntegerLiteral, IntegerLiteral> IntegerEncoder::Canonicalize(
221 IntegerLiteral i_lit) const {
222 const IntegerVariable var(i_lit.var);
223 IntegerValue after(i_lit.bound);
224 IntegerValue before(i_lit.bound - 1);
225 CHECK_GE(before, (*domains_)[var].Min());
226 CHECK_LE(after, (*domains_)[var].Max());
227 int64_t previous = std::numeric_limits<int64_t>::min();
228 for (const ClosedInterval& interval : (*domains_)[var]) {
229 if (before > previous && before < interval.start) before = previous;
230 if (after > previous && after < interval.start) after = interval.start;
231 if (after <= interval.end) break;
232 previous = interval.end;
233 }
234 return {IntegerLiteral::GreaterOrEqual(var, after),
236}
237
239 if (i_lit.bound <= (*domains_)[i_lit.var].Min()) {
240 return GetTrueLiteral();
241 }
242 if (i_lit.bound > (*domains_)[i_lit.var].Max()) {
243 return GetFalseLiteral();
244 }
245
246 const auto canonicalization = Canonicalize(i_lit);
247 const IntegerLiteral new_lit = canonicalization.first;
248
249 const LiteralIndex index = GetAssociatedLiteral(new_lit);
250 if (index != kNoLiteralIndex) return Literal(index);
251 const LiteralIndex n_index = GetAssociatedLiteral(canonicalization.second);
252 if (n_index != kNoLiteralIndex) return Literal(n_index).Negated();
253
254 ++num_created_variables_;
255 const Literal literal(sat_solver_->NewBooleanVariable(), true);
257
258 // TODO(user): on some problem this happens. We should probably make sure that
259 // we don't create extra fixed Boolean variable for no reason.
260 if (sat_solver_->Assignment().LiteralIsAssigned(literal)) {
261 VLOG(1) << "Created a fixed literal for no reason!";
262 }
263 return literal;
264}
265
266namespace {
267std::pair<PositiveOnlyIndex, IntegerValue> PositiveVarKey(IntegerVariable var,
268 IntegerValue value) {
269 return std::make_pair(GetPositiveOnlyIndex(var),
271}
272} // namespace
273
275 IntegerVariable var, IntegerValue value) const {
276 const auto it =
277 equality_to_associated_literal_.find(PositiveVarKey(var, value));
278 if (it != equality_to_associated_literal_.end()) {
279 return it->second.Index();
280 }
281 return kNoLiteralIndex;
282}
283
285 IntegerVariable var, IntegerValue value) {
286 {
287 const auto it =
288 equality_to_associated_literal_.find(PositiveVarKey(var, value));
289 if (it != equality_to_associated_literal_.end()) {
290 return it->second;
291 }
292 }
293
294 // Check for trivial true/false literal to avoid creating variable for no
295 // reasons.
296 const Domain& domain = (*domains_)[var];
297 if (!domain.Contains(value.value())) return GetFalseLiteral();
298 if (value == domain.Min() && value == domain.Max()) {
300 return GetTrueLiteral();
301 }
302
303 ++num_created_variables_;
304 const Literal literal(sat_solver_->NewBooleanVariable(), true);
306
307 // TODO(user): this happens on some problem. We should probably
308 // make sure that we don't create extra fixed Boolean variable for no reason.
309 // Note that here we could detect the case before creating the literal. The
310 // initial domain didn't contain it, but maybe the one of (>= value) or (<=
311 // value) is false?
312 if (sat_solver_->Assignment().LiteralIsAssigned(literal)) {
313 VLOG(1) << "Created a fixed literal for no reason!";
314 }
315 return literal;
316}
317
319 IntegerLiteral i_lit) {
320 const auto& domain = (*domains_)[i_lit.var];
321 const IntegerValue min(domain.Min());
322 const IntegerValue max(domain.Max());
323 if (i_lit.bound <= min) {
324 sat_solver_->AddUnitClause(literal);
325 } else if (i_lit.bound > max) {
326 sat_solver_->AddUnitClause(literal.Negated());
327 } else {
328 const auto pair = Canonicalize(i_lit);
329 HalfAssociateGivenLiteral(pair.first, literal);
330 HalfAssociateGivenLiteral(pair.second, literal.Negated());
331
332 // Detect the case >= max or <= min and properly register them. Note that
333 // both cases will happen at the same time if there is just two possible
334 // value in the domain.
335 if (pair.first.bound == max) {
337 }
338 if (-pair.second.bound == min) {
339 AssociateToIntegerEqualValue(literal.Negated(), i_lit.var, min);
340 }
341 }
342}
343
345 IntegerVariable var,
346 IntegerValue value) {
347 // Detect literal view. Note that the same literal can be associated to more
348 // than one variable, and thus already have a view. We don't change it in
349 // this case.
350 const Domain& domain = (*domains_)[var];
351 if (value == 1 && domain.Min() >= 0 && domain.Max() <= 1) {
352 if (literal.Index() >= literal_view_.size()) {
353 literal_view_.resize(literal.Index().value() + 1, kNoIntegerVariable);
354 literal_view_[literal.Index()] = var;
355 } else if (literal_view_[literal.Index()] == kNoIntegerVariable) {
356 literal_view_[literal.Index()] = var;
357 }
358 }
359 if (value == -1 && domain.Min() >= -1 && domain.Max() <= 0) {
360 if (literal.Index() >= literal_view_.size()) {
361 literal_view_.resize(literal.Index().value() + 1, kNoIntegerVariable);
362 literal_view_[literal.Index()] = NegationOf(var);
363 } else if (literal_view_[literal.Index()] == kNoIntegerVariable) {
364 literal_view_[literal.Index()] = NegationOf(var);
365 }
366 }
367
368 // We use the "do not insert if present" behavior of .insert() to do just one
369 // lookup.
370 const auto insert_result = equality_to_associated_literal_.insert(
371 {PositiveVarKey(var, value), literal});
372 if (!insert_result.second) {
373 // If this key is already associated, make the two literals equal.
374 const Literal representative = insert_result.first->second;
375 if (representative != literal) {
376 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
377 sat_solver_->AddClauseDuringSearch({literal, representative.Negated()});
378 sat_solver_->AddClauseDuringSearch({literal.Negated(), representative});
379 }
380 return;
381 }
382
383 // Fix literal for value outside the domain.
384 if (!domain.Contains(value.value())) {
385 sat_solver_->AddUnitClause(literal.Negated());
386 return;
387 }
388
389 // Update equality_by_var. Note that due to the
390 // equality_to_associated_literal_ hash table, there should never be any
391 // duplicate values for a given variable.
392 const PositiveOnlyIndex index = GetPositiveOnlyIndex(var);
393 if (index >= equality_by_var_.size()) {
394 equality_by_var_.resize(index.value() + 1);
395 is_fully_encoded_.resize(index.value() + 1);
396 }
397 equality_by_var_[index].push_back(
399
400 // Fix literal for constant domain.
401 if (value == domain.Min() && value == domain.Max()) {
402 sat_solver_->AddUnitClause(literal);
403 return;
404 }
405
408
409 // Special case for the first and last value.
410 if (value == domain.Min()) {
411 // Note that this will recursively call AssociateToIntegerEqualValue() but
412 // since equality_to_associated_literal_[] is now set, the recursion will
413 // stop there. When a domain has just 2 values, this allows to call just
414 // once AssociateToIntegerEqualValue() and also associate the other value to
415 // the negation of the given literal.
417 return;
418 }
419 if (value == domain.Max()) {
421 return;
422 }
423
424 // (var == value) <=> (var >= value) and (var <= value).
427 sat_solver_->AddClauseDuringSearch({a, literal.Negated()});
428 sat_solver_->AddClauseDuringSearch({b, literal.Negated()});
429 sat_solver_->AddClauseDuringSearch({a.Negated(), b.Negated(), literal});
430
431 // Update reverse encoding.
432 const int new_size = 1 + literal.Index().value();
433 if (new_size > full_reverse_encoding_.size()) {
434 full_reverse_encoding_.resize(new_size);
435 }
436 full_reverse_encoding_[literal.Index()].push_back(le);
437 full_reverse_encoding_[literal.Index()].push_back(ge);
438}
439
440// TODO(user): The hard constraints we add between associated literals seems to
441// work for optional variables, but I am not 100% sure why!! I think it works
442// because these literals can only appear in a conflict if the presence literal
443// of the optional variables is true.
444void IntegerEncoder::HalfAssociateGivenLiteral(IntegerLiteral i_lit,
446 // Resize reverse encoding.
447 const int new_size = 1 + literal.Index().value();
448 if (new_size > reverse_encoding_.size()) {
449 reverse_encoding_.resize(new_size);
450 }
451 if (new_size > full_reverse_encoding_.size()) {
452 full_reverse_encoding_.resize(new_size);
453 }
454
455 // Associate the new literal to i_lit.
456 if (i_lit.var >= encoding_by_var_.size()) {
457 encoding_by_var_.resize(i_lit.var.value() + 1);
458 }
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) { // New item.
462 AddImplications(var_encoding, insert_result.first, literal);
463 if (sat_solver_->Assignment().LiteralIsTrue(literal)) {
464 if (sat_solver_->CurrentDecisionLevel() == 0) {
465 newly_fixed_integer_literals_.push_back(i_lit);
466 }
467 }
468
469 // TODO(user): do that for the other branch too?
470 reverse_encoding_[literal.Index()].push_back(i_lit);
471 full_reverse_encoding_[literal.Index()].push_back(i_lit);
472 } else {
473 const Literal associated(insert_result.first->second);
474 if (associated != literal) {
475 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
476 sat_solver_->AddClauseDuringSearch({literal, associated.Negated()});
477 sat_solver_->AddClauseDuringSearch({literal.Negated(), associated});
478 }
479 }
480}
481
483 if (i.var >= encoding_by_var_.size()) return false;
484 const absl::btree_map<IntegerValue, Literal>& encoding =
485 encoding_by_var_[i.var];
486 return encoding.find(i.bound) != encoding.end();
487}
488
490 if (i.var >= encoding_by_var_.size()) return kNoLiteralIndex;
491 const absl::btree_map<IntegerValue, Literal>& encoding =
492 encoding_by_var_[i.var];
493 const auto result = encoding.find(i.bound);
494 if (result == encoding.end()) return kNoLiteralIndex;
495 return result->second.Index();
496}
497
499 IntegerLiteral i, IntegerValue* bound) const {
500 // We take the element before the upper_bound() which is either the encoding
501 // of i if it already exists, or the encoding just before it.
502 if (i.var >= encoding_by_var_.size()) return kNoLiteralIndex;
503 const absl::btree_map<IntegerValue, Literal>& encoding =
504 encoding_by_var_[i.var];
505 auto after_it = encoding.upper_bound(i.bound);
506 if (after_it == encoding.begin()) return kNoLiteralIndex;
507 --after_it;
508 *bound = after_it->first;
509 return after_it->second.Index();
510}
511
513 if (parameters_.log_search_progress() && num_decisions_to_break_loop_ > 0) {
514 VLOG(1) << "Num decisions to break propagation loop: "
515 << num_decisions_to_break_loop_;
516 }
517}
518
520 const int level = trail->CurrentDecisionLevel();
521 for (ReversibleInterface* rev : reversible_classes_) rev->SetLevel(level);
522
523 // Make sure that our internal "integer_search_levels_" size matches the
524 // sat decision levels. At the level zero, integer_search_levels_ should
525 // be empty.
526 if (level > integer_search_levels_.size()) {
527 integer_search_levels_.push_back(integer_trail_.size());
528 reason_decision_levels_.push_back(literals_reason_starts_.size());
529 CHECK_EQ(trail->CurrentDecisionLevel(), integer_search_levels_.size());
530 }
531
532 // This is used to map any integer literal out of the initial variable domain
533 // into one that use one of the domain value.
534 var_to_current_lb_interval_index_.SetLevel(level);
535
536 // This is required because when loading a model it is possible that we add
537 // (literal <-> integer literal) associations for literals that have already
538 // been propagated here. This often happens when the presolve is off
539 // and many variables are fixed.
540 //
541 // TODO(user): refactor the interaction IntegerTrail <-> IntegerEncoder so
542 // that we can just push right away such literal. Unfortunately, this is is
543 // a big chunck of work.
544 if (level == 0) {
545 for (const IntegerLiteral i_lit : encoder_->NewlyFixedIntegerLiterals()) {
546 if (IsCurrentlyIgnored(i_lit.var)) continue;
547 if (!Enqueue(i_lit, {}, {})) return false;
548 }
550
551 for (const IntegerLiteral i_lit : integer_literal_to_fix_) {
552 if (IsCurrentlyIgnored(i_lit.var)) continue;
553 if (!Enqueue(i_lit, {}, {})) return false;
554 }
555 integer_literal_to_fix_.clear();
556
557 for (const Literal lit : literal_to_fix_) {
558 if (trail_->Assignment().LiteralIsFalse(lit)) return false;
559 if (trail_->Assignment().LiteralIsTrue(lit)) continue;
560 trail_->EnqueueWithUnitReason(lit);
561 }
562 literal_to_fix_.clear();
563 }
564
565 // Process all the "associated" literals and Enqueue() the corresponding
566 // bounds.
567 while (propagation_trail_index_ < trail->Index()) {
568 const Literal literal = (*trail)[propagation_trail_index_++];
569 for (const IntegerLiteral i_lit : encoder_->GetIntegerLiterals(literal)) {
570 if (IsCurrentlyIgnored(i_lit.var)) continue;
571
572 // The reason is simply the associated literal.
573 if (!EnqueueAssociatedIntegerLiteral(i_lit, literal)) {
574 return false;
575 }
576 }
577 }
578
579 return true;
580}
581
582void IntegerTrail::Untrail(const Trail& trail, int literal_trail_index) {
583 ++num_untrails_;
584 conditional_lbs_.clear();
585 const int level = trail.CurrentDecisionLevel();
586 var_to_current_lb_interval_index_.SetLevel(level);
588 std::min(propagation_trail_index_, literal_trail_index);
589
590 if (level < first_level_without_full_propagation_) {
591 first_level_without_full_propagation_ = -1;
592 }
593
594 // Note that if a conflict was detected before Propagate() of this class was
595 // even called, it is possible that there is nothing to backtrack.
596 if (level >= integer_search_levels_.size()) return;
597 const int target = integer_search_levels_[level];
598 integer_search_levels_.resize(level);
599 CHECK_GE(target, vars_.size());
600 CHECK_LE(target, integer_trail_.size());
601
602 for (int index = integer_trail_.size() - 1; index >= target; --index) {
603 const TrailEntry& entry = integer_trail_[index];
604 if (entry.var < 0) continue; // entry used by EnqueueLiteral().
605 vars_[entry.var].current_trail_index = entry.prev_trail_index;
606 vars_[entry.var].current_bound =
607 integer_trail_[entry.prev_trail_index].bound;
608 }
609 integer_trail_.resize(target);
610
611 // Clear reason.
612 const int old_size = reason_decision_levels_[level];
613 reason_decision_levels_.resize(level);
614 if (old_size < literals_reason_starts_.size()) {
615 literals_reason_buffer_.resize(literals_reason_starts_[old_size]);
616
617 const int bound_start = bounds_reason_starts_[old_size];
618 bounds_reason_buffer_.resize(bound_start);
619 if (bound_start < trail_index_reason_buffer_.size()) {
620 trail_index_reason_buffer_.resize(bound_start);
621 }
622
623 literals_reason_starts_.resize(old_size);
624 bounds_reason_starts_.resize(old_size);
625 }
626
627 // We notify the new level once all variables have been restored to their
628 // old value.
629 for (ReversibleInterface* rev : reversible_classes_) rev->SetLevel(level);
630}
631
633 // Because we always create both a variable and its negation.
634 const int size = 2 * num_vars;
635 vars_.reserve(size);
636 is_ignored_literals_.reserve(size);
637 integer_trail_.reserve(size);
638 domains_->reserve(size);
639 var_trail_index_cache_.reserve(size);
640 tmp_var_to_trail_index_in_queue_.reserve(size);
641}
642
644 IntegerValue upper_bound) {
648 DCHECK(lower_bound >= 0 ||
650 DCHECK(integer_search_levels_.empty());
651 DCHECK_EQ(vars_.size(), integer_trail_.size());
652
653 const IntegerVariable i(vars_.size());
654 is_ignored_literals_.push_back(kNoLiteralIndex);
655 vars_.push_back({lower_bound, static_cast<int>(integer_trail_.size())});
656 integer_trail_.push_back({lower_bound, i});
657 domains_->push_back(Domain(lower_bound.value(), upper_bound.value()));
658
659 // TODO(user): the is_ignored_literals_ Booleans are currently always the same
660 // for a variable and its negation. So it may be better not to store it twice
661 // so that we don't have to be careful when setting them.
662 CHECK_EQ(NegationOf(i).value(), vars_.size());
663 is_ignored_literals_.push_back(kNoLiteralIndex);
664 vars_.push_back({-upper_bound, static_cast<int>(integer_trail_.size())});
665 integer_trail_.push_back({-upper_bound, NegationOf(i)});
666 domains_->push_back(Domain(-upper_bound.value(), -lower_bound.value()));
667
668 var_trail_index_cache_.resize(vars_.size(), integer_trail_.size());
669 tmp_var_to_trail_index_in_queue_.resize(vars_.size(), 0);
670
671 for (SparseBitset<IntegerVariable>* w : watchers_) {
672 w->Resize(NumIntegerVariables());
673 }
674 return i;
675}
676
677IntegerVariable IntegerTrail::AddIntegerVariable(const Domain& domain) {
678 CHECK(!domain.IsEmpty());
679 const IntegerVariable var = AddIntegerVariable(IntegerValue(domain.Min()),
680 IntegerValue(domain.Max()));
681 CHECK(UpdateInitialDomain(var, domain));
682 return var;
683}
684
685const Domain& IntegerTrail::InitialVariableDomain(IntegerVariable var) const {
686 return (*domains_)[var];
687}
688
689bool IntegerTrail::UpdateInitialDomain(IntegerVariable var, Domain domain) {
690 CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
691
692 const Domain& old_domain = InitialVariableDomain(var);
693 domain = domain.IntersectionWith(old_domain);
694 if (old_domain == domain) return true;
695
696 if (domain.IsEmpty()) return false;
697 (*domains_)[var] = domain;
698 (*domains_)[NegationOf(var)] = domain.Negation();
699 if (domain.NumIntervals() > 1) {
700 var_to_current_lb_interval_index_.Set(var, 0);
701 var_to_current_lb_interval_index_.Set(NegationOf(var), 0);
702 }
703
704 // TODO(user): That works, but it might be better to simply update the
705 // bounds here directly. This is because these function might call again
706 // UpdateInitialDomain(), and we will abort after realizing that the domain
707 // didn't change this time.
708 CHECK(Enqueue(IntegerLiteral::GreaterOrEqual(var, IntegerValue(domain.Min())),
709 {}, {}));
710 CHECK(Enqueue(IntegerLiteral::LowerOrEqual(var, IntegerValue(domain.Max())),
711 {}, {}));
712
713 // Set to false excluded literals.
714 int i = 0;
715 int num_fixed = 0;
716 for (const ValueLiteralPair pair : encoder_->PartialDomainEncoding(var)) {
717 while (i < domain.NumIntervals() && pair.value > domain[i].end) ++i;
718 if (i == domain.NumIntervals() || pair.value < domain[i].start) {
719 ++num_fixed;
720 if (trail_->Assignment().LiteralIsTrue(pair.literal)) return false;
721 if (!trail_->Assignment().LiteralIsFalse(pair.literal)) {
722 trail_->EnqueueWithUnitReason(pair.literal.Negated());
723 }
724 }
725 }
726 if (num_fixed > 0) {
727 VLOG(1)
728 << "Domain intersection fixed " << num_fixed
729 << " equality literal corresponding to values outside the new domain.";
730 }
731
732 return true;
733}
734
736 IntegerValue value) {
737 auto insert = constant_map_.insert(std::make_pair(value, kNoIntegerVariable));
738 if (insert.second) { // new element.
739 const IntegerVariable new_var = AddIntegerVariable(value, value);
740 insert.first->second = new_var;
741 if (value != 0) {
742 // Note that this might invalidate insert.first->second.
743 gtl::InsertOrDie(&constant_map_, -value, NegationOf(new_var));
744 }
745 return new_var;
746 }
747 return insert.first->second;
748}
749
751 // The +1 if for the special key zero (the only case when we have an odd
752 // number of entries).
753 return (constant_map_.size() + 1) / 2;
754}
755
757 int threshold) const {
758 // Optimization. We assume this is only called when computing a reason, so we
759 // can ignore this trail_index if we already need a more restrictive reason
760 // for this var.
761 const int index_in_queue = tmp_var_to_trail_index_in_queue_[var];
762 if (threshold <= index_in_queue) {
763 if (index_in_queue != std::numeric_limits<int32_t>::max())
764 has_dependency_ = true;
765 return -1;
766 }
767
768 DCHECK_GE(threshold, vars_.size());
769 int trail_index = vars_[var].current_trail_index;
770
771 // Check the validity of the cached index and use it if possible.
772 if (trail_index > threshold) {
773 const int cached_index = var_trail_index_cache_[var];
774 if (cached_index >= threshold && cached_index < trail_index &&
775 integer_trail_[cached_index].var == var) {
776 trail_index = cached_index;
777 }
778 }
779
780 while (trail_index >= threshold) {
781 trail_index = integer_trail_[trail_index].prev_trail_index;
782 if (trail_index >= var_trail_index_cache_threshold_) {
783 var_trail_index_cache_[var] = trail_index;
784 }
785 }
786
787 const int num_vars = vars_.size();
788 return trail_index < num_vars ? -1 : trail_index;
789}
790
791int IntegerTrail::FindLowestTrailIndexThatExplainBound(
792 IntegerLiteral i_lit) const {
793 DCHECK_LE(i_lit.bound, vars_[i_lit.var].current_bound);
794 if (i_lit.bound <= LevelZeroLowerBound(i_lit.var)) return -1;
795 int trail_index = vars_[i_lit.var].current_trail_index;
796
797 // Check the validity of the cached index and use it if possible. This caching
798 // mechanism is important in case of long chain of propagation on the same
799 // variable. Because during conflict resolution, we call
800 // FindLowestTrailIndexThatExplainBound() with lowest and lowest bound, this
801 // cache can transform a quadratic complexity into a linear one.
802 {
803 const int cached_index = var_trail_index_cache_[i_lit.var];
804 if (cached_index < trail_index) {
805 const TrailEntry& entry = integer_trail_[cached_index];
806 if (entry.var == i_lit.var && entry.bound >= i_lit.bound) {
807 trail_index = cached_index;
808 }
809 }
810 }
811
812 int prev_trail_index = trail_index;
813 while (true) {
814 if (trail_index >= var_trail_index_cache_threshold_) {
815 var_trail_index_cache_[i_lit.var] = trail_index;
816 }
817 const TrailEntry& entry = integer_trail_[trail_index];
818 if (entry.bound == i_lit.bound) return trail_index;
819 if (entry.bound < i_lit.bound) return prev_trail_index;
820 prev_trail_index = trail_index;
821 trail_index = entry.prev_trail_index;
822 }
823}
824
825// TODO(user): Get rid of this function and only keep the trail index one?
827 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
828 std::vector<IntegerLiteral>* reason) const {
829 CHECK_GE(slack, 0);
830 if (slack == 0) return;
831 const int size = reason->size();
832 tmp_indices_.resize(size);
833 for (int i = 0; i < size; ++i) {
834 CHECK_EQ((*reason)[i].bound, LowerBound((*reason)[i].var));
835 CHECK_GE(coeffs[i], 0);
836 tmp_indices_[i] = vars_[(*reason)[i].var].current_trail_index;
837 }
838
839 RelaxLinearReason(slack, coeffs, &tmp_indices_);
840
841 reason->clear();
842 for (const int i : tmp_indices_) {
843 reason->push_back(IntegerLiteral::GreaterOrEqual(integer_trail_[i].var,
844 integer_trail_[i].bound));
845 }
846}
847
849 IntegerValue slack, absl::Span<const IntegerValue> coeffs,
850 absl::Span<const IntegerVariable> vars,
851 std::vector<IntegerLiteral>* reason) const {
852 tmp_indices_.clear();
853 for (const IntegerVariable var : vars) {
854 tmp_indices_.push_back(vars_[var].current_trail_index);
855 }
856 if (slack > 0) RelaxLinearReason(slack, coeffs, &tmp_indices_);
857 for (const int i : tmp_indices_) {
858 reason->push_back(IntegerLiteral::GreaterOrEqual(integer_trail_[i].var,
859 integer_trail_[i].bound));
860 }
861}
862
863void IntegerTrail::RelaxLinearReason(IntegerValue slack,
864 absl::Span<const IntegerValue> coeffs,
865 std::vector<int>* trail_indices) const {
866 DCHECK_GT(slack, 0);
867 DCHECK(relax_heap_.empty());
868
869 // We start by filtering *trail_indices:
870 // - remove all level zero entries.
871 // - keep the one that cannot be relaxed.
872 // - move the other one to the relax_heap_ (and creating the heap).
873 int new_size = 0;
874 const int size = coeffs.size();
875 const int num_vars = vars_.size();
876 for (int i = 0; i < size; ++i) {
877 const int index = (*trail_indices)[i];
878
879 // We ignore level zero entries.
880 if (index < num_vars) continue;
881
882 // If the coeff is too large, we cannot relax this entry.
883 const IntegerValue coeff = coeffs[i];
884 if (coeff > slack) {
885 (*trail_indices)[new_size++] = index;
886 continue;
887 }
888
889 // This is a bit hacky, but when it is used from MergeReasonIntoInternal(),
890 // we never relax a reason that will not be expanded because it is already
891 // part of the current conflict.
892 const TrailEntry& entry = integer_trail_[index];
893 if (entry.var != kNoIntegerVariable &&
894 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
895 (*trail_indices)[new_size++] = index;
896 continue;
897 }
898
899 // Note that both terms of the product are positive.
900 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
901 const int64_t diff =
902 CapProd(coeff.value(), (entry.bound - previous_entry.bound).value());
903 if (diff > slack) {
904 (*trail_indices)[new_size++] = index;
905 continue;
906 }
907
908 relax_heap_.push_back({index, coeff, diff});
909 }
910 trail_indices->resize(new_size);
911 std::make_heap(relax_heap_.begin(), relax_heap_.end());
912
913 while (slack > 0 && !relax_heap_.empty()) {
914 const RelaxHeapEntry heap_entry = relax_heap_.front();
915 std::pop_heap(relax_heap_.begin(), relax_heap_.end());
916 relax_heap_.pop_back();
917
918 // The slack might have changed since the entry was added.
919 if (heap_entry.diff > slack) {
920 trail_indices->push_back(heap_entry.index);
921 continue;
922 }
923
924 // Relax, and decide what to do with the new value of index.
925 slack -= heap_entry.diff;
926 const int index = integer_trail_[heap_entry.index].prev_trail_index;
927
928 // Same code as in the first block.
929 if (index < num_vars) continue;
930 if (heap_entry.coeff > slack) {
931 trail_indices->push_back(index);
932 continue;
933 }
934 const TrailEntry& entry = integer_trail_[index];
935 if (entry.var != kNoIntegerVariable &&
936 index <= tmp_var_to_trail_index_in_queue_[entry.var]) {
937 trail_indices->push_back(index);
938 continue;
939 }
940
941 const TrailEntry& previous_entry = integer_trail_[entry.prev_trail_index];
942 const int64_t diff = CapProd(heap_entry.coeff.value(),
943 (entry.bound - previous_entry.bound).value());
944 if (diff > slack) {
945 trail_indices->push_back(index);
946 continue;
947 }
948 relax_heap_.push_back({index, heap_entry.coeff, diff});
949 std::push_heap(relax_heap_.begin(), relax_heap_.end());
950 }
951
952 // If we aborted early because of the slack, we need to push all remaining
953 // indices back into the reason.
954 for (const RelaxHeapEntry& entry : relax_heap_) {
955 trail_indices->push_back(entry.index);
956 }
957 relax_heap_.clear();
958}
959
961 std::vector<IntegerLiteral>* reason) const {
962 int new_size = 0;
963 for (const IntegerLiteral literal : *reason) {
964 if (literal.bound <= LevelZeroLowerBound(literal.var)) continue;
965 (*reason)[new_size++] = literal;
966 }
967 reason->resize(new_size);
968}
969
970std::vector<Literal>* IntegerTrail::InitializeConflict(
971 IntegerLiteral integer_literal, const LazyReasonFunction& lazy_reason,
972 absl::Span<const Literal> literals_reason,
973 absl::Span<const IntegerLiteral> bounds_reason) {
974 DCHECK(tmp_queue_.empty());
975 std::vector<Literal>* conflict = trail_->MutableConflict();
976 if (lazy_reason == nullptr) {
977 conflict->assign(literals_reason.begin(), literals_reason.end());
978 const int num_vars = vars_.size();
979 for (const IntegerLiteral& literal : bounds_reason) {
980 const int trail_index = FindLowestTrailIndexThatExplainBound(literal);
981 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
982 }
983 } else {
984 // We use the current trail index here.
985 conflict->clear();
986 lazy_reason(integer_literal, integer_trail_.size(), conflict, &tmp_queue_);
987 }
988 return conflict;
989}
990
991namespace {
992
993std::string ReasonDebugString(absl::Span<const Literal> literal_reason,
994 absl::Span<const IntegerLiteral> integer_reason) {
995 std::string result = "literals:{";
996 for (const Literal l : literal_reason) {
997 if (result.back() != '{') result += ",";
998 result += l.DebugString();
999 }
1000 result += "} bounds:{";
1001 for (const IntegerLiteral l : integer_reason) {
1002 if (result.back() != '{') result += ",";
1003 result += l.DebugString();
1004 }
1005 result += "}";
1006 return result;
1007}
1008
1009} // namespace
1010
1011std::string IntegerTrail::DebugString() {
1012 std::string result = "trail:{";
1013 const int num_vars = vars_.size();
1014 const int limit =
1015 std::min(num_vars + 30, static_cast<int>(integer_trail_.size()));
1016 for (int i = num_vars; i < limit; ++i) {
1017 if (result.back() != '{') result += ",";
1018 result +=
1019 IntegerLiteral::GreaterOrEqual(IntegerVariable(integer_trail_[i].var),
1020 integer_trail_[i].bound)
1021 .DebugString();
1022 }
1023 if (limit < integer_trail_.size()) {
1024 result += ", ...";
1025 }
1026 result += "}";
1027 return result;
1028}
1029
1031 IntegerLiteral i_lit, absl::Span<const IntegerLiteral> integer_reason) {
1032 if (i_lit.IsTrueLiteral()) return true;
1033
1034 std::vector<IntegerLiteral> cleaned_reason;
1035 for (const IntegerLiteral lit : integer_reason) {
1036 DCHECK(!lit.IsFalseLiteral());
1037 if (lit.IsTrueLiteral()) continue;
1038 cleaned_reason.push_back(lit);
1039 }
1040
1041 if (i_lit.IsFalseLiteral()) {
1042 return ReportConflict({}, cleaned_reason);
1043 } else {
1044 return Enqueue(i_lit, {}, cleaned_reason);
1045 }
1046}
1047
1049 absl::Span<const Literal> literal_reason,
1050 absl::Span<const IntegerLiteral> integer_reason) {
1051 return EnqueueInternal(i_lit, nullptr, literal_reason, integer_reason,
1052 integer_trail_.size());
1053}
1054
1056 Literal lit, IntegerLiteral i_lit, std::vector<Literal>* literal_reason,
1057 std::vector<IntegerLiteral>* integer_reason) {
1058 const VariablesAssignment& assignment = trail_->Assignment();
1059 if (assignment.LiteralIsFalse(lit)) return true;
1060
1061 // We can always push var if the optional literal is the same.
1062 //
1063 // TODO(user): we can also push lit.var if its presence implies lit.
1064 if (lit.Index() == OptionalLiteralIndex(i_lit.var)) {
1065 return Enqueue(i_lit, *literal_reason, *integer_reason);
1066 }
1067
1068 if (assignment.LiteralIsTrue(lit)) {
1069 literal_reason->push_back(lit.Negated());
1070 return Enqueue(i_lit, *literal_reason, *integer_reason);
1071 }
1072
1073 if (IntegerLiteralIsFalse(i_lit)) {
1074 integer_reason->push_back(
1075 IntegerLiteral::LowerOrEqual(i_lit.var, i_lit.bound - 1));
1076 EnqueueLiteral(lit.Negated(), *literal_reason, *integer_reason);
1077 return true;
1078 }
1079
1080 // We can't push anything in this case.
1081 //
1082 // We record it for this propagation phase (until the next untrail) as this
1083 // is relatively fast and heuristics can exploit this.
1084 //
1085 // Note that currently we only use ConditionalEnqueue() in scheduling
1086 // propagator, and these propagator are quite slow so this is not visible.
1087 //
1088 // TODO(user): We could even keep the reason and maybe do some reasoning using
1089 // at_least_one constraint on a set of the Boolean used here.
1090 const auto [it, inserted] =
1091 conditional_lbs_.insert({{lit.Index(), i_lit.var}, i_lit.bound});
1092 if (!inserted) {
1093 it->second = std::max(it->second, i_lit.bound);
1094 }
1095
1096 return true;
1097}
1098
1100 absl::Span<const Literal> literal_reason,
1101 absl::Span<const IntegerLiteral> integer_reason,
1102 int trail_index_with_same_reason) {
1103 return EnqueueInternal(i_lit, nullptr, literal_reason, integer_reason,
1104 trail_index_with_same_reason);
1105}
1106
1108 LazyReasonFunction lazy_reason) {
1109 return EnqueueInternal(i_lit, lazy_reason, {}, {}, integer_trail_.size());
1110}
1111
1112bool IntegerTrail::ReasonIsValid(
1113 absl::Span<const Literal> literal_reason,
1114 absl::Span<const IntegerLiteral> integer_reason) {
1115 const VariablesAssignment& assignment = trail_->Assignment();
1116 for (const Literal lit : literal_reason) {
1117 if (!assignment.LiteralIsFalse(lit)) return false;
1118 }
1119 for (const IntegerLiteral i_lit : integer_reason) {
1120 if (i_lit.bound > vars_[i_lit.var].current_bound) {
1121 if (IsOptional(i_lit.var)) {
1122 const Literal is_ignored = IsIgnoredLiteral(i_lit.var);
1123 LOG(INFO) << "Reason " << i_lit << " is not true!"
1124 << " optional variable:" << i_lit.var
1125 << " present:" << assignment.LiteralIsFalse(is_ignored)
1126 << " absent:" << assignment.LiteralIsTrue(is_ignored)
1127 << " current_lb:" << vars_[i_lit.var].current_bound;
1128 } else {
1129 LOG(INFO) << "Reason " << i_lit << " is not true!"
1130 << " non-optional variable:" << i_lit.var
1131 << " current_lb:" << vars_[i_lit.var].current_bound;
1132 }
1133 return false;
1134 }
1135 }
1136
1137 // This may not indicate an incorectness, but just some propagators that
1138 // didn't reach a fixed-point at level zero.
1139 if (!integer_search_levels_.empty()) {
1140 int num_literal_assigned_after_root_node = 0;
1141 for (const Literal lit : literal_reason) {
1142 if (trail_->Info(lit.Variable()).level > 0) {
1143 num_literal_assigned_after_root_node++;
1144 }
1145 }
1146 for (const IntegerLiteral i_lit : integer_reason) {
1147 if (LevelZeroLowerBound(i_lit.var) < i_lit.bound) {
1148 num_literal_assigned_after_root_node++;
1149 }
1150 }
1151 if (num_literal_assigned_after_root_node == 0) {
1152 VLOG(2) << "Propagating a literal with no reason at a positive level!\n"
1153 << "level:" << integer_search_levels_.size() << " "
1154 << ReasonDebugString(literal_reason, integer_reason) << "\n"
1155 << DebugString();
1156 }
1157 }
1158
1159 return true;
1160}
1161
1163 Literal literal, absl::Span<const Literal> literal_reason,
1164 absl::Span<const IntegerLiteral> integer_reason) {
1165 EnqueueLiteralInternal(literal, nullptr, literal_reason, integer_reason);
1166}
1167
1168void IntegerTrail::EnqueueLiteralInternal(
1169 Literal literal, LazyReasonFunction lazy_reason,
1170 absl::Span<const Literal> literal_reason,
1171 absl::Span<const IntegerLiteral> integer_reason) {
1173 DCHECK(lazy_reason != nullptr ||
1174 ReasonIsValid(literal_reason, integer_reason));
1175 if (integer_search_levels_.empty()) {
1176 // Level zero. We don't keep any reason.
1178 return;
1179 }
1180
1181 // If we are fixing something at a positive level, remember it.
1182 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1183 literal_reason.empty() && lazy_reason == nullptr) {
1184 literal_to_fix_.push_back(literal);
1185 }
1186
1187 const int trail_index = trail_->Index();
1188 if (trail_index >= boolean_trail_index_to_integer_one_.size()) {
1189 boolean_trail_index_to_integer_one_.resize(trail_index + 1);
1190 }
1191 boolean_trail_index_to_integer_one_[trail_index] = integer_trail_.size();
1192
1193 int reason_index = literals_reason_starts_.size();
1194 if (lazy_reason != nullptr) {
1195 if (integer_trail_.size() >= lazy_reasons_.size()) {
1196 lazy_reasons_.resize(integer_trail_.size() + 1, nullptr);
1197 }
1198 lazy_reasons_[integer_trail_.size()] = lazy_reason;
1199 reason_index = -1;
1200 } else {
1201 // Copy the reason.
1202 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1203 literals_reason_buffer_.insert(literals_reason_buffer_.end(),
1204 literal_reason.begin(),
1205 literal_reason.end());
1206 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1207 bounds_reason_buffer_.insert(bounds_reason_buffer_.end(),
1208 integer_reason.begin(), integer_reason.end());
1209 }
1210
1211 integer_trail_.push_back({/*bound=*/IntegerValue(0),
1212 /*var=*/kNoIntegerVariable,
1213 /*prev_trail_index=*/-1,
1214 /*reason_index=*/reason_index});
1215
1216 trail_->Enqueue(literal, propagator_id_);
1217}
1218
1219// We count the number of propagation at the current level, and returns true
1220// if it seems really large. Note that we disable this if we are in fixed
1221// search.
1223 const int num_vars = vars_.size();
1224 return (!integer_search_levels_.empty() &&
1225 integer_trail_.size() - integer_search_levels_.back() >
1226 std::max(10000, 10 * num_vars) &&
1227 parameters_.search_branching() != SatParameters::FIXED_SEARCH);
1228}
1229
1230// We try to select a variable with a large domain that was propagated a lot
1231// already.
1234 ++num_decisions_to_break_loop_;
1235 std::vector<IntegerVariable> vars;
1236 for (int i = integer_search_levels_.back(); i < integer_trail_.size(); ++i) {
1237 const IntegerVariable var = integer_trail_[i].var;
1238 if (var == kNoIntegerVariable) continue;
1239 if (UpperBound(var) - LowerBound(var) <= 100) continue;
1240 vars.push_back(var);
1241 }
1242 if (vars.empty()) return kNoIntegerVariable;
1243 std::sort(vars.begin(), vars.end());
1244 IntegerVariable best_var = vars[0];
1245 int best_count = 1;
1246 int count = 1;
1247 for (int i = 1; i < vars.size(); ++i) {
1248 if (vars[i] != vars[i - 1]) {
1249 count = 1;
1250 } else {
1251 ++count;
1252 if (count > best_count) {
1253 best_count = count;
1254 best_var = vars[i];
1255 }
1256 }
1257 }
1258 return best_var;
1259}
1260
1262 return first_level_without_full_propagation_ != -1;
1263}
1264
1266 for (IntegerVariable var(0); var < vars_.size(); var += 2) {
1267 if (IsCurrentlyIgnored(var)) continue;
1268 if (!IsFixed(var)) return var;
1269 }
1270 return kNoIntegerVariable;
1271}
1272
1273bool IntegerTrail::EnqueueInternal(
1274 IntegerLiteral i_lit, LazyReasonFunction lazy_reason,
1275 absl::Span<const Literal> literal_reason,
1276 absl::Span<const IntegerLiteral> integer_reason,
1277 int trail_index_with_same_reason) {
1278 DCHECK(lazy_reason != nullptr ||
1279 ReasonIsValid(literal_reason, integer_reason));
1280
1281 const IntegerVariable var(i_lit.var);
1282
1283 // No point doing work if the variable is already ignored.
1284 if (IsCurrentlyIgnored(var)) return true;
1285
1286 // Nothing to do if the bound is not better than the current one.
1287 // TODO(user): Change this to a CHECK? propagator shouldn't try to push such
1288 // bound and waste time explaining it.
1289 if (i_lit.bound <= vars_[var].current_bound) return true;
1290 ++num_enqueues_;
1291
1292 // If the domain of var is not a single intervals and i_lit.bound fall into a
1293 // "hole", we increase it to the next possible value. This ensure that we
1294 // never Enqueue() non-canonical literals. See also Canonicalize().
1295 //
1296 // Note: The literals in the reason are not necessarily canonical, but then
1297 // we always map these to enqueued literals during conflict resolution.
1298 if ((*domains_)[var].NumIntervals() > 1) {
1299 const auto& domain = (*domains_)[var];
1300 int index = var_to_current_lb_interval_index_.FindOrDie(var);
1301 const int size = domain.NumIntervals();
1302 while (index < size && i_lit.bound > domain[index].end) {
1303 ++index;
1304 }
1305 if (index == size) {
1306 return ReportConflict(literal_reason, integer_reason);
1307 } else {
1308 var_to_current_lb_interval_index_.Set(var, index);
1309 i_lit.bound = std::max(i_lit.bound, IntegerValue(domain[index].start));
1310 }
1311 }
1312
1313 // Check if the integer variable has an empty domain.
1314 if (i_lit.bound > UpperBound(var)) {
1315 // We relax the upper bound as much as possible to still have a conflict.
1316 const auto ub_reason = IntegerLiteral::LowerOrEqual(var, i_lit.bound - 1);
1317
1318 if (!IsOptional(var) || trail_->Assignment().LiteralIsFalse(
1319 Literal(is_ignored_literals_[var]))) {
1320 // Note that we want only one call to MergeReasonIntoInternal() for
1321 // efficiency and a potential smaller reason.
1322 auto* conflict = InitializeConflict(i_lit, lazy_reason, literal_reason,
1323 integer_reason);
1324 if (IsOptional(var)) {
1325 conflict->push_back(Literal(is_ignored_literals_[var]));
1326 }
1327 {
1328 const int trail_index = FindLowestTrailIndexThatExplainBound(ub_reason);
1329 const int num_vars = vars_.size(); // must be signed.
1330 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
1331 }
1332 MergeReasonIntoInternal(conflict);
1333 return false;
1334 } else {
1335 // Note(user): We never make the bound of an optional literal cross. We
1336 // used to have a bug where we propagated these bounds and their
1337 // associated literals, and we were reaching a conflict while propagating
1338 // the associated literal instead of setting is_ignored below to false.
1339 const Literal is_ignored = Literal(is_ignored_literals_[var]);
1340 if (integer_search_levels_.empty()) {
1341 trail_->EnqueueWithUnitReason(is_ignored);
1342 } else {
1343 // Here we currently expand any lazy reason because we need to add
1344 // to it the reason for the upper bound.
1345 // TODO(user): A possible solution would be to support the two types
1346 // of reason (lazy and not) at the same time and use the union of both?
1347 if (lazy_reason != nullptr) {
1348 lazy_reason(i_lit, integer_trail_.size(), &lazy_reason_literals_,
1349 &lazy_reason_trail_indices_);
1350 std::vector<IntegerLiteral> temp;
1351 for (const int trail_index : lazy_reason_trail_indices_) {
1352 const TrailEntry& entry = integer_trail_[trail_index];
1353 temp.push_back(IntegerLiteral(entry.var, entry.bound));
1354 }
1355 EnqueueLiteral(is_ignored, lazy_reason_literals_, temp);
1356 } else {
1357 EnqueueLiteral(is_ignored, literal_reason, integer_reason);
1358 }
1359
1360 // Hack, we add the upper bound reason here.
1361 bounds_reason_buffer_.push_back(ub_reason);
1362 }
1363 return true;
1364 }
1365 }
1366
1367 // Stop propagating if we detect a propagation loop. The search heuristic will
1368 // then take an appropriate next decision. Note that we do that after checking
1369 // for a potential conflict if the two bounds of a variable cross. This is
1370 // important, so that in the corner case where all variables are actually
1371 // fixed, we still make sure no propagator detect a conflict.
1372 //
1373 // TODO(user): Some propagation code have CHECKS in place and not like when
1374 // something they just pushed is not reflected right away. They must be aware
1375 // of that, which is a bit tricky.
1376 if (InPropagationLoop()) {
1377 // Note that we still propagate "big" push as it seems better to do that
1378 // now rather than to delay to the next decision.
1379 const IntegerValue lb = LowerBound(i_lit.var);
1380 const IntegerValue ub = UpperBound(i_lit.var);
1381 if (i_lit.bound - lb < (ub - lb) / 2) {
1382 if (first_level_without_full_propagation_ == -1) {
1383 first_level_without_full_propagation_ = trail_->CurrentDecisionLevel();
1384 }
1385 return true;
1386 }
1387 }
1388
1389 // Notify the watchers.
1390 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1391 bitset->Set(i_lit.var);
1392 }
1393
1394 if (!integer_search_levels_.empty() && integer_reason.empty() &&
1395 literal_reason.empty() && lazy_reason == nullptr &&
1396 trail_index_with_same_reason >= integer_trail_.size()) {
1397 integer_literal_to_fix_.push_back(i_lit);
1398 }
1399
1400 // Enqueue the strongest associated Boolean literal implied by this one.
1401 // Because we linked all such literal with implications, all the one before
1402 // will be propagated by the SAT solver.
1403 //
1404 // Important: It is possible that such literal or even stronger ones are
1405 // already true! This is because we might push stuff while Propagate() haven't
1406 // been called yet. Maybe we should call it?
1407 //
1408 // TODO(user): It might be simply better and more efficient to simply enqueue
1409 // all of them here. We have also more liberty to choose the explanation we
1410 // want. A drawback might be that the implications might not be used in the
1411 // binary conflict minimization algo.
1412 IntegerValue bound;
1413 const LiteralIndex literal_index =
1414 encoder_->SearchForLiteralAtOrBefore(i_lit, &bound);
1415 if (literal_index != kNoLiteralIndex) {
1416 const Literal to_enqueue = Literal(literal_index);
1417 if (trail_->Assignment().LiteralIsFalse(to_enqueue)) {
1418 auto* conflict = InitializeConflict(i_lit, lazy_reason, literal_reason,
1419 integer_reason);
1420 conflict->push_back(to_enqueue);
1421 MergeReasonIntoInternal(conflict);
1422 return false;
1423 }
1424
1425 // If the associated literal exactly correspond to i_lit, then we push
1426 // it first, and then we use it as a reason for i_lit. We do that so that
1427 // MergeReasonIntoInternal() will not unecessarily expand further the reason
1428 // for i_lit.
1429 if (IntegerLiteral::GreaterOrEqual(i_lit.var, bound) == i_lit) {
1430 if (!trail_->Assignment().LiteralIsTrue(to_enqueue)) {
1431 EnqueueLiteralInternal(to_enqueue, lazy_reason, literal_reason,
1432 integer_reason);
1433 }
1434 return EnqueueAssociatedIntegerLiteral(i_lit, to_enqueue);
1435 }
1436
1437 if (!trail_->Assignment().LiteralIsTrue(to_enqueue)) {
1438 if (integer_search_levels_.empty()) {
1439 trail_->EnqueueWithUnitReason(to_enqueue);
1440 } else {
1441 // Subtle: the reason is the same as i_lit, that we will enqueue if no
1442 // conflict occur at position integer_trail_.size(), so we just refer to
1443 // this index here.
1444 const int trail_index = trail_->Index();
1445 if (trail_index >= boolean_trail_index_to_integer_one_.size()) {
1446 boolean_trail_index_to_integer_one_.resize(trail_index + 1);
1447 }
1448 boolean_trail_index_to_integer_one_[trail_index] =
1449 trail_index_with_same_reason;
1450 trail_->Enqueue(to_enqueue, propagator_id_);
1451 }
1452 }
1453 }
1454
1455 // Special case for level zero.
1456 if (integer_search_levels_.empty()) {
1457 ++num_level_zero_enqueues_;
1458 vars_[i_lit.var].current_bound = i_lit.bound;
1459 integer_trail_[i_lit.var.value()].bound = i_lit.bound;
1460
1461 // We also update the initial domain. If this fail, since we are at level
1462 // zero, we don't care about the reason.
1463 trail_->MutableConflict()->clear();
1464 return UpdateInitialDomain(
1465 i_lit.var,
1466 Domain(LowerBound(i_lit.var).value(), UpperBound(i_lit.var).value()));
1467 }
1468 DCHECK_GT(trail_->CurrentDecisionLevel(), 0);
1469
1470 int reason_index = literals_reason_starts_.size();
1471 if (lazy_reason != nullptr) {
1472 if (integer_trail_.size() >= lazy_reasons_.size()) {
1473 lazy_reasons_.resize(integer_trail_.size() + 1, nullptr);
1474 }
1475 lazy_reasons_[integer_trail_.size()] = lazy_reason;
1476 reason_index = -1;
1477 } else if (trail_index_with_same_reason >= integer_trail_.size()) {
1478 // Save the reason into our internal buffers.
1479 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1480 if (!literal_reason.empty()) {
1481 literals_reason_buffer_.insert(literals_reason_buffer_.end(),
1482 literal_reason.begin(),
1483 literal_reason.end());
1484 }
1485 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1486 if (!integer_reason.empty()) {
1487 bounds_reason_buffer_.insert(bounds_reason_buffer_.end(),
1488 integer_reason.begin(),
1489 integer_reason.end());
1490 }
1491 } else {
1492 reason_index = integer_trail_[trail_index_with_same_reason].reason_index;
1493 }
1494
1495 const int prev_trail_index = vars_[i_lit.var].current_trail_index;
1496 integer_trail_.push_back({/*bound=*/i_lit.bound,
1497 /*var=*/i_lit.var,
1498 /*prev_trail_index=*/prev_trail_index,
1499 /*reason_index=*/reason_index});
1500
1501 vars_[i_lit.var].current_bound = i_lit.bound;
1502 vars_[i_lit.var].current_trail_index = integer_trail_.size() - 1;
1503 return true;
1504}
1505
1506bool IntegerTrail::EnqueueAssociatedIntegerLiteral(IntegerLiteral i_lit,
1507 Literal literal_reason) {
1508 DCHECK(ReasonIsValid({literal_reason.Negated()}, {}));
1509 DCHECK(!IsCurrentlyIgnored(i_lit.var));
1510
1511 // Nothing to do if the bound is not better than the current one.
1512 if (i_lit.bound <= vars_[i_lit.var].current_bound) return true;
1513 ++num_enqueues_;
1514
1515 // Check if the integer variable has an empty domain. Note that this should
1516 // happen really rarely since in most situation, pushing the upper bound would
1517 // have resulted in this literal beeing false. Because of this we revert to
1518 // the "generic" Enqueue() to avoid some code duplication.
1519 if (i_lit.bound > UpperBound(i_lit.var)) {
1520 return Enqueue(i_lit, {literal_reason.Negated()}, {});
1521 }
1522
1523 // Notify the watchers.
1524 for (SparseBitset<IntegerVariable>* bitset : watchers_) {
1525 bitset->Set(i_lit.var);
1526 }
1527
1528 // Special case for level zero.
1529 if (integer_search_levels_.empty()) {
1530 vars_[i_lit.var].current_bound = i_lit.bound;
1531 integer_trail_[i_lit.var.value()].bound = i_lit.bound;
1532
1533 // We also update the initial domain. If this fail, since we are at level
1534 // zero, we don't care about the reason.
1535 trail_->MutableConflict()->clear();
1536 return UpdateInitialDomain(
1537 i_lit.var,
1538 Domain(LowerBound(i_lit.var).value(), UpperBound(i_lit.var).value()));
1539 }
1540 DCHECK_GT(trail_->CurrentDecisionLevel(), 0);
1541
1542 const int reason_index = literals_reason_starts_.size();
1543 CHECK_EQ(reason_index, bounds_reason_starts_.size());
1544 literals_reason_starts_.push_back(literals_reason_buffer_.size());
1545 bounds_reason_starts_.push_back(bounds_reason_buffer_.size());
1546 literals_reason_buffer_.push_back(literal_reason.Negated());
1547
1548 const int prev_trail_index = vars_[i_lit.var].current_trail_index;
1549 integer_trail_.push_back({/*bound=*/i_lit.bound,
1550 /*var=*/i_lit.var,
1551 /*prev_trail_index=*/prev_trail_index,
1552 /*reason_index=*/reason_index});
1553
1554 vars_[i_lit.var].current_bound = i_lit.bound;
1555 vars_[i_lit.var].current_trail_index = integer_trail_.size() - 1;
1556 return true;
1557}
1558
1559void IntegerTrail::ComputeLazyReasonIfNeeded(int trail_index) const {
1560 const int reason_index = integer_trail_[trail_index].reason_index;
1561 if (reason_index == -1) {
1562 const TrailEntry& entry = integer_trail_[trail_index];
1563 const IntegerLiteral literal(entry.var, entry.bound);
1564 lazy_reasons_[trail_index](literal, trail_index, &lazy_reason_literals_,
1565 &lazy_reason_trail_indices_);
1566 }
1567}
1568
1569absl::Span<const int> IntegerTrail::Dependencies(int trail_index) const {
1570 const int reason_index = integer_trail_[trail_index].reason_index;
1571 if (reason_index == -1) {
1572 return absl::Span<const int>(lazy_reason_trail_indices_);
1573 }
1574
1575 const int start = bounds_reason_starts_[reason_index];
1576 const int end = reason_index + 1 < bounds_reason_starts_.size()
1577 ? bounds_reason_starts_[reason_index + 1]
1578 : bounds_reason_buffer_.size();
1579 if (start == end) return {};
1580
1581 // Cache the result if not already computed. Remark, if the result was never
1582 // computed then the span trail_index_reason_buffer_[start, end) will either
1583 // be non-existent or full of -1.
1584 //
1585 // TODO(user): For empty reason, we will always recompute them.
1586 if (end > trail_index_reason_buffer_.size()) {
1587 trail_index_reason_buffer_.resize(end, -1);
1588 }
1589 if (trail_index_reason_buffer_[start] == -1) {
1590 int new_end = start;
1591 const int num_vars = vars_.size();
1592 for (int i = start; i < end; ++i) {
1593 const int dep =
1594 FindLowestTrailIndexThatExplainBound(bounds_reason_buffer_[i]);
1595 if (dep >= num_vars) {
1596 trail_index_reason_buffer_[new_end++] = dep;
1597 }
1598 }
1599 return absl::Span<const int>(&trail_index_reason_buffer_[start],
1600 new_end - start);
1601 } else {
1602 // TODO(user): We didn't store new_end in a previous call, so end might be
1603 // larger. That is a bit annoying since we have to test for -1 while
1604 // iterating.
1605 return absl::Span<const int>(&trail_index_reason_buffer_[start],
1606 end - start);
1607 }
1608}
1609
1610void IntegerTrail::AppendLiteralsReason(int trail_index,
1611 std::vector<Literal>* output) const {
1612 CHECK_GE(trail_index, vars_.size());
1613 const int reason_index = integer_trail_[trail_index].reason_index;
1614 if (reason_index == -1) {
1615 for (const Literal l : lazy_reason_literals_) {
1616 if (!added_variables_[l.Variable()]) {
1617 added_variables_.Set(l.Variable());
1618 output->push_back(l);
1619 }
1620 }
1621 return;
1622 }
1623
1624 const int start = literals_reason_starts_[reason_index];
1625 const int end = reason_index + 1 < literals_reason_starts_.size()
1626 ? literals_reason_starts_[reason_index + 1]
1627 : literals_reason_buffer_.size();
1628 for (int i = start; i < end; ++i) {
1629 const Literal l = literals_reason_buffer_[i];
1630 if (!added_variables_[l.Variable()]) {
1631 added_variables_.Set(l.Variable());
1632 output->push_back(l);
1633 }
1634 }
1635}
1636
1638 std::vector<Literal> reason;
1639 MergeReasonInto({literal}, &reason);
1640 return reason;
1641}
1642
1643// TODO(user): If this is called many time on the same variables, it could be
1644// made faster by using some caching mecanism.
1645void IntegerTrail::MergeReasonInto(absl::Span<const IntegerLiteral> literals,
1646 std::vector<Literal>* output) const {
1647 DCHECK(tmp_queue_.empty());
1648 const int num_vars = vars_.size();
1649 for (const IntegerLiteral& literal : literals) {
1650 const int trail_index = FindLowestTrailIndexThatExplainBound(literal);
1651
1652 // Any indices lower than that means that there is no reason needed.
1653 // Note that it is important for size to be signed because of -1 indices.
1654 if (trail_index >= num_vars) tmp_queue_.push_back(trail_index);
1655 }
1656 return MergeReasonIntoInternal(output);
1657}
1658
1659// This will expand the reason of the IntegerLiteral already in tmp_queue_ until
1660// everything is explained in term of Literal.
1661void IntegerTrail::MergeReasonIntoInternal(std::vector<Literal>* output) const {
1662 // All relevant trail indices will be >= vars_.size(), so we can safely use
1663 // zero to means that no literal refering to this variable is in the queue.
1664 DCHECK(std::all_of(tmp_var_to_trail_index_in_queue_.begin(),
1665 tmp_var_to_trail_index_in_queue_.end(),
1666 [](int v) { return v == 0; }));
1667
1668 added_variables_.ClearAndResize(BooleanVariable(trail_->NumVariables()));
1669 for (const Literal l : *output) {
1670 added_variables_.Set(l.Variable());
1671 }
1672
1673 // During the algorithm execution, all the queue entries that do not match the
1674 // content of tmp_var_to_trail_index_in_queue_[] will be ignored.
1675 for (const int trail_index : tmp_queue_) {
1676 DCHECK_GE(trail_index, vars_.size());
1677 DCHECK_LT(trail_index, integer_trail_.size());
1678 const TrailEntry& entry = integer_trail_[trail_index];
1679 tmp_var_to_trail_index_in_queue_[entry.var] =
1680 std::max(tmp_var_to_trail_index_in_queue_[entry.var], trail_index);
1681 }
1682
1683 // We manage our heap by hand so that we can range iterate over it above, and
1684 // this initial heapify is faster.
1685 std::make_heap(tmp_queue_.begin(), tmp_queue_.end());
1686
1687 // We process the entries by highest trail_index first. The content of the
1688 // queue will always be a valid reason for the literals we already added to
1689 // the output.
1690 tmp_to_clear_.clear();
1691 while (!tmp_queue_.empty()) {
1692 const int trail_index = tmp_queue_.front();
1693 const TrailEntry& entry = integer_trail_[trail_index];
1694 std::pop_heap(tmp_queue_.begin(), tmp_queue_.end());
1695 tmp_queue_.pop_back();
1696
1697 // Skip any stale queue entry. Amongst all the entry refering to a given
1698 // variable, only the latest added to the queue is valid and we detect it
1699 // using its trail index.
1700 if (tmp_var_to_trail_index_in_queue_[entry.var] != trail_index) {
1701 continue;
1702 }
1703
1704 // Set the cache threshold. Since we process trail indices in decreasing
1705 // order and we only have single linked list, we only want to advance the
1706 // "cache" up to this threshold.
1707 var_trail_index_cache_threshold_ = trail_index;
1708
1709 // If this entry has an associated literal, then it should always be the
1710 // one we used for the reason. This code DCHECK that.
1711 if (DEBUG_MODE) {
1712 const LiteralIndex associated_lit =
1714 IntegerVariable(entry.var), entry.bound));
1715 if (associated_lit != kNoLiteralIndex) {
1716 // We check that the reason is the same!
1717 const int reason_index = integer_trail_[trail_index].reason_index;
1718 CHECK_NE(reason_index, -1);
1719 {
1720 const int start = literals_reason_starts_[reason_index];
1721 const int end = reason_index + 1 < literals_reason_starts_.size()
1722 ? literals_reason_starts_[reason_index + 1]
1723 : literals_reason_buffer_.size();
1724 CHECK_EQ(start + 1, end);
1725 CHECK_EQ(literals_reason_buffer_[start],
1726 Literal(associated_lit).Negated());
1727 }
1728 {
1729 const int start = bounds_reason_starts_[reason_index];
1730 const int end = reason_index + 1 < bounds_reason_starts_.size()
1731 ? bounds_reason_starts_[reason_index + 1]
1732 : bounds_reason_buffer_.size();
1733 CHECK_EQ(start, end);
1734 }
1735 }
1736 }
1737
1738 // Process this entry. Note that if any of the next expansion include the
1739 // variable entry.var in their reason, we must process it again because we
1740 // cannot easily detect if it was needed to infer the current entry.
1741 //
1742 // Important: the queue might already contains entries refering to the same
1743 // variable. The code act like if we deleted all of them at this point, we
1744 // just do that lazily. tmp_var_to_trail_index_in_queue_[var] will
1745 // only refer to newly added entries.
1746 tmp_var_to_trail_index_in_queue_[entry.var] = 0;
1747 has_dependency_ = false;
1748
1749 ComputeLazyReasonIfNeeded(trail_index);
1750 AppendLiteralsReason(trail_index, output);
1751 for (const int next_trail_index : Dependencies(trail_index)) {
1752 if (next_trail_index < 0) break;
1753 DCHECK_LT(next_trail_index, trail_index);
1754 const TrailEntry& next_entry = integer_trail_[next_trail_index];
1755
1756 // Only add literals that are not "implied" by the ones already present.
1757 // For instance, do not add (x >= 4) if we already have (x >= 7). This
1758 // translate into only adding a trail index if it is larger than the one
1759 // in the queue refering to the same variable.
1760 const int index_in_queue =
1761 tmp_var_to_trail_index_in_queue_[next_entry.var];
1762 if (index_in_queue != std::numeric_limits<int32_t>::max())
1763 has_dependency_ = true;
1764 if (next_trail_index > index_in_queue) {
1765 tmp_var_to_trail_index_in_queue_[next_entry.var] = next_trail_index;
1766 tmp_queue_.push_back(next_trail_index);
1767 std::push_heap(tmp_queue_.begin(), tmp_queue_.end());
1768 }
1769 }
1770
1771 // Special case for a "leaf", we will never need this variable again.
1772 if (!has_dependency_) {
1773 tmp_to_clear_.push_back(entry.var);
1774 tmp_var_to_trail_index_in_queue_[entry.var] =
1776 }
1777 }
1778
1779 // clean-up.
1780 for (const IntegerVariable var : tmp_to_clear_) {
1781 tmp_var_to_trail_index_in_queue_[var] = 0;
1782 }
1783}
1784
1785absl::Span<const Literal> IntegerTrail::Reason(const Trail& trail,
1786 int trail_index) const {
1787 const int index = boolean_trail_index_to_integer_one_[trail_index];
1788 std::vector<Literal>* reason = trail.GetEmptyVectorToStoreReason(trail_index);
1789 added_variables_.ClearAndResize(BooleanVariable(trail_->NumVariables()));
1790
1791 ComputeLazyReasonIfNeeded(index);
1792 AppendLiteralsReason(index, reason);
1793 DCHECK(tmp_queue_.empty());
1794 for (const int prev_trail_index : Dependencies(index)) {
1795 if (prev_trail_index < 0) break;
1796 DCHECK_GE(prev_trail_index, vars_.size());
1797 tmp_queue_.push_back(prev_trail_index);
1798 }
1799 MergeReasonIntoInternal(reason);
1800 return *reason;
1801}
1802
1803// TODO(user): Implement a dense version if there is more trail entries
1804// than variables!
1805void IntegerTrail::AppendNewBounds(std::vector<IntegerLiteral>* output) const {
1806 tmp_marked_.ClearAndResize(IntegerVariable(vars_.size()));
1807
1808 // In order to push the best bound for each variable, we loop backward.
1809 const int end = vars_.size();
1810 for (int i = integer_trail_.size(); --i >= end;) {
1811 const TrailEntry& entry = integer_trail_[i];
1812 if (entry.var == kNoIntegerVariable) continue;
1813 if (tmp_marked_[entry.var]) continue;
1814
1815 tmp_marked_.Set(entry.var);
1816 output->push_back(IntegerLiteral::GreaterOrEqual(entry.var, entry.bound));
1817 }
1818}
1819
1821 : SatPropagator("GenericLiteralWatcher"),
1822 time_limit_(model->GetOrCreate<TimeLimit>()),
1823 integer_trail_(model->GetOrCreate<IntegerTrail>()),
1824 rev_int_repository_(model->GetOrCreate<RevIntRepository>()) {
1825 // TODO(user): This propagator currently needs to be last because it is the
1826 // only one enforcing that a fix-point is reached on the integer variables.
1827 // Figure out a better interaction between the sat propagation loop and
1828 // this one.
1829 model->GetOrCreate<SatSolver>()->AddLastPropagator(this);
1830
1831 integer_trail_->RegisterReversibleClass(
1832 &id_to_greatest_common_level_since_last_call_);
1833 integer_trail_->RegisterWatcher(&modified_vars_);
1834 queue_by_priority_.resize(2); // Because default priority is 1.
1835}
1836
1837void GenericLiteralWatcher::UpdateCallingNeeds(Trail* trail) {
1838 // Process any new Literal on the trail.
1839 while (propagation_trail_index_ < trail->Index()) {
1840 const Literal literal = (*trail)[propagation_trail_index_++];
1841 if (literal.Index() >= literal_to_watcher_.size()) continue;
1842 for (const auto entry : literal_to_watcher_[literal.Index()]) {
1843 if (!in_queue_[entry.id]) {
1844 in_queue_[entry.id] = true;
1845 queue_by_priority_[id_to_priority_[entry.id]].push_back(entry.id);
1846 }
1847 if (entry.watch_index >= 0) {
1848 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
1849 }
1850 }
1851 }
1852
1853 // Process the newly changed variables lower bounds.
1854 for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
1855 if (var.value() >= var_to_watcher_.size()) continue;
1856 for (const auto entry : var_to_watcher_[var]) {
1857 if (!in_queue_[entry.id]) {
1858 in_queue_[entry.id] = true;
1859 queue_by_priority_[id_to_priority_[entry.id]].push_back(entry.id);
1860 }
1861 if (entry.watch_index >= 0) {
1862 id_to_watch_indices_[entry.id].push_back(entry.watch_index);
1863 }
1864 }
1865 }
1866
1867 if (trail->CurrentDecisionLevel() == 0 &&
1868 !level_zero_modified_variable_callback_.empty()) {
1869 modified_vars_for_callback_.Resize(modified_vars_.size());
1870 for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
1871 modified_vars_for_callback_.Set(var);
1872 }
1873 }
1874
1875 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
1876}
1877
1879 // Only once per call to Propagate(), if we are at level zero, we might want
1880 // to call propagators even if the bounds didn't change.
1881 const int level = trail->CurrentDecisionLevel();
1882 if (level == 0) {
1883 for (const int id : propagator_ids_to_call_at_level_zero_) {
1884 if (in_queue_[id]) continue;
1885 in_queue_[id] = true;
1886 queue_by_priority_[id_to_priority_[id]].push_back(id);
1887 }
1888 }
1889
1890 UpdateCallingNeeds(trail);
1891
1892 // Note that the priority may be set to -1 inside the loop in order to restart
1893 // at zero.
1894 int test_limit = 0;
1895 for (int priority = 0; priority < queue_by_priority_.size(); ++priority) {
1896 // We test the time limit from time to time. This is in order to return in
1897 // case of slow propagation.
1898 //
1899 // TODO(user): The queue will not be emptied, but I am not sure the solver
1900 // will be left in an usable state. Fix if it become needed to resume
1901 // the solve from the last time it was interrupted.
1902 if (test_limit > 100) {
1903 test_limit = 0;
1904 if (time_limit_->LimitReached()) break;
1905 }
1906
1907 std::deque<int>& queue = queue_by_priority_[priority];
1908 while (!queue.empty()) {
1909 const int id = queue.front();
1910 current_id_ = id;
1911 queue.pop_front();
1912
1913 // Before we propagate, make sure any reversible structure are up to date.
1914 // Note that we never do anything expensive more than once per level.
1915 {
1916 const int low =
1917 id_to_greatest_common_level_since_last_call_[IdType(id)];
1918 const int high = id_to_level_at_last_call_[id];
1919 if (low < high || level > low) { // Equivalent to not all equal.
1920 id_to_level_at_last_call_[id] = level;
1921 id_to_greatest_common_level_since_last_call_.MutableRef(IdType(id)) =
1922 level;
1923 for (ReversibleInterface* rev : id_to_reversible_classes_[id]) {
1924 if (low < high) rev->SetLevel(low);
1925 if (level > low) rev->SetLevel(level);
1926 }
1927 for (int* rev_int : id_to_reversible_ints_[id]) {
1928 rev_int_repository_->SaveState(rev_int);
1929 }
1930 }
1931 }
1932
1933 // This is needed to detect if the propagator propagated anything or not.
1934 const int64_t old_integer_timestamp = integer_trail_->num_enqueues();
1935 const int64_t old_boolean_timestamp = trail->Index();
1936
1937 // TODO(user): Maybe just provide one function Propagate(watch_indices) ?
1938 std::vector<int>& watch_indices_ref = id_to_watch_indices_[id];
1939 const bool result =
1940 watch_indices_ref.empty()
1941 ? watchers_[id]->Propagate()
1942 : watchers_[id]->IncrementalPropagate(watch_indices_ref);
1943 if (!result) {
1944 watch_indices_ref.clear();
1945 in_queue_[id] = false;
1946 return false;
1947 }
1948
1949 // Update the propagation queue. At this point, the propagator has been
1950 // removed from the queue but in_queue_ is still true.
1951 if (id_to_idempotence_[id]) {
1952 // If the propagator is assumed to be idempotent, then we set in_queue_
1953 // to false after UpdateCallingNeeds() so this later function will never
1954 // add it back.
1955 UpdateCallingNeeds(trail);
1956 watch_indices_ref.clear();
1957 in_queue_[id] = false;
1958 } else {
1959 // Otherwise, we set in_queue_ to false first so that
1960 // UpdateCallingNeeds() may add it back if the propagator modified any
1961 // of its watched variables.
1962 watch_indices_ref.clear();
1963 in_queue_[id] = false;
1964 UpdateCallingNeeds(trail);
1965 }
1966
1967 // If the propagator pushed a literal, we exit in order to rerun all SAT
1968 // only propagators first. Note that since a literal was pushed we are
1969 // guaranteed to be called again, and we will resume from priority 0.
1970 if (trail->Index() > old_boolean_timestamp) {
1971 // Important: for now we need to re-run the clauses propagator each time
1972 // we push a new literal because some propagator like the arc consistent
1973 // all diff relies on this.
1974 //
1975 // TODO(user): However, on some problem, it seems to work better to not
1976 // do that. One possible reason is that the reason of a "natural"
1977 // propagation might be better than one we learned.
1978 return true;
1979 }
1980
1981 // If the propagator pushed an integer bound, we revert to priority = 0.
1982 if (integer_trail_->num_enqueues() > old_integer_timestamp) {
1983 ++test_limit;
1984 priority = -1; // Because of the ++priority in the for loop.
1985 break;
1986 }
1987 }
1988 }
1989
1990 // We wait until we reach the fix point before calling the callback.
1991 if (trail->CurrentDecisionLevel() == 0) {
1992 const std::vector<IntegerVariable>& modified_vars =
1993 modified_vars_for_callback_.PositionsSetAtLeastOnce();
1994 for (const auto& callback : level_zero_modified_variable_callback_) {
1995 callback(modified_vars);
1996 }
1997 modified_vars_for_callback_.ClearAndResize(
1998 integer_trail_->NumIntegerVariables());
1999 }
2000
2001 return true;
2002}
2003
2004void GenericLiteralWatcher::Untrail(const Trail& trail, int trail_index) {
2005 if (propagation_trail_index_ <= trail_index) {
2006 // Nothing to do since we found a conflict before Propagate() was called.
2007 CHECK_EQ(propagation_trail_index_, trail_index);
2008 return;
2009 }
2010
2011 // We need to clear the watch indices on untrail.
2012 for (std::deque<int>& queue : queue_by_priority_) {
2013 for (const int id : queue) {
2014 id_to_watch_indices_[id].clear();
2015 }
2016 queue.clear();
2017 }
2018
2019 // This means that we already propagated all there is to propagate
2020 // at the level trail_index, so we can safely clear modified_vars_ in case
2021 // it wasn't already done.
2022 propagation_trail_index_ = trail_index;
2023 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
2024 in_queue_.assign(watchers_.size(), false);
2025}
2026
2027// Registers a propagator and returns its unique ids.
2029 const int id = watchers_.size();
2030 watchers_.push_back(propagator);
2031 id_to_level_at_last_call_.push_back(0);
2032 id_to_greatest_common_level_since_last_call_.GrowByOne();
2033 id_to_reversible_classes_.push_back(std::vector<ReversibleInterface*>());
2034 id_to_reversible_ints_.push_back(std::vector<int*>());
2035 id_to_watch_indices_.push_back(std::vector<int>());
2036 id_to_priority_.push_back(1);
2037 id_to_idempotence_.push_back(true);
2038
2039 // Call this propagator at least once the next time Propagate() is called.
2040 //
2041 // TODO(user): This initial propagation does not respect any later priority
2042 // settings. Fix this. Maybe we should force users to pass the priority at
2043 // registration. For now I didn't want to change the interface because there
2044 // are plans to implement a kind of "dynamic" priority, and if it works we may
2045 // want to get rid of this altogether.
2046 in_queue_.push_back(true);
2047 queue_by_priority_[1].push_back(id);
2048 return id;
2049}
2050
2052 id_to_priority_[id] = priority;
2053 if (priority >= queue_by_priority_.size()) {
2054 queue_by_priority_.resize(priority + 1);
2055 }
2056}
2057
2059 int id) {
2060 id_to_idempotence_[id] = false;
2061}
2062
2064 propagator_ids_to_call_at_level_zero_.push_back(id);
2065}
2066
2068 ReversibleInterface* rev) {
2069 id_to_reversible_classes_[id].push_back(rev);
2070}
2071
2073 id_to_reversible_ints_[id].push_back(rev);
2074}
2075
2076// This is really close to ExcludeCurrentSolutionAndBacktrack().
2077std::function<void(Model*)>
2079 return [=](Model* model) {
2080 SatSolver* sat_solver = model->GetOrCreate<SatSolver>();
2081 IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
2082 IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
2083
2084 const int current_level = sat_solver->CurrentDecisionLevel();
2085 std::vector<Literal> clause_to_exclude_solution;
2086 clause_to_exclude_solution.reserve(current_level);
2087 for (int i = 0; i < current_level; ++i) {
2088 bool include_decision = true;
2089 const Literal decision = sat_solver->Decisions()[i].literal;
2090
2091 // Tests if this decision is associated to a bound of an ignored variable
2092 // in the current assignment.
2093 const InlinedIntegerLiteralVector& associated_literals =
2094 encoder->GetIntegerLiterals(decision);
2095 for (const IntegerLiteral bound : associated_literals) {
2096 if (integer_trail->IsCurrentlyIgnored(bound.var)) {
2097 // In this case we replace the decision (which is a bound on an
2098 // ignored variable) with the fact that the integer variable was
2099 // ignored. This works because the only impact a bound of an ignored
2100 // variable can have on the rest of the model is through the
2101 // is_ignored literal.
2102 clause_to_exclude_solution.push_back(
2103 integer_trail->IsIgnoredLiteral(bound.var).Negated());
2104 include_decision = false;
2105 }
2106 }
2107
2108 if (include_decision) {
2109 clause_to_exclude_solution.push_back(decision.Negated());
2110 }
2111 }
2112
2113 // Note that it is okay to add duplicates literals in ClauseConstraint(),
2114 // the clause will be preprocessed correctly.
2115 sat_solver->Backtrack(0);
2116 model->Add(ClauseConstraint(clause_to_exclude_solution));
2117 };
2118}
2119
2120} // namespace sat
2121} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define CHECK_LT(val1, val2)
Definition: base/logging.h:706
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:896
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:890
#define CHECK_LE(val1, val2)
Definition: base/logging.h:705
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
#define VLOG(verboselevel)
Definition: base/logging.h:984
void resize(size_type new_size)
void reserve(size_type n)
size_type size() const
void push_back(const value_type &x)
We call domain any subset of Int64 = [kint64min, kint64max].
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t Max() const
Returns the max value of the domain.
absl::InlinedVector< ClosedInterval, 1 >::const_iterator end() const
void Set(key_type key, mapped_type value)
Definition: rev.h:238
void SetLevel(int level) final
Definition: rev.h:206
const mapped_type & FindOrDie(key_type key) const
Definition: rev.h:172
void SaveState(T *object)
Definition: rev.h:61
T & MutableRef(IndexType index)
Definition: rev.h:95
IntegerType size() const
Definition: bitset.h:775
void Set(IntegerType index)
Definition: bitset.h:809
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:819
void Resize(IntegerType size)
Definition: bitset.h:795
void ClearAndResize(IntegerType size)
Definition: bitset.h:784
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:546
void RegisterReversibleClass(int id, ReversibleInterface *rev)
Definition: integer.cc:2067
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2051
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2028
void Untrail(const Trail &trail, int literal_trail_index) final
Definition: integer.cc:2004
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
Definition: integer.cc:284
LiteralIndex SearchForLiteralAtOrBefore(IntegerLiteral i, IntegerValue *bound) const
Definition: integer.cc:498
const std::vector< IntegerLiteral > NewlyFixedIntegerLiterals() const
Definition: integer.h:488
LiteralIndex GetAssociatedLiteral(IntegerLiteral i_lit) const
Definition: integer.cc:489
void FullyEncodeVariable(IntegerVariable var)
Definition: integer.cc:66
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
Definition: integer.cc:220
void AssociateToIntegerEqualValue(Literal literal, IntegerVariable var, IntegerValue value)
Definition: integer.cc:344
std::vector< ValueLiteralPair > PartialDomainEncoding(IntegerVariable var) const
Definition: integer.cc:137
bool LiteralIsAssociated(IntegerLiteral i_lit) const
Definition: integer.cc:482
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
Definition: integer.cc:131
bool VariableIsFullyEncoded(IntegerVariable var) const
Definition: integer.cc:96
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
Definition: integer.h:469
std::vector< ValueLiteralPair > RawDomainEncoding(IntegerVariable var) const
Definition: integer.cc:167
LiteralIndex GetAssociatedEqualityLiteral(IntegerVariable var, IntegerValue value) const
Definition: integer.cc:274
void AssociateToIntegerLiteral(Literal literal, IntegerLiteral i_lit)
Definition: integer.cc:318
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:238
IntegerVariable FirstUnassignedVariable() const
Definition: integer.cc:1265
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1048
IntegerVariable GetOrCreateConstantIntegerVariable(IntegerValue value)
Definition: integer.cc:735
void RegisterWatcher(SparseBitset< IntegerVariable > *p)
Definition: integer.h:917
bool Propagate(Trail *trail) final
Definition: integer.cc:519
void ReserveSpaceForNumVariables(int num_vars)
Definition: integer.cc:632
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
Definition: integer.cc:756
bool IsCurrentlyIgnored(IntegerVariable i) const
Definition: integer.h:705
std::vector< Literal > ReasonFor(IntegerLiteral literal) const
Definition: integer.cc:1637
std::function< void(IntegerLiteral literal_to_explain, int trail_index_of_literal, std::vector< Literal > *literals, std::vector< int > *dependencies)> LazyReasonFunction
Definition: integer.h:883
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1453
LiteralIndex OptionalLiteralIndex(IntegerVariable i) const
Definition: integer.h:714
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
Definition: integer.cc:1785
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.h:924
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1162
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
Definition: integer.cc:1232
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1449
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1030
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:848
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1529
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:826
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
Definition: integer.cc:1805
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1445
void MergeReasonInto(absl::Span< const IntegerLiteral > literals, std::vector< Literal > *output) const
Definition: integer.cc:1645
Literal IsIgnoredLiteral(IntegerVariable i) const
Definition: integer.h:710
bool IsOptional(IntegerVariable i) const
Definition: integer.h:702
ABSL_MUST_USE_RESULT bool ConditionalEnqueue(Literal lit, IntegerLiteral i_lit, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason)
Definition: integer.cc:1055
bool IntegerLiteralIsFalse(IntegerLiteral l) const
Definition: integer.h:1523
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:960
IntegerVariable AddIntegerVariable()
Definition: integer.h:693
void RegisterReversibleClass(ReversibleInterface *rev)
Definition: integer.h:947
const Domain & InitialVariableDomain(IntegerVariable var) const
Definition: integer.cc:685
void Untrail(const Trail &trail, int literal_trail_index) final
Definition: integer.cc:582
IntegerVariable NumIntegerVariables() const
Definition: integer.h:645
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
Definition: integer.cc:689
LiteralIndex Index() const
Definition: sat_base.h:87
std::string DebugString() const
Definition: sat_base.h:96
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
bool AddClauseDuringSearch(absl::Span< const Literal > literals)
Definition: sat_solver.cc:157
BooleanVariable NewBooleanVariable()
Definition: sat_solver.h:87
const VariablesAssignment & Assignment() const
Definition: sat_solver.h:378
const std::vector< Decision > & Decisions() const
Definition: sat_solver.h:375
bool AddBinaryClause(Literal a, Literal b)
Definition: sat_solver.cc:189
void Backtrack(int target_level)
Definition: sat_solver.cc:991
bool AddUnitClause(Literal true_literal)
Definition: sat_solver.cc:185
void Enqueue(Literal true_literal, int propagator_id)
Definition: sat_base.h:253
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
Definition: sat_base.h:323
const VariablesAssignment & Assignment() const
Definition: sat_base.h:383
std::vector< Literal > * MutableConflict()
Definition: sat_base.h:364
const AssignmentInfo & Info(BooleanVariable var) const
Definition: sat_base.h:384
void EnqueueWithUnitReason(Literal true_literal)
Definition: sat_base.h:268
bool LiteralIsAssigned(Literal literal) const
Definition: sat_base.h:156
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:153
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:150
int64_t b
int64_t a
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
GRBmodel * model
MPCallback * callback
int index
const int INFO
Definition: log_severity.h:31
const bool DEBUG_MODE
Definition: macros.h:24
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
absl::InlinedVector< IntegerLiteral, 2 > InlinedIntegerLiteralVector
Definition: integer.h:226
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc:89
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:935
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:149
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:47
std::function< void(Model *)> ExcludeCurrentSolutionWithoutIgnoredVariableAndBacktrack()
Definition: integer.cc:2078
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
Definition: integer.h:155
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:145
Collection of objects used to extend the Constraint Solver library.
int64_t CapProd(int64_t x, int64_t y)
Literal literal
Definition: optimization.cc:89
ColIndex representative
IntervalVar * interval
Definition: resource.cc:100
int64_t bound
std::optional< int64_t > end
int64_t start
Represents a closed interval [start, end].
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1393
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1387
const double coeff