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