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