Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
alldiff_cst.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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//
15// AllDifferent constraints
16
17#include <algorithm>
18#include <cstdint>
19#include <memory>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include "absl/strings/str_format.h"
25#include "absl/strings/string_view.h"
27#include "ortools/base/types.h"
31
32namespace operations_research {
33namespace {
34
35class BaseAllDifferent : public Constraint {
36 public:
37 BaseAllDifferent(Solver* const s, const std::vector<IntVar*>& vars)
38 : Constraint(s), vars_(vars) {}
39 ~BaseAllDifferent() override {}
40 std::string DebugStringInternal(absl::string_view name) const {
41 return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", "));
42 }
43
44 protected:
45 const std::vector<IntVar*> vars_;
46 int64_t size() const { return vars_.size(); }
47};
48
49//-----------------------------------------------------------------------------
50// ValueAllDifferent
51
52class ValueAllDifferent : public BaseAllDifferent {
53 public:
54 ValueAllDifferent(Solver* const s, const std::vector<IntVar*>& vars)
55 : BaseAllDifferent(s, vars) {}
56 ~ValueAllDifferent() override {}
57
58 void Post() override;
59 void InitialPropagate() override;
60 void OneMove(int index);
61 bool AllMoves();
62
63 std::string DebugString() const override {
64 return DebugStringInternal("ValueAllDifferent");
65 }
66 void Accept(ModelVisitor* const visitor) const override {
67 visitor->BeginVisitConstraint(ModelVisitor::kAllDifferent, this);
68 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
69 vars_);
70 visitor->VisitIntegerArgument(ModelVisitor::kRangeArgument, 0);
71 visitor->EndVisitConstraint(ModelVisitor::kAllDifferent, this);
72 }
73
74 private:
75 RevSwitch all_instantiated_;
76};
77
78void ValueAllDifferent::Post() {
79 for (int i = 0; i < size(); ++i) {
80 IntVar* var = vars_[i];
81 Demon* d = MakeConstraintDemon1(solver(), this, &ValueAllDifferent::OneMove,
82 "OneMove", i);
83 var->WhenBound(d);
84 }
85}
86
87void ValueAllDifferent::InitialPropagate() {
88 for (int i = 0; i < size(); ++i) {
89 if (vars_[i]->Bound()) {
90 OneMove(i);
91 }
92 }
93}
94
95void ValueAllDifferent::OneMove(int index) {
96 if (!AllMoves()) {
97 const int64_t val = vars_[index]->Value();
98 for (int j = 0; j < size(); ++j) {
99 if (index != j) {
100 if (vars_[j]->Size() < 0xFFFFFF) {
101 vars_[j]->RemoveValue(val);
102 } else {
103 solver()->AddConstraint(solver()->MakeNonEquality(vars_[j], val));
104 }
105 }
106 }
107 }
108}
109
110bool ValueAllDifferent::AllMoves() {
111 if (all_instantiated_.Switched() || size() == 0) {
112 return true;
113 }
114 for (int i = 0; i < size(); ++i) {
115 if (!vars_[i]->Bound()) {
116 return false;
117 }
118 }
119 std::unique_ptr<int64_t[]> values(new int64_t[size()]);
120 for (int i = 0; i < size(); ++i) {
121 values[i] = vars_[i]->Value();
122 }
123 std::sort(values.get(), values.get() + size());
124 for (int i = 0; i < size() - 1; ++i) {
125 if (values[i] == values[i + 1]) {
126 values.reset(); // prevent leaks (solver()->Fail() won't return)
127 solver()->Fail();
128 }
129 }
130 all_instantiated_.Switch(solver());
131 return true;
132}
133
134// ---------- Bounds All Different ----------
135// See http://www.cs.uwaterloo.ca/~cquimper/Papers/ijcai03_TR.pdf for details.
136
137class RangeBipartiteMatching {
138 public:
139 struct Interval {
140 int64_t min;
141 int64_t max;
144 };
145
146 RangeBipartiteMatching(Solver* const solver, int size)
147 : solver_(solver),
148 size_(size),
149 intervals_(new Interval[size + 1]),
150 min_sorted_(new Interval*[size]),
151 max_sorted_(new Interval*[size]),
152 bounds_(new int64_t[2 * size + 2]),
153 tree_(new int[2 * size + 2]),
154 diff_(new int64_t[2 * size + 2]),
155 hall_(new int[2 * size + 2]),
156 active_size_(0) {
157 for (int i = 0; i < size; ++i) {
158 max_sorted_[i] = &intervals_[i];
159 min_sorted_[i] = max_sorted_[i];
160 }
161 }
162
163 void SetRange(int index, int64_t imin, int64_t imax) {
164 intervals_[index].min = imin;
165 intervals_[index].max = imax;
166 }
167
168 bool Propagate() {
169 SortArray();
170
171 const bool modified1 = PropagateMin();
172 const bool modified2 = PropagateMax();
173 return modified1 || modified2;
174 }
175
176 int64_t Min(int index) const { return intervals_[index].min; }
177
178 int64_t Max(int index) const { return intervals_[index].max; }
179
180 private:
181 // This method sorts the min_sorted_ and max_sorted_ arrays and fill
182 // the bounds_ array (and set the active_size_ counter).
183 void SortArray() {
184 std::sort(min_sorted_.get(), min_sorted_.get() + size_,
185 CompareIntervalMin());
186 std::sort(max_sorted_.get(), max_sorted_.get() + size_,
187 CompareIntervalMax());
188
189 int64_t min = min_sorted_[0]->min;
190 int64_t max = max_sorted_[0]->max + 1;
191 int64_t last = min - 2;
192 bounds_[0] = last;
193
194 int i = 0;
195 int j = 0;
196 int nb = 0;
197 for (;;) { // merge min_sorted_[] and max_sorted_[] into bounds_[].
198 if (i < size_ && min <= max) { // make sure min_sorted_ exhausted first.
199 if (min != last) {
200 last = min;
201 bounds_[++nb] = last;
202 }
203 min_sorted_[i]->min_rank = nb;
204 if (++i < size_) {
205 min = min_sorted_[i]->min;
206 }
207 } else {
208 if (max != last) {
209 last = max;
210 bounds_[++nb] = last;
211 }
212 max_sorted_[j]->max_rank = nb;
213 if (++j == size_) {
214 break;
215 }
216 max = max_sorted_[j]->max + 1;
217 }
218 }
219 active_size_ = nb;
220 bounds_[nb + 1] = bounds_[nb] + 2;
221 }
222
223 // These two methods will actually do the new bounds computation.
224 bool PropagateMin() {
225 bool modified = false;
226
227 for (int i = 1; i <= active_size_ + 1; ++i) {
228 hall_[i] = i - 1;
229 tree_[i] = i - 1;
230 diff_[i] = bounds_[i] - bounds_[i - 1];
231 }
232 // visit intervals in increasing max order
233 for (int i = 0; i < size_; ++i) {
234 const int x = max_sorted_[i]->min_rank;
235 const int y = max_sorted_[i]->max_rank;
236 int z = PathMax(tree_.get(), x + 1);
237 int j = tree_[z];
238 if (--diff_[z] == 0) {
239 tree_[z] = z + 1;
240 z = PathMax(tree_.get(), z + 1);
241 tree_[z] = j;
242 }
243 PathSet(x + 1, z, z, tree_.get()); // path compression
244 if (diff_[z] < bounds_[z] - bounds_[y]) {
245 solver_->Fail();
246 }
247 if (hall_[x] > x) {
248 int w = PathMax(hall_.get(), hall_[x]);
249 max_sorted_[i]->min = bounds_[w];
250 PathSet(x, w, w, hall_.get()); // path compression
251 modified = true;
252 }
253 if (diff_[z] == bounds_[z] - bounds_[y]) {
254 PathSet(hall_[y], j - 1, y, hall_.get()); // mark hall interval
255 hall_[y] = j - 1;
256 }
257 }
258 return modified;
259 }
260
261 bool PropagateMax() {
262 bool modified = false;
263
264 for (int i = 0; i <= active_size_; i++) {
265 tree_[i] = i + 1;
266 hall_[i] = i + 1;
267 diff_[i] = bounds_[i + 1] - bounds_[i];
268 }
269 // visit intervals in decreasing min order
270 for (int i = size_ - 1; i >= 0; --i) {
271 const int x = min_sorted_[i]->max_rank;
272 const int y = min_sorted_[i]->min_rank;
273 int z = PathMin(tree_.get(), x - 1);
274 int j = tree_[z];
275 if (--diff_[z] == 0) {
276 tree_[z] = z - 1;
277 z = PathMin(tree_.get(), z - 1);
278 tree_[z] = j;
279 }
280 PathSet(x - 1, z, z, tree_.get());
281 if (diff_[z] < bounds_[y] - bounds_[z]) {
282 solver_->Fail();
283 // useless. Should have been caught by the PropagateMin() method.
284 }
285 if (hall_[x] < x) {
286 int w = PathMin(hall_.get(), hall_[x]);
287 min_sorted_[i]->max = bounds_[w] - 1;
288 PathSet(x, w, w, hall_.get());
289 modified = true;
290 }
291 if (diff_[z] == bounds_[y] - bounds_[z]) {
292 PathSet(hall_[y], j + 1, y, hall_.get());
293 hall_[y] = j + 1;
294 }
295 }
296 return modified;
297 }
298
299 // TODO(user) : use better sort, use bounding boxes of modifications to
300 // improve the sorting (only modified vars).
301
302 // This method is used by the STL sort.
303 struct CompareIntervalMin {
304 bool operator()(const Interval* i1, const Interval* i2) const {
305 return (i1->min < i2->min);
306 }
307 };
308
309 // This method is used by the STL sort.
310 struct CompareIntervalMax {
311 bool operator()(const Interval* i1, const Interval* i2) const {
312 return (i1->max < i2->max);
313 }
314 };
315
316 void PathSet(int start, int end, int to, int* const tree) {
317 int l = start;
318 while (l != end) {
319 int k = l;
320 l = tree[k];
321 tree[k] = to;
322 }
323 }
324
325 int PathMin(const int* const tree, int index) {
326 int i = index;
327 while (tree[i] < i) {
328 i = tree[i];
329 }
330 return i;
331 }
332
333 int PathMax(const int* const tree, int index) {
334 int i = index;
335 while (tree[i] > i) {
336 i = tree[i];
337 }
338 return i;
339 }
340
341 Solver* const solver_;
342 const int size_;
343 std::unique_ptr<Interval[]> intervals_;
344 std::unique_ptr<Interval*[]> min_sorted_;
345 std::unique_ptr<Interval*[]> max_sorted_;
346 // bounds_[1..active_size_] hold set of min & max in the n intervals_
347 // while bounds_[0] and bounds_[active_size_ + 1] allow sentinels.
348 std::unique_ptr<int64_t[]> bounds_;
349 std::unique_ptr<int[]> tree_; // tree links.
350 std::unique_ptr<int64_t[]> diff_; // diffs between critical capacities.
351 std::unique_ptr<int[]> hall_; // hall interval links.
352 int active_size_;
353};
354
355class BoundsAllDifferent : public BaseAllDifferent {
356 public:
357 BoundsAllDifferent(Solver* const s, const std::vector<IntVar*>& vars)
358 : BaseAllDifferent(s, vars), matching_(s, vars.size()) {}
359
360 ~BoundsAllDifferent() override {}
361
362 void Post() override {
364 solver(), this, &BoundsAllDifferent::IncrementalPropagate,
365 "IncrementalPropagate");
366
367 for (int i = 0; i < size(); ++i) {
368 vars_[i]->WhenRange(range);
369 Demon* bound = MakeConstraintDemon1(solver(), this,
370 &BoundsAllDifferent::PropagateValue,
371 "PropagateValue", i);
372 vars_[i]->WhenBound(bound);
373 }
374 }
375
376 void InitialPropagate() override {
377 IncrementalPropagate();
378 for (int i = 0; i < size(); ++i) {
379 if (vars_[i]->Bound()) {
380 PropagateValue(i);
381 }
382 }
383 }
384
385 virtual void IncrementalPropagate() {
386 for (int i = 0; i < size(); ++i) {
387 matching_.SetRange(i, vars_[i]->Min(), vars_[i]->Max());
388 }
389
390 if (matching_.Propagate()) {
391 for (int i = 0; i < size(); ++i) {
392 vars_[i]->SetRange(matching_.Min(i), matching_.Max(i));
393 }
394 }
395 }
396
397 void PropagateValue(int index) {
398 const int64_t to_remove = vars_[index]->Value();
399 for (int j = 0; j < index; j++) {
400 if (vars_[j]->Size() < 0xFFFFFF) {
401 vars_[j]->RemoveValue(to_remove);
402 } else {
403 solver()->AddConstraint(solver()->MakeNonEquality(vars_[j], to_remove));
404 }
405 }
406 for (int j = index + 1; j < size(); j++) {
407 if (vars_[j]->Size() < 0xFFFFFF) {
408 vars_[j]->RemoveValue(to_remove);
409 } else {
410 solver()->AddConstraint(solver()->MakeNonEquality(vars_[j], to_remove));
411 }
412 }
413 }
414
415 std::string DebugString() const override {
416 return DebugStringInternal("BoundsAllDifferent");
417 }
418
419 void Accept(ModelVisitor* const visitor) const override {
420 visitor->BeginVisitConstraint(ModelVisitor::kAllDifferent, this);
421 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
422 vars_);
423 visitor->VisitIntegerArgument(ModelVisitor::kRangeArgument, 1);
424 visitor->EndVisitConstraint(ModelVisitor::kAllDifferent, this);
425 }
426
427 private:
428 RangeBipartiteMatching matching_;
429};
430
431class SortConstraint : public Constraint {
432 public:
433 SortConstraint(Solver* const solver,
434 const std::vector<IntVar*>& original_vars,
435 const std::vector<IntVar*>& sorted_vars)
436 : Constraint(solver),
437 ovars_(original_vars),
438 svars_(sorted_vars),
439 mins_(original_vars.size(), 0),
440 maxs_(original_vars.size(), 0),
441 matching_(solver, original_vars.size()) {}
442
443 ~SortConstraint() override {}
444
445 void Post() override {
446 Demon* const demon =
447 solver()->MakeDelayedConstraintInitialPropagateCallback(this);
448 for (int i = 0; i < size(); ++i) {
449 ovars_[i]->WhenRange(demon);
450 svars_[i]->WhenRange(demon);
451 }
452 }
453
454 void InitialPropagate() override {
455 for (int i = 0; i < size(); ++i) {
456 int64_t vmin = 0;
457 int64_t vmax = 0;
458 ovars_[i]->Range(&vmin, &vmax);
459 mins_[i] = vmin;
460 maxs_[i] = vmax;
461 }
462 // Propagates from variables to sorted variables.
463 std::sort(mins_.begin(), mins_.end());
464 std::sort(maxs_.begin(), maxs_.end());
465 for (int i = 0; i < size(); ++i) {
466 svars_[i]->SetRange(mins_[i], maxs_[i]);
467 }
468 // Maintains sortedness.
469 for (int i = 0; i < size() - 1; ++i) {
470 svars_[i + 1]->SetMin(svars_[i]->Min());
471 }
472 for (int i = size() - 1; i > 0; --i) {
473 svars_[i - 1]->SetMax(svars_[i]->Max());
474 }
475 // Reverse propagation.
476 for (int i = 0; i < size(); ++i) {
477 int64_t imin = 0;
478 int64_t imax = 0;
479 FindIntersectionRange(i, &imin, &imax);
480 matching_.SetRange(i, imin, imax);
481 }
482 matching_.Propagate();
483 for (int i = 0; i < size(); ++i) {
484 const int64_t vmin = svars_[matching_.Min(i)]->Min();
485 const int64_t vmax = svars_[matching_.Max(i)]->Max();
486 ovars_[i]->SetRange(vmin, vmax);
487 }
488 }
489
490 void Accept(ModelVisitor* const visitor) const override {
491 visitor->BeginVisitConstraint(ModelVisitor::kSortingConstraint, this);
492 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
493 ovars_);
494 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kTargetArgument,
495 svars_);
496 visitor->EndVisitConstraint(ModelVisitor::kSortingConstraint, this);
497 }
498
499 std::string DebugString() const override {
500 return absl::StrFormat("Sort(%s, %s)", JoinDebugStringPtr(ovars_, ", "),
501 JoinDebugStringPtr(svars_, ", "));
502 }
503
504 private:
505 int64_t size() const { return ovars_.size(); }
506
507 void FindIntersectionRange(int index, int64_t* const range_min,
508 int64_t* const range_max) const {
509 // Naive version.
510 // TODO(user): Implement log(n) version.
511 int64_t imin = 0;
512 while (imin < size() && NotIntersect(index, imin)) {
513 imin++;
514 }
515 if (imin == size()) {
516 solver()->Fail();
517 }
518 int64_t imax = size() - 1;
519 while (imax > imin && NotIntersect(index, imax)) {
520 imax--;
521 }
522 *range_min = imin;
523 *range_max = imax;
524 }
525
526 bool NotIntersect(int oindex, int sindex) const {
527 return ovars_[oindex]->Min() > svars_[sindex]->Max() ||
528 ovars_[oindex]->Max() < svars_[sindex]->Min();
529 }
530
531 const std::vector<IntVar*> ovars_;
532 const std::vector<IntVar*> svars_;
533 std::vector<int64_t> mins_;
534 std::vector<int64_t> maxs_;
535 RangeBipartiteMatching matching_;
536};
537
538// All variables are pairwise different, unless they are assigned to
539// the escape value.
540class AllDifferentExcept : public Constraint {
541 public:
542 AllDifferentExcept(Solver* const s, std::vector<IntVar*> vars,
543 int64_t escape_value)
544 : Constraint(s), vars_(std::move(vars)), escape_value_(escape_value) {}
545
546 ~AllDifferentExcept() override {}
547
548 void Post() override {
549 for (int i = 0; i < vars_.size(); ++i) {
550 IntVar* const var = vars_[i];
551 Demon* const d = MakeConstraintDemon1(
552 solver(), this, &AllDifferentExcept::Propagate, "Propagate", i);
553 var->WhenBound(d);
554 }
555 }
556
557 void InitialPropagate() override {
558 for (int i = 0; i < vars_.size(); ++i) {
559 if (vars_[i]->Bound()) {
560 Propagate(i);
561 }
562 }
563 }
564
565 void Propagate(int index) {
566 const int64_t val = vars_[index]->Value();
567 if (val != escape_value_) {
568 for (int j = 0; j < vars_.size(); ++j) {
569 if (index != j) {
570 vars_[j]->RemoveValue(val);
571 }
572 }
573 }
574 }
575
576 std::string DebugString() const override {
577 return absl::StrFormat("AllDifferentExcept([%s], %d",
578 JoinDebugStringPtr(vars_, ", "), escape_value_);
579 }
580
581 void Accept(ModelVisitor* const visitor) const override {
582 visitor->BeginVisitConstraint(ModelVisitor::kAllDifferent, this);
583 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
584 vars_);
585 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, escape_value_);
586 visitor->EndVisitConstraint(ModelVisitor::kAllDifferent, this);
587 }
588
589 private:
590 std::vector<IntVar*> vars_;
591 const int64_t escape_value_;
592};
593
594// Creates a constraint that states that all variables in the first
595// vector are different from all variables from the second group,
596// unless they are assigned to the escape value if it is defined. Thus
597// the set of values in the first vector minus the escape value does
598// not intersect the set of values in the second vector.
599class NullIntersectArrayExcept : public Constraint {
600 public:
601 NullIntersectArrayExcept(Solver* const s, std::vector<IntVar*> first_vars,
602 std::vector<IntVar*> second_vars,
603 int64_t escape_value)
604 : Constraint(s),
605 first_vars_(std::move(first_vars)),
606 second_vars_(std::move(second_vars)),
607 escape_value_(escape_value),
608 has_escape_value_(true) {}
609
610 NullIntersectArrayExcept(Solver* const s, std::vector<IntVar*> first_vars,
611 std::vector<IntVar*> second_vars)
612 : Constraint(s),
613 first_vars_(std::move(first_vars)),
614 second_vars_(std::move(second_vars)),
615 escape_value_(0),
616 has_escape_value_(false) {}
617
618 ~NullIntersectArrayExcept() override {}
619
620 void Post() override {
621 for (int i = 0; i < first_vars_.size(); ++i) {
622 IntVar* const var = first_vars_[i];
623 Demon* const d = MakeConstraintDemon1(
624 solver(), this, &NullIntersectArrayExcept::PropagateFirst,
625 "PropagateFirst", i);
626 var->WhenBound(d);
627 }
628 for (int i = 0; i < second_vars_.size(); ++i) {
629 IntVar* const var = second_vars_[i];
630 Demon* const d = MakeConstraintDemon1(
631 solver(), this, &NullIntersectArrayExcept::PropagateSecond,
632 "PropagateSecond", i);
633 var->WhenBound(d);
634 }
635 }
636
637 void InitialPropagate() override {
638 for (int i = 0; i < first_vars_.size(); ++i) {
639 if (first_vars_[i]->Bound()) {
640 PropagateFirst(i);
641 }
642 }
643 for (int i = 0; i < second_vars_.size(); ++i) {
644 if (second_vars_[i]->Bound()) {
645 PropagateSecond(i);
646 }
647 }
648 }
649
650 void PropagateFirst(int index) {
651 const int64_t val = first_vars_[index]->Value();
652 if (!has_escape_value_ || val != escape_value_) {
653 for (int j = 0; j < second_vars_.size(); ++j) {
654 second_vars_[j]->RemoveValue(val);
655 }
656 }
657 }
658
659 void PropagateSecond(int index) {
660 const int64_t val = second_vars_[index]->Value();
661 if (!has_escape_value_ || val != escape_value_) {
662 for (int j = 0; j < first_vars_.size(); ++j) {
663 first_vars_[j]->RemoveValue(val);
664 }
665 }
666 }
667
668 std::string DebugString() const override {
669 return absl::StrFormat("NullIntersectArray([%s], [%s], escape = %d",
670 JoinDebugStringPtr(first_vars_, ", "),
671 JoinDebugStringPtr(second_vars_, ", "),
672 escape_value_);
673 }
674
675 void Accept(ModelVisitor* const visitor) const override {
676 visitor->BeginVisitConstraint(ModelVisitor::kNullIntersect, this);
677 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kLeftArgument,
678 first_vars_);
679 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kRightArgument,
680 second_vars_);
681 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, escape_value_);
682 visitor->EndVisitConstraint(ModelVisitor::kNullIntersect, this);
683 }
684
685 private:
686 std::vector<IntVar*> first_vars_;
687 std::vector<IntVar*> second_vars_;
688 const int64_t escape_value_;
689 const bool has_escape_value_;
690};
691} // namespace
692
693Constraint* Solver::MakeAllDifferent(const std::vector<IntVar*>& vars) {
694 return MakeAllDifferent(vars, true);
695}
696
697Constraint* Solver::MakeAllDifferent(const std::vector<IntVar*>& vars,
698 bool stronger_propagation) {
699 const int size = vars.size();
700 for (int i = 0; i < size; ++i) {
701 CHECK_EQ(this, vars[i]->solver());
702 }
703 if (size < 2) {
704 return MakeTrueConstraint();
705 } else if (size == 2) {
706 return MakeNonEquality(const_cast<IntVar* const>(vars[0]),
707 const_cast<IntVar* const>(vars[1]));
708 } else {
709 if (stronger_propagation) {
710 return RevAlloc(new BoundsAllDifferent(this, vars));
711 } else {
712 return RevAlloc(new ValueAllDifferent(this, vars));
713 }
714 }
715}
716
717Constraint* Solver::MakeSortingConstraint(const std::vector<IntVar*>& vars,
718 const std::vector<IntVar*>& sorted) {
719 CHECK_EQ(vars.size(), sorted.size());
720 return RevAlloc(new SortConstraint(this, vars, sorted));
721}
722
723Constraint* Solver::MakeAllDifferentExcept(const std::vector<IntVar*>& vars,
724 int64_t escape_value) {
725 int escape_candidates = 0;
726 for (int i = 0; i < vars.size(); ++i) {
727 escape_candidates += (vars[i]->Contains(escape_value));
728 }
729 if (escape_candidates <= 1) {
730 return MakeAllDifferent(vars);
731 } else {
732 return RevAlloc(new AllDifferentExcept(this, vars, escape_value));
733 }
734}
735
736Constraint* Solver::MakeNullIntersect(const std::vector<IntVar*>& first_vars,
737 const std::vector<IntVar*>& second_vars) {
738 return RevAlloc(new NullIntersectArrayExcept(this, first_vars, second_vars));
739}
740
742 const std::vector<IntVar*>& first_vars,
743 const std::vector<IntVar*>& second_vars, int64_t escape_value) {
744 int first_escape_candidates = 0;
745 for (int i = 0; i < first_vars.size(); ++i) {
746 first_escape_candidates += (first_vars[i]->Contains(escape_value));
747 }
748 int second_escape_candidates = 0;
749 for (int i = 0; i < second_vars.size(); ++i) {
750 second_escape_candidates += (second_vars[i]->Contains(escape_value));
751 }
752 if (first_escape_candidates == 0 || second_escape_candidates == 0) {
753 return RevAlloc(
754 new NullIntersectArrayExcept(this, first_vars, second_vars));
755 } else {
756 return RevAlloc(new NullIntersectArrayExcept(this, first_vars, second_vars,
757 escape_value));
758 }
759}
760} // namespace operations_research
IntegerValue y
IntegerValue size
const std::vector< IntVar * > vars_
int64_t max
int max_rank
int min_rank
int64_t min
void Switch(Solver *const solver)
Constraint * MakeSortingConstraint(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &sorted)
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
Constraint * MakeAllDifferentExcept(const std::vector< IntVar * > &vars, int64_t escape_value)
Constraint * MakeNullIntersectExcept(const std::vector< IntVar * > &first_vars, const std::vector< IntVar * > &second_vars, int64_t escape_value)
Constraint * MakeAllDifferent(const std::vector< IntVar * > &vars)
Constraint * MakeNullIntersect(const std::vector< IntVar * > &first_vars, const std::vector< IntVar * > &second_vars)
const std::string name
A name for logging purposes.
IntVar * var
int index
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
Definition matchers.h:468
In SWIG mode, we don't want anything besides these top-level includes.
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)
Join v[i]->DebugString().
Demon * MakeDelayedConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
STL namespace.
false true
Definition numbers.cc:228
trees with all degrees equal w the current value of w
trees with all degrees equal to
const Variable x
Definition qp_tests.cc:127
int64_t bound
std::optional< int64_t > end
int64_t start
const std::optional< Range > & range
Definition statistics.cc:37