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