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