OR-Tools  9.6
all_different.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <functional>
19 #include <limits>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/container/btree_map.h"
24 #include "absl/types/span.h"
25 #include "ortools/base/logging.h"
27 #include "ortools/sat/integer.h"
28 #include "ortools/sat/model.h"
29 #include "ortools/sat/sat_base.h"
30 #include "ortools/sat/sat_solver.h"
31 #include "ortools/util/sort.h"
33 
34 namespace operations_research {
35 namespace sat {
36 
37 std::function<void(Model*)> AllDifferentBinary(
38  const std::vector<IntegerVariable>& vars) {
39  return [=](Model* model) {
40  // Fully encode all the given variables and construct a mapping value ->
41  // List of literal each indicating that a given variable takes this value.
42  //
43  // Note that we use a map to always add the constraints in the same order.
44  absl::btree_map<IntegerValue, std::vector<Literal>> value_to_literals;
45  IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
46  for (const IntegerVariable var : vars) {
48  for (const auto& entry : encoder->FullDomainEncoding(var)) {
49  value_to_literals[entry.value].push_back(entry.literal);
50  }
51  }
52 
53  // Add an at most one constraint for each value.
54  for (const auto& entry : value_to_literals) {
55  if (entry.second.size() > 1) {
56  model->Add(AtMostOneConstraint(entry.second));
57  }
58  }
59 
60  // If the number of values is equal to the number of variables, we have
61  // a permutation. We can add a bool_or for each literals attached to a
62  // value.
63  if (value_to_literals.size() == vars.size()) {
64  for (const auto& entry : value_to_literals) {
65  model->Add(ClauseConstraint(entry.second));
66  }
67  }
68  };
69 }
70 
71 std::function<void(Model*)> AllDifferentOnBounds(
72  const std::vector<AffineExpression>& expressions) {
73  return [=](Model* model) {
74  if (expressions.empty()) return;
75  auto* constraint = new AllDifferentBoundsPropagator(
76  expressions, model->GetOrCreate<IntegerTrail>());
77  constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
78  model->TakeOwnership(constraint);
79  };
80 }
81 
82 std::function<void(Model*)> AllDifferentOnBounds(
83  const std::vector<IntegerVariable>& vars) {
84  return [=](Model* model) {
85  if (vars.empty()) return;
86  std::vector<AffineExpression> expressions;
87  expressions.reserve(vars.size());
88  for (const IntegerVariable var : vars) {
89  expressions.push_back(AffineExpression(var));
90  }
91  auto* constraint = new AllDifferentBoundsPropagator(
92  expressions, model->GetOrCreate<IntegerTrail>());
93  constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
94  model->TakeOwnership(constraint);
95  };
96 }
97 
98 std::function<void(Model*)> AllDifferentAC(
99  const std::vector<IntegerVariable>& variables) {
100  return [=](Model* model) {
101  if (variables.size() < 3) return;
102 
104  variables, model->GetOrCreate<IntegerEncoder>(),
105  model->GetOrCreate<Trail>(), model->GetOrCreate<IntegerTrail>());
106  constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
107  model->TakeOwnership(constraint);
108  };
109 }
110 
112  std::vector<IntegerVariable> variables, IntegerEncoder* encoder,
113  Trail* trail, IntegerTrail* integer_trail)
114  : num_variables_(variables.size()),
115  variables_(std::move(variables)),
116  trail_(trail),
117  integer_trail_(integer_trail) {
118  // Initialize literals cache.
119  int64_t min_value = std::numeric_limits<int64_t>::max();
120  int64_t max_value = std::numeric_limits<int64_t>::min();
121  variable_min_value_.resize(num_variables_);
122  variable_max_value_.resize(num_variables_);
123  variable_literal_index_.resize(num_variables_);
124  int num_fixed_variables = 0;
125  for (int x = 0; x < num_variables_; x++) {
126  variable_min_value_[x] = integer_trail_->LowerBound(variables_[x]).value();
127  variable_max_value_[x] = integer_trail_->UpperBound(variables_[x]).value();
128 
129  // Compute value range of all variables.
130  min_value = std::min(min_value, variable_min_value_[x]);
131  max_value = std::max(max_value, variable_max_value_[x]);
132 
133  // FullyEncode does not like 1-value domains, handle this case first.
134  // TODO(user): Prune now, ignore these variables during solving.
135  if (variable_min_value_[x] == variable_max_value_[x]) {
136  num_fixed_variables++;
137  variable_literal_index_[x].push_back(kTrueLiteralIndex);
138  continue;
139  }
140 
141  // Force full encoding if not already done.
142  if (!encoder->VariableIsFullyEncoded(variables_[x])) {
143  encoder->FullyEncodeVariable(variables_[x]);
144  }
145 
146  // Fill cache with literals, default value is kFalseLiteralIndex.
147  int64_t size = variable_max_value_[x] - variable_min_value_[x] + 1;
148  variable_literal_index_[x].resize(size, kFalseLiteralIndex);
149  for (const auto& entry : encoder->FullDomainEncoding(variables_[x])) {
150  int64_t value = entry.value.value();
151  // Can happen because of initial propagation!
152  if (value < variable_min_value_[x] || variable_max_value_[x] < value) {
153  continue;
154  }
155  variable_literal_index_[x][value - variable_min_value_[x]] =
156  entry.literal.Index();
157  }
158  }
159  min_all_values_ = min_value;
160  num_all_values_ = max_value - min_value + 1;
161 
162  successor_.resize(num_variables_);
163  variable_to_value_.assign(num_variables_, -1);
164  visiting_.resize(num_variables_);
165  variable_visited_from_.resize(num_variables_);
166  residual_graph_successors_.resize(num_variables_ + num_all_values_ + 1);
167  component_number_.resize(num_variables_ + num_all_values_ + 1);
168 }
169 
171  const int id = watcher->Register(this);
172  watcher->SetPropagatorPriority(id, 2);
173  for (const auto& literal_indices : variable_literal_index_) {
174  for (const LiteralIndex li : literal_indices) {
175  // Watch only unbound literals.
176  if (li >= 0 &&
177  !trail_->Assignment().VariableIsAssigned(Literal(li).Variable())) {
178  watcher->WatchLiteral(Literal(li), id);
179  watcher->WatchLiteral(Literal(li).Negated(), id);
180  }
181  }
182  }
183 }
184 
185 LiteralIndex AllDifferentConstraint::VariableLiteralIndexOf(int x,
186  int64_t value) {
187  return (value < variable_min_value_[x] || variable_max_value_[x] < value)
189  : variable_literal_index_[x][value - variable_min_value_[x]];
190 }
191 
192 inline bool AllDifferentConstraint::VariableHasPossibleValue(int x,
193  int64_t value) {
194  LiteralIndex li = VariableLiteralIndexOf(x, value);
195  if (li == kFalseLiteralIndex) return false;
196  if (li == kTrueLiteralIndex) return true;
197  DCHECK_GE(li, 0);
198  return !trail_->Assignment().LiteralIsFalse(Literal(li));
199 }
200 
201 bool AllDifferentConstraint::MakeAugmentingPath(int start) {
202  // Do a BFS and use visiting_ as a queue, with num_visited pointing
203  // at its begin() and num_to_visit its end().
204  // To switch to the augmenting path once a nonmatched value was found,
205  // we remember the BFS tree in variable_visited_from_.
206  int num_to_visit = 0;
207  int num_visited = 0;
208  // Enqueue start.
209  visiting_[num_to_visit++] = start;
210  variable_visited_[start] = true;
211  variable_visited_from_[start] = -1;
212 
213  while (num_visited < num_to_visit) {
214  // Dequeue node to visit.
215  const int node = visiting_[num_visited++];
216 
217  for (const int value : successor_[node]) {
218  if (value_visited_[value]) continue;
219  value_visited_[value] = true;
220  if (value_to_variable_[value] == -1) {
221  // value is not matched: change path from node to start, and return.
222  int path_node = node;
223  int path_value = value;
224  while (path_node != -1) {
225  int old_value = variable_to_value_[path_node];
226  variable_to_value_[path_node] = path_value;
227  value_to_variable_[path_value] = path_node;
228  path_node = variable_visited_from_[path_node];
229  path_value = old_value;
230  }
231  return true;
232  } else {
233  // Enqueue node matched to value.
234  const int next_node = value_to_variable_[value];
235  variable_visited_[next_node] = true;
236  visiting_[num_to_visit++] = next_node;
237  variable_visited_from_[next_node] = node;
238  }
239  }
240  }
241  return false;
242 }
243 
244 // The algorithm copies the solver state to successor_, which is used to compute
245 // a matching. If all variables can be matched, it generates the residual graph
246 // in separate vectors, computes its SCCs, and filters variable -> value if
247 // variable is not in the same SCC as value.
248 // Explanations for failure and filtering are fine-grained:
249 // failure is explained by a Hall set, i.e. dom(variables) \subseteq {values},
250 // with |variables| < |values|; filtering is explained by the Hall set that
251 // would happen if the variable was assigned to the value.
252 //
253 // TODO(user): If needed, there are several ways performance could be
254 // improved.
255 // If copying the variable state is too costly, it could be maintained instead.
256 // If the propagator has too many fruitless calls (without failing/pruning),
257 // we can remember the O(n) arcs used in the matching and the SCC decomposition,
258 // and guard calls to Propagate() if these arcs are still valid.
260  // Copy variable state to graph state.
261  prev_matching_ = variable_to_value_;
262  value_to_variable_.assign(num_all_values_, -1);
263  variable_to_value_.assign(num_variables_, -1);
264  for (int x = 0; x < num_variables_; x++) {
265  successor_[x].clear();
266  const int64_t min_value = integer_trail_->LowerBound(variables_[x]).value();
267  const int64_t max_value = integer_trail_->UpperBound(variables_[x]).value();
268  for (int64_t value = min_value; value <= max_value; value++) {
269  if (VariableHasPossibleValue(x, value)) {
270  const int offset_value = value - min_all_values_;
271  // Forward-checking should propagate x != value.
272  successor_[x].push_back(offset_value);
273  }
274  }
275  if (successor_[x].size() == 1) {
276  const int offset_value = successor_[x][0];
277  if (value_to_variable_[offset_value] == -1) {
278  value_to_variable_[offset_value] = x;
279  variable_to_value_[x] = offset_value;
280  }
281  }
282  }
283 
284  // Because we currently propagates all clauses before entering this
285  // propagator, we known that this can't happen.
286  if (DEBUG_MODE) {
287  for (int x = 0; x < num_variables_; x++) {
288  for (const int offset_value : successor_[x]) {
289  if (value_to_variable_[offset_value] != -1 &&
290  value_to_variable_[offset_value] != x) {
291  LOG(FATAL) << "Should have been propagated by AllDifferentBinary()!";
292  }
293  }
294  }
295  }
296 
297  // Seed with previous matching.
298  for (int x = 0; x < num_variables_; x++) {
299  if (variable_to_value_[x] != -1) continue;
300  const int prev_value = prev_matching_[x];
301  if (prev_value == -1 || value_to_variable_[prev_value] != -1) continue;
302 
303  if (VariableHasPossibleValue(x, prev_matching_[x] + min_all_values_)) {
304  variable_to_value_[x] = prev_matching_[x];
305  value_to_variable_[prev_matching_[x]] = x;
306  }
307  }
308 
309  // Compute max matching.
310  int x = 0;
311  for (; x < num_variables_; x++) {
312  if (variable_to_value_[x] == -1) {
313  value_visited_.assign(num_all_values_, false);
314  variable_visited_.assign(num_variables_, false);
315  MakeAugmentingPath(x);
316  }
317  if (variable_to_value_[x] == -1) break; // No augmenting path exists.
318  }
319 
320  // Fail if covering variables impossible.
321  // Explain with the forbidden parts of the graph that prevent
322  // MakeAugmentingPath from increasing the matching size.
323  if (x < num_variables_) {
324  // For now explain all forbidden arcs.
325  std::vector<Literal>* conflict = trail_->MutableConflict();
326  conflict->clear();
327  for (int y = 0; y < num_variables_; y++) {
328  if (!variable_visited_[y]) continue;
329  for (int value = variable_min_value_[y]; value <= variable_max_value_[y];
330  value++) {
331  const LiteralIndex li = VariableLiteralIndexOf(y, value);
332  if (li >= 0 && !value_visited_[value - min_all_values_]) {
333  DCHECK(trail_->Assignment().LiteralIsFalse(Literal(li)));
334  conflict->push_back(Literal(li));
335  }
336  }
337  }
338  return false;
339  }
340 
341  // The current matching is a valid solution, now try to filter values.
342  // Build residual graph, compute its SCCs.
343  for (int x = 0; x < num_variables_; x++) {
344  residual_graph_successors_[x].clear();
345  for (const int succ : successor_[x]) {
346  if (succ != variable_to_value_[x]) {
347  residual_graph_successors_[x].push_back(num_variables_ + succ);
348  }
349  }
350  }
351  for (int offset_value = 0; offset_value < num_all_values_; offset_value++) {
352  residual_graph_successors_[num_variables_ + offset_value].clear();
353  if (value_to_variable_[offset_value] != -1) {
354  residual_graph_successors_[num_variables_ + offset_value].push_back(
355  value_to_variable_[offset_value]);
356  }
357  }
358  const int dummy_node = num_variables_ + num_all_values_;
359  residual_graph_successors_[dummy_node].clear();
360  if (num_variables_ < num_all_values_) {
361  for (int x = 0; x < num_variables_; x++) {
362  residual_graph_successors_[dummy_node].push_back(x);
363  }
364  for (int offset_value = 0; offset_value < num_all_values_; offset_value++) {
365  if (value_to_variable_[offset_value] == -1) {
366  residual_graph_successors_[num_variables_ + offset_value].push_back(
367  dummy_node);
368  }
369  }
370  }
371 
372  // Compute SCCs, make node -> component map.
373  struct SccOutput {
374  explicit SccOutput(std::vector<int>* c) : components(c) {}
375  void emplace_back(int const* b, int const* e) {
376  for (int const* it = b; it < e; ++it) {
377  (*components)[*it] = num_components;
378  }
379  ++num_components;
380  }
381  int num_components = 0;
382  std::vector<int>* components;
383  };
384  SccOutput scc_output(&component_number_);
386  static_cast<int>(residual_graph_successors_.size()),
387  residual_graph_successors_, &scc_output);
388 
389  // Remove arcs var -> val where SCC(var) -/->* SCC(val).
390  for (int x = 0; x < num_variables_; x++) {
391  if (successor_[x].size() == 1) continue;
392  for (const int offset_value : successor_[x]) {
393  const int value_node = offset_value + num_variables_;
394  if (variable_to_value_[x] != offset_value &&
395  component_number_[x] != component_number_[value_node] &&
396  VariableHasPossibleValue(x, offset_value + min_all_values_)) {
397  // We can deduce that x != value. To explain, force x == offset_value,
398  // then find another assignment for the variable matched to
399  // offset_value. It will fail: explaining why is the same as
400  // explaining failure as above, and it is an explanation of x != value.
401  value_visited_.assign(num_all_values_, false);
402  variable_visited_.assign(num_variables_, false);
403  // Undo x -> old_value and old_variable -> offset_value.
404  const int old_variable = value_to_variable_[offset_value];
405  variable_to_value_[old_variable] = -1;
406  const int old_value = variable_to_value_[x];
407  value_to_variable_[old_value] = -1;
408  variable_to_value_[x] = offset_value;
409  value_to_variable_[offset_value] = x;
410 
411  value_visited_[offset_value] = true;
412  MakeAugmentingPath(old_variable);
413  DCHECK_EQ(variable_to_value_[old_variable], -1); // No reassignment.
414 
415  std::vector<Literal>* reason = trail_->GetEmptyVectorToStoreReason();
416  for (int y = 0; y < num_variables_; y++) {
417  if (!variable_visited_[y]) continue;
418  for (int value = variable_min_value_[y];
419  value <= variable_max_value_[y]; value++) {
420  const LiteralIndex li = VariableLiteralIndexOf(y, value);
421  if (li >= 0 && !value_visited_[value - min_all_values_]) {
422  DCHECK(!VariableHasPossibleValue(y, value));
423  reason->push_back(Literal(li));
424  }
425  }
426  }
427 
428  const LiteralIndex li =
429  VariableLiteralIndexOf(x, offset_value + min_all_values_);
430  DCHECK_NE(li, kTrueLiteralIndex);
431  DCHECK_NE(li, kFalseLiteralIndex);
432  return trail_->EnqueueWithStoredReason(Literal(li).Negated());
433  }
434  }
435  }
436 
437  return true;
438 }
439 
441  const std::vector<AffineExpression>& expressions,
442  IntegerTrail* integer_trail)
443  : integer_trail_(integer_trail) {
444  CHECK(!expressions.empty());
445 
446  // We need +2 for sentinels.
447  const int capacity = expressions.size() + 2;
448  index_to_start_index_.resize(capacity);
449  index_to_end_index_.resize(capacity);
450  index_is_present_.resize(capacity, false);
451  index_to_expr_.resize(capacity, kNoIntegerVariable);
452 
453  for (int i = 0; i < expressions.size(); ++i) {
454  bounds_.push_back({expressions[i]});
455  negated_bounds_.push_back({expressions[i].Negated()});
456  }
457 }
458 
460  if (!PropagateLowerBounds()) return false;
461 
462  // Note that it is not required to swap back bounds_ and negated_bounds_.
463  // TODO(user): investigate the impact.
464  std::swap(bounds_, negated_bounds_);
465  const bool result = PropagateLowerBounds();
466  std::swap(bounds_, negated_bounds_);
467  return result;
468 }
469 
470 void AllDifferentBoundsPropagator::FillHallReason(IntegerValue hall_lb,
471  IntegerValue hall_ub) {
472  integer_reason_.clear();
473  const int limit = GetIndex(hall_ub);
474  for (int i = GetIndex(hall_lb); i <= limit; ++i) {
475  const AffineExpression expr = index_to_expr_[i];
476  integer_reason_.push_back(expr.GreaterOrEqual(hall_lb));
477  integer_reason_.push_back(expr.LowerOrEqual(hall_ub));
478  }
479 }
480 
481 int AllDifferentBoundsPropagator::FindStartIndexAndCompressPath(int index) {
482  // First, walk the pointer until we find one pointing to itself.
483  int start_index = index;
484  while (true) {
485  const int next = index_to_start_index_[start_index];
486  if (start_index == next) break;
487  start_index = next;
488  }
489 
490  // Second, redo the same thing and make everyone point to the representative.
491  while (true) {
492  const int next = index_to_start_index_[index];
493  if (start_index == next) break;
494  index_to_start_index_[index] = start_index;
495  index = next;
496  }
497  return start_index;
498 }
499 
500 bool AllDifferentBoundsPropagator::PropagateLowerBounds() {
501  // Start by filling the cached bounds and sorting by increasing lb.
502  for (CachedBounds& entry : bounds_) {
503  entry.lb = integer_trail_->LowerBound(entry.expr);
504  entry.ub = integer_trail_->UpperBound(entry.expr);
505  }
506  IncrementalSort(bounds_.begin(), bounds_.end(),
507  [](CachedBounds a, CachedBounds b) { return a.lb < b.lb; });
508 
509  // We will split the affine epressions in vars sorted by lb in contiguous
510  // subset with index of the form [start, start + num_in_window).
511  int start = 0;
512  int num_in_window = 1;
513 
514  // Minimum lower bound in the current window.
515  IntegerValue min_lb = bounds_.front().lb;
516 
517  const int size = bounds_.size();
518  for (int i = 1; i < size; ++i) {
519  const IntegerValue lb = bounds_[i].lb;
520 
521  // If the lower bounds of all the other variables is greater, then it can
522  // never fall into a potential hall interval formed by the variable in the
523  // current window, so we can split the problem into independent parts.
524  if (lb <= min_lb + IntegerValue(num_in_window - 1)) {
525  ++num_in_window;
526  continue;
527  }
528 
529  // Process the current window.
530  if (num_in_window > 1) {
531  absl::Span<CachedBounds> window(&bounds_[start], num_in_window);
532  if (!PropagateLowerBoundsInternal(min_lb, window)) {
533  return false;
534  }
535  }
536 
537  // Start of the next window.
538  start = i;
539  num_in_window = 1;
540  min_lb = lb;
541  }
542 
543  // Take care of the last window.
544  if (num_in_window > 1) {
545  absl::Span<CachedBounds> window(&bounds_[start], num_in_window);
546  return PropagateLowerBoundsInternal(min_lb, window);
547  }
548 
549  return true;
550 }
551 
552 bool AllDifferentBoundsPropagator::PropagateLowerBoundsInternal(
553  IntegerValue min_lb, absl::Span<CachedBounds> bounds) {
554  hall_starts_.clear();
555  hall_ends_.clear();
556 
557  // All cached lb in bounds will be in [min_lb, min_lb + bounds_.size()).
558  // Make sure we change our base_ so that GetIndex() fit in our buffers.
559  base_ = min_lb - IntegerValue(1);
560 
561  // Sparse cleaning of index_is_present_.
562  for (const int i : indices_to_clear_) {
563  index_is_present_[i] = false;
564  }
565  indices_to_clear_.clear();
566 
567  // Sort bounds by increasing ub.
568  std::sort(bounds.begin(), bounds.end(),
569  [](CachedBounds a, CachedBounds b) { return a.ub < b.ub; });
570  for (const CachedBounds entry : bounds) {
571  const AffineExpression expr = entry.expr;
572 
573  // Note that it is important to use the cache to make sure GetIndex() is
574  // not out of bound in case integer_trail_->LowerBound() changed when we
575  // pushed something.
576  const IntegerValue lb = entry.lb;
577  const int lb_index = GetIndex(lb);
578  const bool value_is_covered = index_is_present_[lb_index];
579 
580  // Check if lb is in an Hall interval, and push it if this is the case.
581  if (value_is_covered) {
582  const int hall_index =
583  std::lower_bound(hall_ends_.begin(), hall_ends_.end(), lb) -
584  hall_ends_.begin();
585  if (hall_index < hall_ends_.size() && hall_starts_[hall_index] <= lb) {
586  const IntegerValue hs = hall_starts_[hall_index];
587  const IntegerValue he = hall_ends_[hall_index];
588  FillHallReason(hs, he);
589  integer_reason_.push_back(expr.GreaterOrEqual(hs));
590  if (!integer_trail_->SafeEnqueue(expr.GreaterOrEqual(he + 1),
591  integer_reason_)) {
592  return false;
593  }
594  }
595  }
596 
597  // Update our internal representation of the non-consecutive intervals.
598  //
599  // If lb is not used, we add a node there, otherwise we add it to the
600  // right of the interval that contains lb. In both cases, if there is an
601  // interval to the left (resp. right) we merge them.
602  int new_index = lb_index;
603  int start_index = lb_index;
604  int end_index = lb_index;
605  if (value_is_covered) {
606  start_index = FindStartIndexAndCompressPath(new_index);
607  new_index = index_to_end_index_[start_index] + 1;
608  end_index = new_index;
609  } else {
610  if (index_is_present_[new_index - 1]) {
611  start_index = FindStartIndexAndCompressPath(new_index - 1);
612  }
613  }
614  if (index_is_present_[new_index + 1]) {
615  end_index = index_to_end_index_[new_index + 1];
616  index_to_start_index_[new_index + 1] = start_index;
617  }
618 
619  // Update the end of the representative.
620  index_to_end_index_[start_index] = end_index;
621 
622  // This is the only place where we "add" a new node.
623  {
624  index_to_start_index_[new_index] = start_index;
625  index_to_expr_[new_index] = expr;
626  index_is_present_[new_index] = true;
627  indices_to_clear_.push_back(new_index);
628  }
629 
630  // In most situation, we cannot have a conflict now, because it should have
631  // been detected before by pushing an interval lower bound past its upper
632  // bound. However, it is possible that when we push one bound, other bounds
633  // change. So if the upper bound is smaller than the current interval end,
634  // we abort so that the conflit reason will be better on the next call to
635  // the propagator.
636  const IntegerValue end = GetValue(end_index);
637  if (end > integer_trail_->UpperBound(expr)) return true;
638 
639  // If we have a new Hall interval, add it to the set. Note that it will
640  // always be last, and if it overlaps some previous Hall intervals, it
641  // always overlaps them fully.
642  //
643  // Note: It is okay to not use entry.ub here if we want to fetch the last
644  // value, but in practice it shouldn't really change when we push a
645  // lower_bound and it is faster to use the cached entry.
646  if (end == entry.ub) {
647  const IntegerValue start = GetValue(start_index);
648  while (!hall_starts_.empty() && start <= hall_starts_.back()) {
649  hall_starts_.pop_back();
650  hall_ends_.pop_back();
651  }
652  DCHECK(hall_ends_.empty() || hall_ends_.back() < start);
653  hall_starts_.push_back(start);
654  hall_ends_.push_back(end);
655  }
656  }
657  return true;
658 }
659 
661  GenericLiteralWatcher* watcher) {
662  const int id = watcher->Register(this);
663  for (const CachedBounds& entry : bounds_) {
664  watcher->WatchAffineExpression(entry.expr, id);
665  }
667 }
668 
669 } // namespace sat
670 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void RegisterWith(GenericLiteralWatcher *watcher)
AllDifferentBoundsPropagator(const std::vector< AffineExpression > &expressions, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
AllDifferentConstraint(std::vector< IntegerVariable > variables, IntegerEncoder *encoder, Trail *trail, IntegerTrail *integer_trail)
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition: integer.h:1673
void WatchAffineExpression(AffineExpression e, int id)
Definition: integer.h:1376
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2309
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2286
void FullyEncodeVariable(IntegerVariable var)
Definition: integer.cc:74
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
Definition: integer.cc:140
bool VariableIsFullyEncoded(IntegerVariable var) const
Definition: integer.cc:105
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1561
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1211
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1557
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
Definition: sat_base.h:332
std::vector< Literal > * MutableConflict()
Definition: sat_base.h:373
const VariablesAssignment & Assignment() const
Definition: sat_base.h:402
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
Definition: sat_base.h:296
bool VariableIsAssigned(BooleanVariable var) const
Definition: sat_base.h:172
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:161
int64_t b
int64_t a
Block * next
SharedBoundsManager * bounds
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
int index
const bool DEBUG_MODE
Definition: macros.h:24
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:946
const IntegerVariable kNoIntegerVariable(-1)
const LiteralIndex kTrueLiteralIndex(-2)
const LiteralIndex kFalseLiteralIndex(-3)
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:932
std::function< std::vector< ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1894
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
Collection of objects used to extend the Constraint Solver library.
void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, Compare comp=Compare{}, bool is_stable=false)
Definition: sort.h:46
IntVar * lower_bound
Definition: routing.cc:1086
int64_t capacity
std::optional< int64_t > end
int64_t start
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
Definition: integer.h:1528
IntegerLiteral LowerOrEqual(IntegerValue bound) const
Definition: integer.h:1544