OR-Tools  9.6
count_cst.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
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/logging.h"
31 
32 namespace operations_research {
33 Constraint* 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 
48 Constraint* 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 
71 namespace {
72 class 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 
152 class 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 
203 void 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 
224 void 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 
254 void 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 
277 void 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 
294 void 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 
307 void 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 
315 void 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 
325 class FastDistribute : public Constraint {
326  public:
327  FastDistribute(Solver* const 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 
379 FastDistribute::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 
394 std::string FastDistribute::DebugString() const {
395  return absl::StrFormat("FastDistribute(vars = [%s], cards = [%s])",
396  JoinDebugStringPtr(vars_, ", "),
397  JoinDebugStringPtr(cards_, ", "));
398 }
399 
400 void 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 
421 void 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 
442 void 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 
455 void 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 
481 void 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 
493 void 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 
501 void 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 
511 class BoundedDistribute : public Constraint {
512  public:
513  BoundedDistribute(Solver* const 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 
574 BoundedDistribute::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 
593 std::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 
601 void 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 
615 void 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 
656 void 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 
670 void 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 
681 void 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 
696 void 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 
704 void 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 
714 class BoundedFastDistribute : public Constraint {
715  public:
716  BoundedFastDistribute(Solver* const 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 
774 BoundedFastDistribute::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 
790 std::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 
797 void 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 
813 void 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 
853 void 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 
867 void 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 
893 void 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 
908 void 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 
916 void 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 
926 class 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 
957 Constraint* 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 
966 Constraint* 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 
995 Constraint* 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 
1001 Constraint* 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 
1015 Constraint* 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 
1034 Constraint* 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 
1054 Constraint* 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 
1060 Constraint* 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 
1077 Constraint* 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
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:44
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
A constraint is the main modeling object.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64_t Min() const =0
virtual int64_t Max() const =0
The class IntVar is a subset of IntExpr.
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
virtual int64_t OldMax() const =0
Returns the previous max.
virtual void WhenBound(Demon *d)=0
This method attaches a demon that will be awakened when the variable is bound.
virtual void WhenDomain(Demon *d)=0
This method attaches a demon that will watch any domain modification of the domain of the variable.
virtual int64_t OldMin() const =0
Returns the previous min.
void SetToZero(Solver *const solver, int64_t row, int64_t column)
Erases the 'column' bit in the 'row' row.
Definition: utilities.cc:179
bool IsSet(int64_t row, int64_t column) const
Returns whether the 'column' bit in the 'row' row is set.
void SetToOne(Solver *const solver, int64_t row, int64_t column)
Sets the 'column' bit in the 'row' row.
Definition: utilities.cc:171
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
Constraint * MakeSumEquality(const std::vector< IntVar * > &vars, int64_t cst)
Definition: expr_array.cc:3451
Constraint * MakeFalseConstraint()
This constraint always fails.
Definition: constraints.cc:523
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 * MakeTrueConstraint()
This constraint always succeeds.
Definition: constraints.cc:518
T * RevAlloc(T *object)
Registers the given object as being reversible.
Constraint * MakeAllDifferent(const std::vector< IntVar * > &vars)
All variables are pairwise different.
Definition: alldiff_cst.cc:692
IntExpr * MakeSum(IntExpr *const left, IntExpr *const right)
left + right.
IntVar * MakeIsEqualCstVar(IntExpr *const var, int64_t value)
status var of (var == value)
Definition: expr_cst.cc:464
Constraint * MakeAtMost(std::vector< IntVar * > vars, int64_t value, int64_t max_count)
|{i | vars[i] == value}| <= max_count
Definition: count_cst.cc:957
std::vector< IntVarIterator * > holes_
int64_t value
IntVar * var
Definition: expr_array.cc:1874
int index
Collection of objects used to extend the Constraint Solver library.
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
std::string JoinDebugStringPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:45
bool IsIncreasingContiguous(const std::vector< T > &values)
std::vector< int64_t > ToInt64Vector(const std::vector< int > &input)
Definition: utilities.cc:829
bool IsArrayInRange(const std::vector< IntVar * > &vars, T range_min, T range_max)
bool AreAllOnes(const std::vector< T > &values)