OR-Tools  9.3
all_different.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
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"
27#include "ortools/sat/integer.h"
28#include "ortools/sat/model.h"
31#include "ortools/util/sort.h"
33
34namespace operations_research {
35namespace sat {
36
37std::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
71std::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
82std::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
98std::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
185LiteralIndex 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
192inline 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
201bool 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_);
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
470void 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
481int 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
500bool 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
552bool 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
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
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:1561
void WatchAffineExpression(AffineExpression e, int id)
Definition: integer.h:1278
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2051
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2028
void FullyEncodeVariable(IntegerVariable var)
Definition: integer.cc:66
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
Definition: integer.cc:131
bool VariableIsFullyEncoded(IntegerVariable var) const
Definition: integer.cc:96
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1449
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1030
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1445
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:323
const VariablesAssignment & Assignment() const
Definition: sat_base.h:383
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
Definition: sat_base.h:287
std::vector< Literal > * MutableConflict()
Definition: sat_base.h:364
bool VariableIsAssigned(BooleanVariable var) const
Definition: sat_base.h:161
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:150
int64_t b
int64_t a
Block * next
SharedBoundsManager * bounds
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double lower_bound
GRBmodel * model
int index
const int FATAL
Definition: log_severity.h:32
const bool DEBUG_MODE
Definition: macros.h:24
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
std::function< std::vector< ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1783
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:935
const IntegerVariable kNoIntegerVariable(-1)
const LiteralIndex kTrueLiteralIndex(-2)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:921
const LiteralIndex kFalseLiteralIndex(-3)
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
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
STL namespace.
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:1416
IntegerLiteral LowerOrEqual(IntegerValue bound) const
Definition: integer.h:1432