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