Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
count_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// Count constraints
16
17#include <algorithm>
18#include <cstdint>
19#include <limits>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include "absl/strings/str_format.h"
25#include "absl/strings/str_join.h"
27#include "ortools/base/types.h"
31
32namespace operations_research {
33Constraint* Solver::MakeCount(const std::vector<IntVar*>& vars, int64_t value,
34 int64_t max_count) {
35 std::vector<IntVar*> tmp_sum;
36 for (int i = 0; i < vars.size(); ++i) {
37 if (vars[i]->Contains(value)) {
38 if (vars[i]->Bound()) {
39 max_count--;
40 } else {
41 tmp_sum.push_back(MakeIsEqualCstVar(vars[i], value));
42 }
43 }
44 }
45 return MakeSumEquality(tmp_sum, max_count);
46}
47
48Constraint* Solver::MakeCount(const std::vector<IntVar*>& vars, int64_t value,
49 IntVar* max_count) {
50 if (max_count->Bound()) {
51 return MakeCount(vars, value, max_count->Min());
52 } else {
53 std::vector<IntVar*> tmp_sum;
54 int64_t num_vars_bound_to_v = 0;
55 for (int i = 0; i < vars.size(); ++i) {
56 if (vars[i]->Contains(value)) {
57 if (vars[i]->Bound()) {
58 ++num_vars_bound_to_v;
59 } else {
60 tmp_sum.push_back(MakeIsEqualCstVar(vars[i], value));
61 }
62 }
63 }
64 return MakeSumEquality(tmp_sum,
65 MakeSum(max_count, -num_vars_bound_to_v)->Var());
66 }
67}
68
69// ---------- Distribute ----------
70
71namespace {
72class AtMost : public Constraint {
73 public:
74 AtMost(Solver* const s, std::vector<IntVar*> vars, int64_t value,
75 int64_t max_count)
76 : Constraint(s),
77 vars_(std::move(vars)),
78 value_(value),
79 max_count_(max_count),
80 current_count_(0) {}
81
82 ~AtMost() override {}
83
84 void Post() override {
85 for (IntVar* var : vars_) {
86 if (!var->Bound() && var->Contains(value_)) {
87 Demon* const d = MakeConstraintDemon1(solver(), this, &AtMost::OneBound,
88 "OneBound", var);
89 var->WhenBound(d);
90 }
91 }
92 }
93
94 void InitialPropagate() override {
95 for (IntVar* var : vars_) {
96 if (var->Bound() && var->Min() == value_) {
97 current_count_.Incr(solver());
98 }
99 }
100 CheckCount();
101 }
102
103 void OneBound(IntVar* var) {
104 if (var->Min() == value_) {
105 current_count_.Incr(solver());
106 CheckCount();
107 }
108 }
109
110 void CheckCount() {
111 if (current_count_.Value() < max_count_) {
112 return;
113 }
114
115 // Remove all remaining values.
116 int forced = 0;
117 for (IntVar* var : vars_) {
118 if (var->Bound()) {
119 if (var->Min() == value_) {
120 forced++;
121 }
122 } else {
123 var->RemoveValue(value_);
124 }
125 }
126 if (forced > max_count_) {
127 solver()->Fail();
128 }
129 }
130
131 std::string DebugString() const override {
132 return absl::StrFormat("AtMost(%s, %d, %d)",
133 JoinDebugStringPtr(vars_, ", "), value_, max_count_);
134 }
135
136 void Accept(ModelVisitor* const visitor) const override {
137 visitor->BeginVisitConstraint(ModelVisitor::kAtMost, this);
138 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
139 vars_);
140 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, value_);
141 visitor->VisitIntegerArgument(ModelVisitor::kCountArgument, max_count_);
142 visitor->EndVisitConstraint(ModelVisitor::kAtMost, this);
143 }
144
145 private:
146 const std::vector<IntVar*> vars_;
147 const int64_t value_;
148 const int64_t max_count_;
149 NumericalRev<int> current_count_;
150};
151
152class Distribute : public Constraint {
153 public:
154 Distribute(Solver* const s, const std::vector<IntVar*>& vars,
155 const std::vector<int64_t>& values,
156 const std::vector<IntVar*>& cards)
157 : Constraint(s),
158 vars_(vars),
159 values_(values),
160 cards_(cards),
161 undecided_(vars.size(), cards.size()),
162 min_(cards.size(), 0),
163 max_(cards.size(), 0) {}
164
165 ~Distribute() override {}
166
167 void Post() override;
168 void InitialPropagate() override;
169 void OneBound(int index);
170 void OneDomain(int index);
171 void CountVar(int cindex);
172 void CardMin(int cindex);
173 void CardMax(int cindex);
174 std::string DebugString() const override {
175 return absl::StrFormat(
176 "Distribute(vars = [%s], values = [%s], cards = [%s])",
177 JoinDebugStringPtr(vars_, ", "), absl::StrJoin(values_, ", "),
178 JoinDebugStringPtr(cards_, ", "));
179 }
180
181 void Accept(ModelVisitor* const visitor) const override {
182 visitor->BeginVisitConstraint(ModelVisitor::kDistribute, this);
183 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
184 vars_);
185 visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
186 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCardsArgument,
187 cards_);
188 visitor->EndVisitConstraint(ModelVisitor::kDistribute, this);
189 }
190
191 private:
192 int64_t var_size() const { return vars_.size(); }
193 int64_t card_size() const { return cards_.size(); }
194
195 const std::vector<IntVar*> vars_;
196 const std::vector<int64_t> values_;
197 const std::vector<IntVar*> cards_;
198 RevBitMatrix undecided_;
199 NumericalRevArray<int> min_;
200 NumericalRevArray<int> max_;
201};
202
203void Distribute::Post() {
204 for (int i = 0; i < var_size(); ++i) {
205 IntVar* const var = vars_[i];
206 if (!var->Bound()) {
207 Demon* d = MakeConstraintDemon1(solver(), this, &Distribute::OneBound,
208 "OneBound", i);
209 var->WhenBound(d);
210 d = MakeConstraintDemon1(solver(), this, &Distribute::OneDomain,
211 "OneDomain", i);
212 var->WhenDomain(d);
213 }
214 }
215 for (int i = 0; i < card_size(); ++i) {
216 if (!cards_[i]->Bound()) {
217 Demon* d =
218 MakeConstraintDemon1(solver(), this, &Distribute::CountVar, "Var", i);
219 cards_[i]->WhenRange(d);
220 }
221 }
222}
223
224void Distribute::InitialPropagate() {
225 Solver* const s = solver();
226 for (int j = 0; j < card_size(); ++j) {
227 int min = 0;
228 int max = 0;
229 for (int i = 0; i < var_size(); ++i) {
230 IntVar* const var = vars_[i];
231 if (var->Bound()) {
232 if (var->Min() == values_[j]) {
233 min++;
234 max++;
235 }
236 } else {
237 if (var->Contains(values_[j])) {
238 max++;
239 undecided_.SetToOne(s, i, j);
240 }
241 }
242 }
243 cards_[j]->SetRange(min, max);
244 if (cards_[j]->Max() == min) {
245 CardMin(j);
246 } else if (cards_[j]->Min() == max) {
247 CardMax(j);
248 }
249 min_.SetValue(s, j, min);
250 max_.SetValue(s, j, max);
251 }
252}
253
254void Distribute::OneBound(int index) {
255 IntVar* const var = vars_[index];
256 Solver* const s = solver();
257 for (int j = 0; j < card_size(); ++j) {
258 if (undecided_.IsSet(index, j)) {
259 undecided_.SetToZero(s, index, j);
260 if (var->Min() == values_[j]) {
261 min_.Incr(s, j);
262 cards_[j]->SetMin(min_[j]);
263 if (min_[j] == cards_[j]->Max()) {
264 CardMin(j);
265 }
266 } else {
267 max_.Decr(s, j);
268 cards_[j]->SetMax(max_[j]);
269 if (max_[j] == cards_[j]->Min()) {
270 CardMax(j);
271 }
272 }
273 }
274 }
275}
276
277void Distribute::OneDomain(int index) {
278 IntVar* const var = vars_[index];
279 Solver* const s = solver();
280 for (int j = 0; j < card_size(); ++j) {
281 if (undecided_.IsSet(index, j)) {
282 if (!var->Contains(values_[j])) {
283 undecided_.SetToZero(s, index, j);
284 max_.Decr(s, j);
285 cards_[j]->SetMax(max_[j]);
286 if (max_[j] == cards_[j]->Min()) {
287 CardMax(j);
288 }
289 }
290 }
291 }
292}
293
294void Distribute::CountVar(int cindex) {
295 if (cards_[cindex]->Min() > max_[cindex] ||
296 cards_[cindex]->Max() < min_[cindex]) {
297 solver()->Fail();
298 }
299 if (cards_[cindex]->Min() == max_[cindex]) {
300 CardMax(cindex);
301 }
302 if (cards_[cindex]->Max() == min_[cindex]) {
303 CardMin(cindex);
304 }
305}
306
307void Distribute::CardMin(int cindex) {
308 for (int i = 0; i < var_size(); ++i) {
309 if (undecided_.IsSet(i, cindex)) {
310 vars_[i]->RemoveValue(values_[cindex]);
311 }
312 }
313}
314
315void Distribute::CardMax(int cindex) {
316 for (int i = 0; i < var_size(); ++i) {
317 if (undecided_.IsSet(i, cindex)) {
318 vars_[i]->SetValue(values_[cindex]);
319 }
320 }
321}
322
323// ----- FastDistribute -----
324
325class FastDistribute : public Constraint {
326 public:
327 FastDistribute(Solver* s, const std::vector<IntVar*>& vars,
328 const std::vector<IntVar*>& cards);
329 ~FastDistribute() override {}
330
331 void Post() override;
332 void InitialPropagate() override;
333 void OneBound(int index);
334 void OneDomain(int index);
335 void CountVar(int card_index);
336 void CardMin(int card_index);
337 void CardMax(int card_index);
338 std::string DebugString() const override;
339 void SetRevCannotContribute(int64_t var_index, int64_t card_index) {
340 Solver* const s = solver();
341 undecided_.SetToZero(s, var_index, card_index);
342 max_.Decr(s, card_index);
343 cards_[card_index]->SetMax(max_[card_index]);
344 if (max_[card_index] == cards_[card_index]->Min()) {
345 CardMax(card_index);
346 }
347 }
348 void SetRevDoContribute(int64_t var_index, int64_t card_index) {
349 Solver* const s = solver();
350 undecided_.SetToZero(s, var_index, card_index);
351 min_.Incr(s, card_index);
352 cards_[card_index]->SetMin(min_[card_index]);
353 if (min_[card_index] == cards_[card_index]->Max()) {
354 CardMin(card_index);
355 }
356 }
357
358 void Accept(ModelVisitor* const visitor) const override {
359 visitor->BeginVisitConstraint(ModelVisitor::kDistribute, this);
360 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
361 vars_);
362 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCardsArgument,
363 cards_);
364 visitor->EndVisitConstraint(ModelVisitor::kDistribute, this);
365 }
366
367 private:
368 int64_t var_size() const { return vars_.size(); }
369 int64_t card_size() const { return cards_.size(); }
370
371 const std::vector<IntVar*> vars_;
372 const std::vector<IntVar*> cards_;
373 RevBitMatrix undecided_;
374 NumericalRevArray<int> min_;
375 NumericalRevArray<int> max_;
376 std::vector<IntVarIterator*> holes_;
377};
378
379FastDistribute::FastDistribute(Solver* const s,
380 const std::vector<IntVar*>& vars,
381 const std::vector<IntVar*>& cards)
382 : Constraint(s),
383 vars_(vars),
384 cards_(cards),
385 undecided_(vars.size(), cards.size()),
386 min_(cards.size(), 0),
387 max_(cards.size(), 0),
388 holes_(vars.size()) {
389 for (int var_index = 0; var_index < var_size(); ++var_index) {
390 holes_[var_index] = vars_[var_index]->MakeHoleIterator(true);
391 }
392}
393
394std::string FastDistribute::DebugString() const {
395 return absl::StrFormat("FastDistribute(vars = [%s], cards = [%s])",
397 JoinDebugStringPtr(cards_, ", "));
398}
399
400void FastDistribute::Post() {
401 for (int var_index = 0; var_index < var_size(); ++var_index) {
402 IntVar* const var = vars_[var_index];
403 if (!var->Bound()) {
404 Demon* d = MakeConstraintDemon1(solver(), this, &FastDistribute::OneBound,
405 "OneBound", var_index);
406 var->WhenBound(d);
407 d = MakeConstraintDemon1(solver(), this, &FastDistribute::OneDomain,
408 "OneDomain", var_index);
409 var->WhenDomain(d);
410 }
411 }
412 for (int card_index = 0; card_index < card_size(); ++card_index) {
413 if (!cards_[card_index]->Bound()) {
414 Demon* d = MakeConstraintDemon1(solver(), this, &FastDistribute::CountVar,
415 "Var", card_index);
416 cards_[card_index]->WhenRange(d);
417 }
418 }
419}
420
421void FastDistribute::InitialPropagate() {
422 Solver* const s = solver();
423 for (int card_index = 0; card_index < card_size(); ++card_index) {
424 int min = 0;
425 int max = 0;
426 for (int var_index = 0; var_index < var_size(); ++var_index) {
427 IntVar* const var = vars_[var_index];
428 if (var->Bound() && var->Min() == card_index) {
429 min++;
430 max++;
431 } else if (var->Contains(card_index)) {
432 max++;
433 undecided_.SetToOne(s, var_index, card_index);
434 }
435 }
436 min_.SetValue(s, card_index, min);
437 max_.SetValue(s, card_index, max);
438 CountVar(card_index);
439 }
440}
441
442void FastDistribute::OneBound(int index) {
443 IntVar* const var = vars_[index];
444 for (int card_index = 0; card_index < card_size(); ++card_index) {
445 if (undecided_.IsSet(index, card_index)) {
446 if (var->Min() == card_index) {
447 SetRevDoContribute(index, card_index);
448 } else {
449 SetRevCannotContribute(index, card_index);
450 }
451 }
452 }
453}
454
455void FastDistribute::OneDomain(int index) {
456 IntVar* const var = vars_[index];
457 const int64_t oldmin = var->OldMin();
458 const int64_t oldmax = var->OldMax();
459 const int64_t vmin = var->Min();
460 const int64_t vmax = var->Max();
461 for (int64_t card_index = std::max(oldmin, int64_t{0});
462 card_index < std::min(vmin, card_size()); ++card_index) {
463 if (undecided_.IsSet(index, card_index)) {
464 SetRevCannotContribute(index, card_index);
465 }
466 }
467 for (const int64_t card_index : InitAndGetValues(holes_[index])) {
468 if (card_index >= 0 && card_index < card_size() &&
469 undecided_.IsSet(index, card_index)) {
470 SetRevCannotContribute(index, card_index);
471 }
472 }
473 for (int64_t card_index = std::max(vmax + 1, int64_t{0});
474 card_index <= std::min(oldmax, card_size() - 1); ++card_index) {
475 if (undecided_.IsSet(index, card_index)) {
476 SetRevCannotContribute(index, card_index);
477 }
478 }
479}
480
481void FastDistribute::CountVar(int card_index) {
482 const int64_t stored_min = min_[card_index];
483 const int64_t stored_max = max_[card_index];
484 cards_[card_index]->SetRange(min_[card_index], max_[card_index]);
485 if (cards_[card_index]->Min() == stored_max) {
486 CardMax(card_index);
487 }
488 if (cards_[card_index]->Max() == stored_min) {
489 CardMin(card_index);
490 }
491}
492
493void FastDistribute::CardMin(int card_index) {
494 for (int var_index = 0; var_index < var_size(); ++var_index) {
495 if (undecided_.IsSet(var_index, card_index)) {
496 vars_[var_index]->RemoveValue(card_index);
497 }
498 }
499}
500
501void FastDistribute::CardMax(int card_index) {
502 for (int var_index = 0; var_index < var_size(); ++var_index) {
503 if (undecided_.IsSet(var_index, card_index)) {
504 vars_[var_index]->SetValue(card_index);
505 }
506 }
507}
508
509// ----- BoundedDistribute -----
510
511class BoundedDistribute : public Constraint {
512 public:
513 BoundedDistribute(Solver* s, const std::vector<IntVar*>& vars,
514 const std::vector<int64_t>& values,
515 const std::vector<int64_t>& card_min,
516 const std::vector<int64_t>& card_max);
517 ~BoundedDistribute() override {}
518
519 void Post() override;
520 void InitialPropagate() override;
521 void OneBound(int index);
522 void OneDomain(int index);
523 void CountVar(int card_index);
524 void CardMin(int card_index);
525 void CardMax(int card_index);
526 std::string DebugString() const override;
527 void SetRevCannotContribute(int64_t var_index, int64_t card_index) {
528 Solver* const s = solver();
529 undecided_.SetToZero(s, var_index, card_index);
530 max_.Decr(s, card_index);
531 if (max_[card_index] < card_min_[card_index]) {
532 solver()->Fail();
533 }
534 if (max_[card_index] == card_min_[card_index]) {
535 CardMax(card_index);
536 }
537 }
538 void SetRevDoContribute(int64_t var_index, int64_t card_index) {
539 Solver* const s = solver();
540 undecided_.SetToZero(s, var_index, card_index);
541 min_.Incr(s, card_index);
542 if (min_[card_index] > card_max_[card_index]) {
543 solver()->Fail();
544 }
545 if (min_[card_index] == card_max_[card_index]) {
546 CardMin(card_index);
547 }
548 }
549
550 void Accept(ModelVisitor* const visitor) const override {
551 visitor->BeginVisitConstraint(ModelVisitor::kDistribute, this);
552 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
553 vars_);
554 visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
555 visitor->VisitIntegerArrayArgument(ModelVisitor::kMinArgument, card_min_);
556 visitor->VisitIntegerArrayArgument(ModelVisitor::kMaxArgument, card_max_);
557 visitor->EndVisitConstraint(ModelVisitor::kDistribute, this);
558 }
559
560 private:
561 int64_t var_size() const { return vars_.size(); }
562 int64_t card_size() const { return values_.size(); }
563
564 const std::vector<IntVar*> vars_;
565 const std::vector<int64_t> values_;
566 const std::vector<int64_t> card_min_;
567 const std::vector<int64_t> card_max_;
568 RevBitMatrix undecided_;
569 NumericalRevArray<int> min_;
570 NumericalRevArray<int> max_;
571 std::vector<IntVarIterator*> holes_;
572};
573
574BoundedDistribute::BoundedDistribute(Solver* const s,
575 const std::vector<IntVar*>& vars,
576 const std::vector<int64_t>& values,
577 const std::vector<int64_t>& card_min,
578 const std::vector<int64_t>& card_max)
579 : Constraint(s),
580 vars_(vars),
581 values_(values),
582 card_min_(card_min),
583 card_max_(card_max),
584 undecided_(vars.size(), values.size()),
585 min_(values.size(), 0),
586 max_(values.size(), 0),
587 holes_(vars.size()) {
588 for (int var_index = 0; var_index < var_size(); ++var_index) {
589 holes_[var_index] = vars_[var_index]->MakeHoleIterator(true);
590 }
591}
592
593std::string BoundedDistribute::DebugString() const {
594 return absl::StrFormat(
595 "BoundedDistribute([%s], values = [%s], card_min = [%s], card_max = "
596 "[%s]",
597 JoinDebugStringPtr(vars_, ", "), absl::StrJoin(values_, ", "),
598 absl::StrJoin(card_min_, ", "), absl::StrJoin(card_max_, ", "));
599}
600
601void BoundedDistribute::Post() {
602 for (int var_index = 0; var_index < var_size(); ++var_index) {
603 IntVar* const var = vars_[var_index];
604 if (!var->Bound()) {
605 Demon* d = MakeConstraintDemon1(
606 solver(), this, &BoundedDistribute::OneBound, "OneBound", var_index);
607 var->WhenBound(d);
608 d = MakeConstraintDemon1(solver(), this, &BoundedDistribute::OneDomain,
609 "OneDomain", var_index);
610 var->WhenDomain(d);
611 }
612 }
613}
614
615void BoundedDistribute::InitialPropagate() {
616 Solver* const s = solver();
617
618 int64_t sum_card_min = 0;
619 for (int i = 0; i < card_size(); ++i) {
620 if (card_max_[i] < card_min_[i]) {
621 solver()->Fail();
622 }
623 sum_card_min += card_min_[i];
624 }
625 if (sum_card_min > var_size()) {
626 s->Fail();
627 }
628 if (sum_card_min == var_size()) {
629 for (int i = 0; i < var_size(); ++i) {
630 vars_[i]->SetValues(values_);
631 }
632 }
633
634 for (int card_index = 0; card_index < card_size(); ++card_index) {
635 const int64_t value = values_[card_index];
636 int min = 0;
637 int max = 0;
638 for (int i = 0; i < var_size(); ++i) {
639 IntVar* const var = vars_[i];
640 if (var->Bound()) {
641 if (var->Min() == value) {
642 min++;
643 max++;
644 }
645 } else if (var->Contains(value)) {
646 max++;
647 undecided_.SetToOne(s, i, card_index);
648 }
649 }
650 min_.SetValue(s, card_index, min);
651 max_.SetValue(s, card_index, max);
652 CountVar(card_index);
653 }
654}
655
656void BoundedDistribute::OneBound(int index) {
657 IntVar* const var = vars_[index];
658 const int64_t var_min = var->Min();
659 for (int card_index = 0; card_index < card_size(); ++card_index) {
660 if (undecided_.IsSet(index, card_index)) {
661 if (var_min == values_[card_index]) {
662 SetRevDoContribute(index, card_index);
663 } else {
664 SetRevCannotContribute(index, card_index);
665 }
666 }
667 }
668}
669
670void BoundedDistribute::OneDomain(int index) {
671 IntVar* const var = vars_[index];
672 for (int card_index = 0; card_index < card_size(); ++card_index) {
673 if (undecided_.IsSet(index, card_index)) {
674 if (!var->Contains(values_[card_index])) {
675 SetRevCannotContribute(index, card_index);
676 }
677 }
678 }
679}
680
681void BoundedDistribute::CountVar(int card_index) {
682 const int64_t stored_min = min_[card_index];
683 const int64_t stored_max = max_[card_index];
684 if (card_min_[card_index] > stored_max ||
685 card_max_[card_index] < stored_min) {
686 solver()->Fail();
687 }
688 if (card_min_[card_index] == stored_max) {
689 CardMax(card_index);
690 }
691 if (card_max_[card_index] == stored_min) {
692 CardMin(card_index);
693 }
694}
695
696void BoundedDistribute::CardMin(int card_index) {
697 for (int var_index = 0; var_index < var_size(); ++var_index) {
698 if (undecided_.IsSet(var_index, card_index)) {
699 vars_[var_index]->RemoveValue(values_[card_index]);
700 }
701 }
702}
703
704void BoundedDistribute::CardMax(int card_index) {
705 for (int var_index = 0; var_index < var_size(); ++var_index) {
706 if (undecided_.IsSet(var_index, card_index)) {
707 vars_[var_index]->SetValue(values_[card_index]);
708 }
709 }
710}
711
712// ----- BoundedFastDistribute -----
713
714class BoundedFastDistribute : public Constraint {
715 public:
716 BoundedFastDistribute(Solver* s, const std::vector<IntVar*>& vars,
717 const std::vector<int64_t>& card_min,
718 const std::vector<int64_t>& card_max);
719 ~BoundedFastDistribute() override {}
720
721 void Post() override;
722 void InitialPropagate() override;
723 void OneBound(int index);
724 void OneDomain(int index);
725 void CountVar(int card_index);
726 void CardMin(int card_index);
727 void CardMax(int card_index);
728 std::string DebugString() const override;
729 void SetRevCannotContribute(int64_t var_index, int64_t card_index) {
730 Solver* const s = solver();
731 undecided_.SetToZero(s, var_index, card_index);
732 max_.Decr(s, card_index);
733 if (max_[card_index] < card_min_[card_index]) {
734 solver()->Fail();
735 }
736 if (max_[card_index] == card_min_[card_index]) {
737 CardMax(card_index);
738 }
739 }
740 void SetRevDoContribute(int64_t var_index, int64_t card_index) {
741 Solver* const s = solver();
742 undecided_.SetToZero(s, var_index, card_index);
743 min_.Incr(s, card_index);
744 if (min_[card_index] > card_max_[card_index]) {
745 solver()->Fail();
746 }
747 if (min_[card_index] == card_max_[card_index]) {
748 CardMin(card_index);
749 }
750 }
751
752 void Accept(ModelVisitor* const visitor) const override {
753 visitor->BeginVisitConstraint(ModelVisitor::kDistribute, this);
754 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
755 vars_);
756 visitor->VisitIntegerArrayArgument(ModelVisitor::kMinArgument, card_min_);
757 visitor->VisitIntegerArrayArgument(ModelVisitor::kMaxArgument, card_max_);
758 visitor->EndVisitConstraint(ModelVisitor::kDistribute, this);
759 }
760
761 private:
762 int64_t var_size() const { return vars_.size(); }
763 int64_t card_size() const { return card_min_.size(); }
764
765 const std::vector<IntVar*> vars_;
766 const std::vector<int64_t> card_min_;
767 const std::vector<int64_t> card_max_;
768 RevBitMatrix undecided_;
769 NumericalRevArray<int> min_;
770 NumericalRevArray<int> max_;
771 std::vector<IntVarIterator*> holes_;
772};
773
774BoundedFastDistribute::BoundedFastDistribute(
775 Solver* const s, const std::vector<IntVar*>& vars,
776 const std::vector<int64_t>& card_min, const std::vector<int64_t>& card_max)
777 : Constraint(s),
778 vars_(vars),
779 card_min_(card_min),
780 card_max_(card_max),
781 undecided_(vars.size(), card_min.size()),
782 min_(card_min.size(), 0),
783 max_(card_max.size(), 0),
784 holes_(vars.size()) {
785 for (int var_index = 0; var_index < var_size(); ++var_index) {
786 holes_[var_index] = vars_[var_index]->MakeHoleIterator(true);
787 }
788}
789
790std::string BoundedFastDistribute::DebugString() const {
791 return absl::StrFormat(
792 "BoundedFastDistribute([%s], card_min = [%s], card_max = [%s]",
793 JoinDebugStringPtr(vars_, ", "), absl::StrJoin(card_min_, ", "),
794 absl::StrJoin(card_max_, ", "));
795}
796
797void BoundedFastDistribute::Post() {
798 for (int var_index = 0; var_index < var_size(); ++var_index) {
799 IntVar* const var = vars_[var_index];
800 if (!var->Bound()) {
801 Demon* d =
802 MakeConstraintDemon1(solver(), this, &BoundedFastDistribute::OneBound,
803 "OneBound", var_index);
804 var->WhenBound(d);
805 d = MakeConstraintDemon1(solver(), this,
806 &BoundedFastDistribute::OneDomain, "OneDomain",
807 var_index);
808 var->WhenDomain(d);
809 }
810 }
811}
812
813void BoundedFastDistribute::InitialPropagate() {
814 Solver* const s = solver();
815
816 int64_t sum_card_min = 0;
817 for (int i = 0; i < card_size(); ++i) {
818 if (card_max_[i] < card_min_[i]) {
819 solver()->Fail();
820 }
821 sum_card_min += card_min_[i];
822 }
823 if (sum_card_min > var_size()) {
824 s->Fail();
825 }
826 if (sum_card_min == var_size()) {
827 for (int i = 0; i < var_size(); ++i) {
828 vars_[i]->SetRange(0, card_size() - 1);
829 }
830 }
831
832 for (int card_index = 0; card_index < card_size(); ++card_index) {
833 int min = 0;
834 int max = 0;
835 for (int i = 0; i < var_size(); ++i) {
836 IntVar* const var = vars_[i];
837 if (var->Bound()) {
838 if (var->Min() == card_index) {
839 min++;
840 max++;
841 }
842 } else if (var->Contains(card_index)) {
843 max++;
844 undecided_.SetToOne(s, i, card_index);
845 }
846 }
847 min_.SetValue(s, card_index, min);
848 max_.SetValue(s, card_index, max);
849 CountVar(card_index);
850 }
851}
852
853void BoundedFastDistribute::OneBound(int index) {
854 IntVar* const var = vars_[index];
855 const int64_t var_min = var->Min();
856 for (int card_index = 0; card_index < card_size(); ++card_index) {
857 if (undecided_.IsSet(index, card_index)) {
858 if (var_min == card_index) {
859 SetRevDoContribute(index, card_index);
860 } else {
861 SetRevCannotContribute(index, card_index);
862 }
863 }
864 }
865}
866
867void BoundedFastDistribute::OneDomain(int index) {
868 IntVar* const var = vars_[index];
869 const int64_t oldmin = var->OldMin();
870 const int64_t oldmax = var->OldMax();
871 const int64_t vmin = var->Min();
872 const int64_t vmax = var->Max();
873 for (int64_t card_index = std::max(oldmin, int64_t{0});
874 card_index < std::min(vmin, card_size()); ++card_index) {
875 if (undecided_.IsSet(index, card_index)) {
876 SetRevCannotContribute(index, card_index);
877 }
878 }
879 for (const int64_t card_index : InitAndGetValues(holes_[index])) {
880 if (card_index >= 0 && card_index < card_size() &&
881 undecided_.IsSet(index, card_index)) {
882 SetRevCannotContribute(index, card_index);
883 }
884 }
885 for (int64_t card_index = std::max(vmax + 1, int64_t{0});
886 card_index <= std::min(oldmax, card_size() - 1); ++card_index) {
887 if (undecided_.IsSet(index, card_index)) {
888 SetRevCannotContribute(index, card_index);
889 }
890 }
891}
892
893void BoundedFastDistribute::CountVar(int card_index) {
894 const int64_t stored_min = min_[card_index];
895 const int64_t stored_max = max_[card_index];
896 if (card_min_[card_index] > stored_max ||
897 card_max_[card_index] < stored_min) {
898 solver()->Fail();
899 }
900 if (card_min_[card_index] == stored_max) {
901 CardMax(card_index);
902 }
903 if (card_max_[card_index] == stored_min) {
904 CardMin(card_index);
905 }
906}
907
908void BoundedFastDistribute::CardMin(int card_index) {
909 for (int var_index = 0; var_index < var_size(); ++var_index) {
910 if (undecided_.IsSet(var_index, card_index)) {
911 vars_[var_index]->RemoveValue(card_index);
912 }
913 }
914}
915
916void BoundedFastDistribute::CardMax(int card_index) {
917 for (int var_index = 0; var_index < var_size(); ++var_index) {
918 if (undecided_.IsSet(var_index, card_index)) {
919 vars_[var_index]->SetValue(card_index);
920 }
921 }
922}
923
924// ----- SetAllToZero -----
925
926class SetAllToZero : public Constraint {
927 public:
928 SetAllToZero(Solver* const s, const std::vector<IntVar*>& vars)
929 : Constraint(s), vars_(vars) {}
930
931 ~SetAllToZero() override {}
932
933 void Post() override {}
934
935 void InitialPropagate() override {
936 for (int i = 0; i < vars_.size(); ++i) {
937 vars_[i]->SetValue(0);
938 }
939 }
940
941 std::string DebugString() const override { return "SetAllToZero()"; }
942
943 void Accept(ModelVisitor* const visitor) const override {
944 visitor->BeginVisitConstraint(ModelVisitor::kDistribute, this);
945 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCardsArgument,
946 vars_);
947 visitor->EndVisitConstraint(ModelVisitor::kDistribute, this);
948 }
949
950 private:
951 const std::vector<IntVar*> vars_;
952};
953} // namespace
954
955// ----- Factory -----
956
957Constraint* Solver::MakeAtMost(std::vector<IntVar*> vars, int64_t value,
958 int64_t max_count) {
959 CHECK_GE(max_count, 0);
960 if (max_count >= vars.size()) {
961 return MakeTrueConstraint();
962 }
963 return RevAlloc(new AtMost(this, std::move(vars), value, max_count));
964}
965
966Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
967 const std::vector<int64_t>& values,
968 const std::vector<IntVar*>& cards) {
969 if (vars.empty()) {
970 return RevAlloc(new SetAllToZero(this, cards));
971 }
972 CHECK_EQ(values.size(), cards.size());
973 for (IntVar* const var : vars) {
974 CHECK_EQ(this, var->solver());
975 }
976
977 // TODO(user) : we can sort values (and cards) before doing the test.
978 bool fast = true;
979 for (int i = 0; i < values.size(); ++i) {
980 if (values[i] != i) {
981 fast = false;
982 break;
983 }
984 }
985 for (IntVar* const card : cards) {
986 CHECK_EQ(this, card->solver());
987 }
988 if (fast) {
989 return RevAlloc(new FastDistribute(this, vars, cards));
990 } else {
991 return RevAlloc(new Distribute(this, vars, values, cards));
992 }
993}
994
995Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
996 const std::vector<int>& values,
997 const std::vector<IntVar*>& cards) {
998 return MakeDistribute(vars, ToInt64Vector(values), cards);
999}
1000
1001Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
1002 const std::vector<IntVar*>& cards) {
1003 if (vars.empty()) {
1004 return RevAlloc(new SetAllToZero(this, cards));
1005 }
1006 for (IntVar* const var : vars) {
1007 CHECK_EQ(this, var->solver());
1008 }
1009 for (IntVar* const card : cards) {
1010 CHECK_EQ(this, card->solver());
1011 }
1012 return RevAlloc(new FastDistribute(this, vars, cards));
1013}
1014
1015Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
1016 int64_t card_min, int64_t card_max,
1017 int64_t card_size) {
1018 const int vsize = vars.size();
1019 CHECK_NE(vsize, 0);
1020 for (IntVar* const var : vars) {
1021 CHECK_EQ(this, var->solver());
1022 }
1023 if (card_min == 0 && card_max >= vsize) {
1024 return MakeTrueConstraint();
1025 } else if (card_min > vsize || card_max < 0 || card_max < card_min) {
1026 return MakeFalseConstraint();
1027 } else {
1028 std::vector<int64_t> mins(card_size, card_min);
1029 std::vector<int64_t> maxes(card_size, card_max);
1030 return RevAlloc(new BoundedFastDistribute(this, vars, mins, maxes));
1031 }
1032}
1033
1034Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
1035 const std::vector<int64_t>& card_min,
1036 const std::vector<int64_t>& card_max) {
1037 const int vsize = vars.size();
1038 CHECK_NE(vsize, 0);
1039 int64_t cmax = std::numeric_limits<int64_t>::max();
1040 int64_t cmin = std::numeric_limits<int64_t>::min();
1041 for (int i = 0; i < card_max.size(); ++i) {
1042 cmax = std::min(cmax, card_max[i]);
1043 cmin = std::max(cmin, card_min[i]);
1044 }
1045 if (cmax < 0 || cmin > vsize) {
1046 return MakeFalseConstraint();
1047 } else if (cmax >= vsize && cmin == 0) {
1048 return MakeTrueConstraint();
1049 } else {
1050 return RevAlloc(new BoundedFastDistribute(this, vars, card_min, card_max));
1051 }
1052}
1053
1054Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
1055 const std::vector<int>& card_min,
1056 const std::vector<int>& card_max) {
1057 return MakeDistribute(vars, ToInt64Vector(card_min), ToInt64Vector(card_max));
1058}
1059
1060Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
1061 const std::vector<int64_t>& values,
1062 const std::vector<int64_t>& card_min,
1063 const std::vector<int64_t>& card_max) {
1064 CHECK_NE(vars.size(), 0);
1065 CHECK_EQ(card_min.size(), values.size());
1066 CHECK_EQ(card_min.size(), card_max.size());
1067 if (AreAllOnes(card_min) && AreAllOnes(card_max) &&
1068 values.size() == vars.size() && IsIncreasingContiguous(values) &&
1069 IsArrayInRange(vars, values.front(), values.back())) {
1070 return MakeAllDifferent(vars);
1071 } else {
1072 return RevAlloc(
1073 new BoundedDistribute(this, vars, values, card_min, card_max));
1074 }
1075}
1076
1077Constraint* Solver::MakeDistribute(const std::vector<IntVar*>& vars,
1078 const std::vector<int>& values,
1079 const std::vector<int>& card_min,
1080 const std::vector<int>& card_max) {
1081 return MakeDistribute(vars, ToInt64Vector(values), ToInt64Vector(card_min),
1082 ToInt64Vector(card_max));
1083}
1084} // namespace operations_research
IntegerValue size
const std::vector< IntVar * > vars_
int64_t max
int64_t min
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64_t Min() const =0
bool IsSet(int64_t row, int64_t column) const
Returns whether the 'column' bit in the 'row' row is set.
void SetToZero(Solver *solver, int64_t row, int64_t column)
Erases the 'column' bit in the 'row' row.
Definition utilities.cc:179
void SetToOne(Solver *solver, int64_t row, int64_t column)
Sets the 'column' bit in the 'row' row.
Definition utilities.cc:171
For the time being, Solver is neither MT_SAFE nor MT_HOT.
Constraint * MakeDistribute(const std::vector< IntVar * > &vars, const std::vector< int64_t > &values, const std::vector< IntVar * > &cards)
Aggregated version of count: |{i | v[i] == values[j]}| == cards[j].
Definition count_cst.cc:966
Constraint * MakeSumEquality(const std::vector< IntVar * > &vars, int64_t cst)
IntVar * MakeIsEqualCstVar(IntExpr *var, int64_t value)
status var of (var == value)
Definition expr_cst.cc:468
IntExpr * MakeSum(IntExpr *left, IntExpr *right)
--— Integer Expressions --—
Constraint * MakeAtMost(std::vector< IntVar * > vars, int64_t value, int64_t max_count)
|{i | vars[i] == value}| <= max_count
Definition count_cst.cc:957
Constraint * MakeCount(const std::vector< IntVar * > &vars, int64_t value, int64_t max_count)
|{i | vars[i] == value}| == max_count
Definition count_cst.cc:33
int64_t value
IntVar * var
int index
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().
std::vector< int64_t > ToInt64Vector(const std::vector< int > &input)
--— Vector manipulations --—
Definition utilities.cc:829
bool IsIncreasingContiguous(const std::vector< T > &values)
bool IsArrayInRange(const std::vector< IntVar * > &vars, T range_min, T range_max)
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
bool AreAllOnes(const std::vector< T > &values)
STL namespace.
int var_index
Definition search.cc:3268