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