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