OR-Tools  9.1
expr_array.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//
15// Array Expression constraints
16
17#include <algorithm>
18#include <cmath>
19#include <cstdint>
20#include <limits>
21#include <string>
22#include <vector>
23
24#include "absl/strings/str_format.h"
25#include "absl/strings/str_join.h"
33
34namespace operations_research {
35namespace {
36// ----- Tree Array Constraint -----
37
38class TreeArrayConstraint : public CastConstraint {
39 public:
40 TreeArrayConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
41 IntVar* const sum_var)
42 : CastConstraint(solver, sum_var),
43 vars_(vars),
44 block_size_(solver->parameters().array_split_size()) {
45 std::vector<int> lengths;
46 lengths.push_back(vars_.size());
47 while (lengths.back() > 1) {
48 const int current = lengths.back();
49 lengths.push_back((current + block_size_ - 1) / block_size_);
50 }
51 tree_.resize(lengths.size());
52 for (int i = 0; i < lengths.size(); ++i) {
53 tree_[i].resize(lengths[lengths.size() - i - 1]);
54 }
55 DCHECK_GE(tree_.size(), 1);
56 DCHECK_EQ(1, tree_[0].size());
57 root_node_ = &tree_[0][0];
58 }
59
60 std::string DebugStringInternal(const std::string& name) const {
61 return absl::StrFormat("%s(%s) == %s", name,
63 target_var_->DebugString());
64 }
65
66 void AcceptInternal(const std::string& name,
67 ModelVisitor* const visitor) const {
68 visitor->BeginVisitConstraint(name, this);
69 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
70 vars_);
71 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
73 visitor->EndVisitConstraint(name, this);
74 }
75
76 // Increases min by delta_min, reduces max by delta_max.
77 void ReduceRange(int depth, int position, int64_t delta_min,
78 int64_t delta_max) {
79 NodeInfo* const info = &tree_[depth][position];
80 if (delta_min > 0) {
81 info->node_min.SetValue(solver(),
82 CapAdd(info->node_min.Value(), delta_min));
83 }
84 if (delta_max > 0) {
85 info->node_max.SetValue(solver(),
86 CapSub(info->node_max.Value(), delta_max));
87 }
88 }
89
90 // Sets the range on the given node.
91 void SetRange(int depth, int position, int64_t new_min, int64_t new_max) {
92 NodeInfo* const info = &tree_[depth][position];
93 if (new_min > info->node_min.Value()) {
94 info->node_min.SetValue(solver(), new_min);
95 }
96 if (new_max < info->node_max.Value()) {
97 info->node_max.SetValue(solver(), new_max);
98 }
99 }
100
101 void InitLeaf(int position, int64_t var_min, int64_t var_max) {
102 InitNode(MaxDepth(), position, var_min, var_max);
103 }
104
105 void InitNode(int depth, int position, int64_t node_min, int64_t node_max) {
106 tree_[depth][position].node_min.SetValue(solver(), node_min);
107 tree_[depth][position].node_max.SetValue(solver(), node_max);
108 }
109
110 int64_t Min(int depth, int position) const {
111 return tree_[depth][position].node_min.Value();
112 }
113
114 int64_t Max(int depth, int position) const {
115 return tree_[depth][position].node_max.Value();
116 }
117
118 int64_t RootMin() const { return root_node_->node_min.Value(); }
119
120 int64_t RootMax() const { return root_node_->node_max.Value(); }
121
122 int Parent(int position) const { return position / block_size_; }
123
124 int ChildStart(int position) const { return position * block_size_; }
125
126 int ChildEnd(int depth, int position) const {
127 DCHECK_LT(depth + 1, tree_.size());
128 return std::min((position + 1) * block_size_ - 1, Width(depth + 1) - 1);
129 }
130
131 bool IsLeaf(int depth) const { return depth == MaxDepth(); }
132
133 int MaxDepth() const { return tree_.size() - 1; }
134
135 int Width(int depth) const { return tree_[depth].size(); }
136
137 protected:
138 const std::vector<IntVar*> vars_;
139
140 private:
141 struct NodeInfo {
142 NodeInfo() : node_min(0), node_max(0) {}
143 Rev<int64_t> node_min;
144 Rev<int64_t> node_max;
145 };
146
147 std::vector<std::vector<NodeInfo> > tree_;
148 const int block_size_;
149 NodeInfo* root_node_;
150};
151
152// ---------- Sum Array ----------
153
154// Some of these optimizations here are described in:
155// "Bounds consistency techniques for long linear constraints". In
156// Workshop on Techniques for Implementing Constraint Programming
157// Systems (TRICS), a workshop of CP 2002, N. Beldiceanu, W. Harvey,
158// Martin Henz, Francois Laburthe, Eric Monfroy, Tobias Müller,
159// Laurent Perron and Christian Schulte editors, pages 39-46, 2002.
160
161// ----- SumConstraint -----
162
163// This constraint implements sum(vars) == sum_var.
164class SumConstraint : public TreeArrayConstraint {
165 public:
166 SumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
167 IntVar* const sum_var)
168 : TreeArrayConstraint(solver, vars, sum_var), sum_demon_(nullptr) {}
169
170 ~SumConstraint() override {}
171
172 void Post() override {
173 for (int i = 0; i < vars_.size(); ++i) {
174 Demon* const demon = MakeConstraintDemon1(
175 solver(), this, &SumConstraint::LeafChanged, "LeafChanged", i);
176 vars_[i]->WhenRange(demon);
177 }
178 sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
179 solver(), this, &SumConstraint::SumChanged, "SumChanged"));
180 target_var_->WhenRange(sum_demon_);
181 }
182
183 void InitialPropagate() override {
184 // Copy vars to leaf nodes.
185 for (int i = 0; i < vars_.size(); ++i) {
186 InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
187 }
188 // Compute up.
189 for (int i = MaxDepth() - 1; i >= 0; --i) {
190 for (int j = 0; j < Width(i); ++j) {
191 int64_t sum_min = 0;
192 int64_t sum_max = 0;
193 const int block_start = ChildStart(j);
194 const int block_end = ChildEnd(i, j);
195 for (int k = block_start; k <= block_end; ++k) {
196 sum_min = CapAdd(sum_min, Min(i + 1, k));
197 sum_max = CapAdd(sum_max, Max(i + 1, k));
198 }
199 InitNode(i, j, sum_min, sum_max);
200 }
201 }
202 // Propagate to sum_var.
203 target_var_->SetRange(RootMin(), RootMax());
204
205 // Push down.
206 SumChanged();
207 }
208
209 void SumChanged() {
210 if (target_var_->Max() == RootMin() &&
212 // We can fix all terms to min.
213 for (int i = 0; i < vars_.size(); ++i) {
214 vars_[i]->SetValue(vars_[i]->Min());
215 }
216 } else if (target_var_->Min() == RootMax() &&
218 // We can fix all terms to max.
219 for (int i = 0; i < vars_.size(); ++i) {
220 vars_[i]->SetValue(vars_[i]->Max());
221 }
222 } else {
223 PushDown(0, 0, target_var_->Min(), target_var_->Max());
224 }
225 }
226
227 void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
228 // Nothing to do?
229 if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
230 return;
231 }
232
233 // Leaf node -> push to leaf var.
234 if (IsLeaf(depth)) {
235 vars_[position]->SetRange(new_min, new_max);
236 return;
237 }
238
239 // Standard propagation from the bounds of the sum to the
240 // individuals terms.
241
242 // These are maintained automatically in the tree structure.
243 const int64_t sum_min = Min(depth, position);
244 const int64_t sum_max = Max(depth, position);
245
246 // Intersect the new bounds with the computed bounds.
247 new_max = std::min(sum_max, new_max);
248 new_min = std::max(sum_min, new_min);
249
250 // Detect failure early.
251 if (new_max < sum_min || new_min > sum_max) {
252 solver()->Fail();
253 }
254
255 // Push to children nodes.
256 const int block_start = ChildStart(position);
257 const int block_end = ChildEnd(depth, position);
258 for (int i = block_start; i <= block_end; ++i) {
259 const int64_t target_var_min = Min(depth + 1, i);
260 const int64_t target_var_max = Max(depth + 1, i);
261 const int64_t residual_min = CapSub(sum_min, target_var_min);
262 const int64_t residual_max = CapSub(sum_max, target_var_max);
263 PushDown(depth + 1, i, CapSub(new_min, residual_max),
264 CapSub(new_max, residual_min));
265 }
266 // TODO(user) : Is the diameter optimization (see reference
267 // above, rule 5) useful?
268 }
269
270 void LeafChanged(int term_index) {
271 IntVar* const var = vars_[term_index];
272 PushUp(term_index, CapSub(var->Min(), var->OldMin()),
273 CapSub(var->OldMax(), var->Max()));
274 EnqueueDelayedDemon(sum_demon_); // TODO(user): Is this needed?
275 }
276
277 void PushUp(int position, int64_t delta_min, int64_t delta_max) {
278 DCHECK_GE(delta_max, 0);
279 DCHECK_GE(delta_min, 0);
280 DCHECK_GT(CapAdd(delta_min, delta_max), 0);
281 for (int depth = MaxDepth(); depth >= 0; --depth) {
282 ReduceRange(depth, position, delta_min, delta_max);
283 position = Parent(position);
284 }
285 DCHECK_EQ(0, position);
286 target_var_->SetRange(RootMin(), RootMax());
287 }
288
289 std::string DebugString() const override {
290 return DebugStringInternal("Sum");
291 }
292
293 void Accept(ModelVisitor* const visitor) const override {
294 AcceptInternal(ModelVisitor::kSumEqual, visitor);
295 }
296
297 private:
298 Demon* sum_demon_;
299};
300
301// This constraint implements sum(vars) == target_var.
302class SmallSumConstraint : public Constraint {
303 public:
304 SmallSumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
305 IntVar* const target_var)
306 : Constraint(solver),
307 vars_(vars),
308 target_var_(target_var),
309 computed_min_(0),
310 computed_max_(0),
311 sum_demon_(nullptr) {}
312
313 ~SmallSumConstraint() override {}
314
315 void Post() override {
316 for (int i = 0; i < vars_.size(); ++i) {
317 if (!vars_[i]->Bound()) {
318 Demon* const demon = MakeConstraintDemon1(
319 solver(), this, &SmallSumConstraint::VarChanged, "VarChanged",
320 vars_[i]);
321 vars_[i]->WhenRange(demon);
322 }
323 }
324 sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
325 solver(), this, &SmallSumConstraint::SumChanged, "SumChanged"));
326 target_var_->WhenRange(sum_demon_);
327 }
328
329 void InitialPropagate() override {
330 // Compute up.
331 int64_t sum_min = 0;
332 int64_t sum_max = 0;
333 for (IntVar* const var : vars_) {
334 sum_min = CapAdd(sum_min, var->Min());
335 sum_max = CapAdd(sum_max, var->Max());
336 }
337
338 // Propagate to sum_var.
339 computed_min_.SetValue(solver(), sum_min);
340 computed_max_.SetValue(solver(), sum_max);
341 target_var_->SetRange(sum_min, sum_max);
342
343 // Push down.
344 SumChanged();
345 }
346
347 void SumChanged() {
348 int64_t new_min = target_var_->Min();
349 int64_t new_max = target_var_->Max();
350 const int64_t sum_min = computed_min_.Value();
351 const int64_t sum_max = computed_max_.Value();
352 if (new_max == sum_min && new_max != std::numeric_limits<int64_t>::max()) {
353 // We can fix all terms to min.
354 for (int i = 0; i < vars_.size(); ++i) {
355 vars_[i]->SetValue(vars_[i]->Min());
356 }
357 } else if (new_min == sum_max &&
359 // We can fix all terms to max.
360 for (int i = 0; i < vars_.size(); ++i) {
361 vars_[i]->SetValue(vars_[i]->Max());
362 }
363 } else {
364 if (new_min > sum_min || new_max < sum_max) { // something to do.
365 // Intersect the new bounds with the computed bounds.
366 new_max = std::min(sum_max, new_max);
367 new_min = std::max(sum_min, new_min);
368
369 // Detect failure early.
370 if (new_max < sum_min || new_min > sum_max) {
371 solver()->Fail();
372 }
373
374 // Push to variables.
375 for (IntVar* const var : vars_) {
376 const int64_t var_min = var->Min();
377 const int64_t var_max = var->Max();
378 const int64_t residual_min = CapSub(sum_min, var_min);
379 const int64_t residual_max = CapSub(sum_max, var_max);
380 var->SetRange(CapSub(new_min, residual_max),
381 CapSub(new_max, residual_min));
382 }
383 }
384 }
385 }
386
387 void VarChanged(IntVar* var) {
388 const int64_t delta_min = CapSub(var->Min(), var->OldMin());
389 const int64_t delta_max = CapSub(var->OldMax(), var->Max());
390 computed_min_.Add(solver(), delta_min);
391 computed_max_.Add(solver(), -delta_max);
392 if (computed_max_.Value() < target_var_->Max() ||
393 computed_min_.Value() > target_var_->Min()) {
394 target_var_->SetRange(computed_min_.Value(), computed_max_.Value());
395 } else {
396 EnqueueDelayedDemon(sum_demon_);
397 }
398 }
399
400 std::string DebugString() const override {
401 return absl::StrFormat("SmallSum(%s) == %s",
403 target_var_->DebugString());
404 }
405
406 void Accept(ModelVisitor* const visitor) const override {
407 visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
408 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
409 vars_);
410 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
412 visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
413 }
414
415 private:
416 const std::vector<IntVar*> vars_;
417 IntVar* target_var_;
418 NumericalRev<int64_t> computed_min_;
419 NumericalRev<int64_t> computed_max_;
420 Demon* sum_demon_;
421};
422// ----- SafeSumConstraint -----
423
424bool DetectSumOverflow(const std::vector<IntVar*>& vars) {
425 int64_t sum_min = 0;
426 int64_t sum_max = 0;
427 for (int i = 0; i < vars.size(); ++i) {
428 sum_min = CapAdd(sum_min, vars[i]->Min());
429 sum_max = CapAdd(sum_max, vars[i]->Max());
430 if (sum_min == std::numeric_limits<int64_t>::min() ||
432 return true;
433 }
434 }
435 return false;
436}
437
438// This constraint implements sum(vars) == sum_var.
439class SafeSumConstraint : public TreeArrayConstraint {
440 public:
441 SafeSumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
442 IntVar* const sum_var)
443 : TreeArrayConstraint(solver, vars, sum_var), sum_demon_(nullptr) {}
444
445 ~SafeSumConstraint() override {}
446
447 void Post() override {
448 for (int i = 0; i < vars_.size(); ++i) {
449 Demon* const demon = MakeConstraintDemon1(
450 solver(), this, &SafeSumConstraint::LeafChanged, "LeafChanged", i);
451 vars_[i]->WhenRange(demon);
452 }
453 sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
454 solver(), this, &SafeSumConstraint::SumChanged, "SumChanged"));
455 target_var_->WhenRange(sum_demon_);
456 }
457
458 void SafeComputeNode(int depth, int position, int64_t* const sum_min,
459 int64_t* const sum_max) {
460 DCHECK_LT(depth, MaxDepth());
461 const int block_start = ChildStart(position);
462 const int block_end = ChildEnd(depth, position);
463 for (int k = block_start; k <= block_end; ++k) {
464 if (*sum_min != std::numeric_limits<int64_t>::min()) {
465 *sum_min = CapAdd(*sum_min, Min(depth + 1, k));
466 }
467 if (*sum_max != std::numeric_limits<int64_t>::max()) {
468 *sum_max = CapAdd(*sum_max, Max(depth + 1, k));
469 }
470 if (*sum_min == std::numeric_limits<int64_t>::min() &&
471 *sum_max == std::numeric_limits<int64_t>::max()) {
472 break;
473 }
474 }
475 }
476
477 void InitialPropagate() override {
478 // Copy vars to leaf nodes.
479 for (int i = 0; i < vars_.size(); ++i) {
480 InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
481 }
482 // Compute up.
483 for (int i = MaxDepth() - 1; i >= 0; --i) {
484 for (int j = 0; j < Width(i); ++j) {
485 int64_t sum_min = 0;
486 int64_t sum_max = 0;
487 SafeComputeNode(i, j, &sum_min, &sum_max);
488 InitNode(i, j, sum_min, sum_max);
489 }
490 }
491 // Propagate to sum_var.
492 target_var_->SetRange(RootMin(), RootMax());
493
494 // Push down.
495 SumChanged();
496 }
497
498 void SumChanged() {
499 DCHECK(CheckInternalState());
500 if (target_var_->Max() == RootMin()) {
501 // We can fix all terms to min.
502 for (int i = 0; i < vars_.size(); ++i) {
503 vars_[i]->SetValue(vars_[i]->Min());
504 }
505 } else if (target_var_->Min() == RootMax()) {
506 // We can fix all terms to max.
507 for (int i = 0; i < vars_.size(); ++i) {
508 vars_[i]->SetValue(vars_[i]->Max());
509 }
510 } else {
511 PushDown(0, 0, target_var_->Min(), target_var_->Max());
512 }
513 }
514
515 void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
516 // Nothing to do?
517 if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
518 return;
519 }
520
521 // Leaf node -> push to leaf var.
522 if (IsLeaf(depth)) {
523 vars_[position]->SetRange(new_min, new_max);
524 return;
525 }
526
527 // Standard propagation from the bounds of the sum to the
528 // individuals terms.
529
530 // These are maintained automatically in the tree structure.
531 const int64_t sum_min = Min(depth, position);
532 const int64_t sum_max = Max(depth, position);
533
534 // Intersect the new bounds with the computed bounds.
535 new_max = std::min(sum_max, new_max);
536 new_min = std::max(sum_min, new_min);
537
538 // Detect failure early.
539 if (new_max < sum_min || new_min > sum_max) {
540 solver()->Fail();
541 }
542
543 // Push to children nodes.
544 const int block_start = ChildStart(position);
545 const int block_end = ChildEnd(depth, position);
546 for (int pos = block_start; pos <= block_end; ++pos) {
547 const int64_t target_var_min = Min(depth + 1, pos);
548 const int64_t residual_min =
550 ? CapSub(sum_min, target_var_min)
551 : std::numeric_limits<int64_t>::min();
552 const int64_t target_var_max = Max(depth + 1, pos);
553 const int64_t residual_max =
555 ? CapSub(sum_max, target_var_max)
556 : std::numeric_limits<int64_t>::max();
557 PushDown(depth + 1, pos,
558 (residual_max == std::numeric_limits<int64_t>::min()
560 : CapSub(new_min, residual_max)),
561 (residual_min == std::numeric_limits<int64_t>::max()
563 : CapSub(new_max, residual_min)));
564 }
565 // TODO(user) : Is the diameter optimization (see reference
566 // above, rule 5) useful?
567 }
568
569 void LeafChanged(int term_index) {
570 IntVar* const var = vars_[term_index];
571 PushUp(term_index, CapSub(var->Min(), var->OldMin()),
572 CapSub(var->OldMax(), var->Max()));
573 EnqueueDelayedDemon(sum_demon_); // TODO(user): Is this needed?
574 }
575
576 void PushUp(int position, int64_t delta_min, int64_t delta_max) {
577 DCHECK_GE(delta_max, 0);
578 DCHECK_GE(delta_min, 0);
579 if (CapAdd(delta_min, delta_max) == 0) {
580 // This may happen if the computation of old min/max has under/overflowed
581 // resulting in no actual change in min and max.
582 return;
583 }
584 bool delta_corrupted = false;
585 for (int depth = MaxDepth(); depth >= 0; --depth) {
586 if (Min(depth, position) != std::numeric_limits<int64_t>::min() &&
587 Max(depth, position) != std::numeric_limits<int64_t>::max() &&
588 delta_min != std::numeric_limits<int64_t>::max() &&
589 delta_max != std::numeric_limits<int64_t>::max() &&
590 !delta_corrupted) { // No overflow.
591 ReduceRange(depth, position, delta_min, delta_max);
592 } else if (depth == MaxDepth()) { // Leaf.
593 SetRange(depth, position, vars_[position]->Min(),
594 vars_[position]->Max());
595 delta_corrupted = true;
596 } else { // Recompute.
597 int64_t sum_min = 0;
598 int64_t sum_max = 0;
599 SafeComputeNode(depth, position, &sum_min, &sum_max);
600 if (sum_min == std::numeric_limits<int64_t>::min() &&
602 return; // Nothing to do upward.
603 }
604 SetRange(depth, position, sum_min, sum_max);
605 delta_corrupted = true;
606 }
607 position = Parent(position);
608 }
609 DCHECK_EQ(0, position);
610 target_var_->SetRange(RootMin(), RootMax());
611 }
612
613 std::string DebugString() const override {
614 return DebugStringInternal("Sum");
615 }
616
617 void Accept(ModelVisitor* const visitor) const override {
618 AcceptInternal(ModelVisitor::kSumEqual, visitor);
619 }
620
621 private:
622 bool CheckInternalState() {
623 for (int i = 0; i < vars_.size(); ++i) {
624 CheckLeaf(i, vars_[i]->Min(), vars_[i]->Max());
625 }
626 // Check up.
627 for (int i = MaxDepth() - 1; i >= 0; --i) {
628 for (int j = 0; j < Width(i); ++j) {
629 int64_t sum_min = 0;
630 int64_t sum_max = 0;
631 SafeComputeNode(i, j, &sum_min, &sum_max);
632 CheckNode(i, j, sum_min, sum_max);
633 }
634 }
635 return true;
636 }
637
638 void CheckLeaf(int position, int64_t var_min, int64_t var_max) {
639 CheckNode(MaxDepth(), position, var_min, var_max);
640 }
641
642 void CheckNode(int depth, int position, int64_t node_min, int64_t node_max) {
643 DCHECK_EQ(Min(depth, position), node_min);
644 DCHECK_EQ(Max(depth, position), node_max);
645 }
646
647 Demon* sum_demon_;
648};
649
650// ---------- Min Array ----------
651
652// This constraint implements min(vars) == min_var.
653class MinConstraint : public TreeArrayConstraint {
654 public:
655 MinConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
656 IntVar* const min_var)
657 : TreeArrayConstraint(solver, vars, min_var), min_demon_(nullptr) {}
658
659 ~MinConstraint() override {}
660
661 void Post() override {
662 for (int i = 0; i < vars_.size(); ++i) {
663 Demon* const demon = MakeConstraintDemon1(
664 solver(), this, &MinConstraint::LeafChanged, "LeafChanged", i);
665 vars_[i]->WhenRange(demon);
666 }
667 min_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
668 solver(), this, &MinConstraint::MinVarChanged, "MinVarChanged"));
669 target_var_->WhenRange(min_demon_);
670 }
671
672 void InitialPropagate() override {
673 // Copy vars to leaf nodes.
674 for (int i = 0; i < vars_.size(); ++i) {
675 InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
676 }
677
678 // Compute up.
679 for (int i = MaxDepth() - 1; i >= 0; --i) {
680 for (int j = 0; j < Width(i); ++j) {
681 int64_t min_min = std::numeric_limits<int64_t>::max();
682 int64_t min_max = std::numeric_limits<int64_t>::max();
683 const int block_start = ChildStart(j);
684 const int block_end = ChildEnd(i, j);
685 for (int k = block_start; k <= block_end; ++k) {
686 min_min = std::min(min_min, Min(i + 1, k));
687 min_max = std::min(min_max, Max(i + 1, k));
688 }
689 InitNode(i, j, min_min, min_max);
690 }
691 }
692 // Propagate to min_var.
693 target_var_->SetRange(RootMin(), RootMax());
694
695 // Push down.
696 MinVarChanged();
697 }
698
699 void MinVarChanged() {
700 PushDown(0, 0, target_var_->Min(), target_var_->Max());
701 }
702
703 void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
704 // Nothing to do?
705 if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
706 return;
707 }
708
709 // Leaf node -> push to leaf var.
710 if (IsLeaf(depth)) {
711 vars_[position]->SetRange(new_min, new_max);
712 return;
713 }
714
715 const int64_t node_min = Min(depth, position);
716 const int64_t node_max = Max(depth, position);
717
718 int candidate = -1;
719 int active = 0;
720 const int block_start = ChildStart(position);
721 const int block_end = ChildEnd(depth, position);
722
723 if (new_max < node_max) {
724 // Look if only one candidat to push the max down.
725 for (int i = block_start; i <= block_end; ++i) {
726 if (Min(depth + 1, i) <= new_max) {
727 if (active++ >= 1) {
728 break;
729 }
730 candidate = i;
731 }
732 }
733 if (active == 0) {
734 solver()->Fail();
735 }
736 }
737
738 if (node_min < new_min) {
739 for (int i = block_start; i <= block_end; ++i) {
740 if (i == candidate && active == 1) {
741 PushDown(depth + 1, i, new_min, new_max);
742 } else {
743 PushDown(depth + 1, i, new_min, Max(depth + 1, i));
744 }
745 }
746 } else if (active == 1) {
747 PushDown(depth + 1, candidate, Min(depth + 1, candidate), new_max);
748 }
749 }
750
751 // TODO(user): Regroup code between Min and Max constraints.
752 void LeafChanged(int term_index) {
753 IntVar* const var = vars_[term_index];
754 SetRange(MaxDepth(), term_index, var->Min(), var->Max());
755 const int parent_depth = MaxDepth() - 1;
756 const int parent = Parent(term_index);
757 const int64_t old_min = var->OldMin();
758 const int64_t var_min = var->Min();
759 const int64_t var_max = var->Max();
760 if ((old_min == Min(parent_depth, parent) && old_min != var_min) ||
761 var_max < Max(parent_depth, parent)) {
762 // Can influence the parent bounds.
763 PushUp(term_index);
764 }
765 }
766
767 void PushUp(int position) {
768 int depth = MaxDepth();
769 while (depth > 0) {
770 const int parent = Parent(position);
771 const int parent_depth = depth - 1;
772 int64_t min_min = std::numeric_limits<int64_t>::max();
773 int64_t min_max = std::numeric_limits<int64_t>::max();
774 const int block_start = ChildStart(parent);
775 const int block_end = ChildEnd(parent_depth, parent);
776 for (int k = block_start; k <= block_end; ++k) {
777 min_min = std::min(min_min, Min(depth, k));
778 min_max = std::min(min_max, Max(depth, k));
779 }
780 if (min_min > Min(parent_depth, parent) ||
781 min_max < Max(parent_depth, parent)) {
782 SetRange(parent_depth, parent, min_min, min_max);
783 } else {
784 break;
785 }
786 depth = parent_depth;
787 position = parent;
788 }
789 if (depth == 0) { // We have pushed all the way up.
790 target_var_->SetRange(RootMin(), RootMax());
791 }
792 MinVarChanged();
793 }
794
795 std::string DebugString() const override {
796 return DebugStringInternal("Min");
797 }
798
799 void Accept(ModelVisitor* const visitor) const override {
800 AcceptInternal(ModelVisitor::kMinEqual, visitor);
801 }
802
803 private:
804 Demon* min_demon_;
805};
806
807class SmallMinConstraint : public Constraint {
808 public:
809 SmallMinConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
810 IntVar* const target_var)
811 : Constraint(solver),
812 vars_(vars),
813 target_var_(target_var),
814 computed_min_(0),
815 computed_max_(0) {}
816
817 ~SmallMinConstraint() override {}
818
819 void Post() override {
820 for (int i = 0; i < vars_.size(); ++i) {
821 if (!vars_[i]->Bound()) {
822 Demon* const demon = MakeConstraintDemon1(
823 solver(), this, &SmallMinConstraint::VarChanged, "VarChanged",
824 vars_[i]);
825 vars_[i]->WhenRange(demon);
826 }
827 }
828 Demon* const mdemon = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
829 solver(), this, &SmallMinConstraint::MinVarChanged, "MinVarChanged"));
830 target_var_->WhenRange(mdemon);
831 }
832
833 void InitialPropagate() override {
834 int64_t min_min = std::numeric_limits<int64_t>::max();
835 int64_t min_max = std::numeric_limits<int64_t>::max();
836 for (IntVar* const var : vars_) {
837 min_min = std::min(min_min, var->Min());
838 min_max = std::min(min_max, var->Max());
839 }
840 computed_min_.SetValue(solver(), min_min);
841 computed_max_.SetValue(solver(), min_max);
842 // Propagate to min_var.
843 target_var_->SetRange(min_min, min_max);
844
845 // Push down.
846 MinVarChanged();
847 }
848
849 std::string DebugString() const override {
850 return absl::StrFormat("SmallMin(%s) == %s",
852 target_var_->DebugString());
853 }
854
855 void Accept(ModelVisitor* const visitor) const override {
856 visitor->BeginVisitConstraint(ModelVisitor::kMinEqual, this);
857 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
858 vars_);
859 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
861 visitor->EndVisitConstraint(ModelVisitor::kMinEqual, this);
862 }
863
864 private:
865 void VarChanged(IntVar* var) {
866 const int64_t old_min = var->OldMin();
867 const int64_t var_min = var->Min();
868 const int64_t var_max = var->Max();
869 if ((old_min == computed_min_.Value() && old_min != var_min) ||
870 var_max < computed_max_.Value()) {
871 // Can influence the min var bounds.
872 int64_t min_min = std::numeric_limits<int64_t>::max();
873 int64_t min_max = std::numeric_limits<int64_t>::max();
874 for (IntVar* const var : vars_) {
875 min_min = std::min(min_min, var->Min());
876 min_max = std::min(min_max, var->Max());
877 }
878 if (min_min > computed_min_.Value() || min_max < computed_max_.Value()) {
879 computed_min_.SetValue(solver(), min_min);
880 computed_max_.SetValue(solver(), min_max);
881 target_var_->SetRange(computed_min_.Value(), computed_max_.Value());
882 }
883 }
884 MinVarChanged();
885 }
886
887 void MinVarChanged() {
888 const int64_t new_min = target_var_->Min();
889 const int64_t new_max = target_var_->Max();
890 // Nothing to do?
891 if (new_min <= computed_min_.Value() && new_max >= computed_max_.Value()) {
892 return;
893 }
894
895 IntVar* candidate = nullptr;
896 int active = 0;
897
898 if (new_max < computed_max_.Value()) {
899 // Look if only one candidate to push the max down.
900 for (IntVar* const var : vars_) {
901 if (var->Min() <= new_max) {
902 if (active++ >= 1) {
903 break;
904 }
905 candidate = var;
906 }
907 }
908 if (active == 0) {
909 solver()->Fail();
910 }
911 }
912 if (computed_min_.Value() < new_min) {
913 if (active == 1) {
914 candidate->SetRange(new_min, new_max);
915 } else {
916 for (IntVar* const var : vars_) {
917 var->SetMin(new_min);
918 }
919 }
920 } else if (active == 1) {
921 candidate->SetMax(new_max);
922 }
923 }
924
925 std::vector<IntVar*> vars_;
926 IntVar* const target_var_;
927 Rev<int64_t> computed_min_;
928 Rev<int64_t> computed_max_;
929};
930
931// ---------- Max Array ----------
932
933// This constraint implements max(vars) == max_var.
934class MaxConstraint : public TreeArrayConstraint {
935 public:
936 MaxConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
937 IntVar* const max_var)
938 : TreeArrayConstraint(solver, vars, max_var), max_demon_(nullptr) {}
939
940 ~MaxConstraint() override {}
941
942 void Post() override {
943 for (int i = 0; i < vars_.size(); ++i) {
944 Demon* const demon = MakeConstraintDemon1(
945 solver(), this, &MaxConstraint::LeafChanged, "LeafChanged", i);
946 vars_[i]->WhenRange(demon);
947 }
948 max_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
949 solver(), this, &MaxConstraint::MaxVarChanged, "MaxVarChanged"));
950 target_var_->WhenRange(max_demon_);
951 }
952
953 void InitialPropagate() override {
954 // Copy vars to leaf nodes.
955 for (int i = 0; i < vars_.size(); ++i) {
956 InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
957 }
958
959 // Compute up.
960 for (int i = MaxDepth() - 1; i >= 0; --i) {
961 for (int j = 0; j < Width(i); ++j) {
962 int64_t max_min = std::numeric_limits<int64_t>::min();
963 int64_t max_max = std::numeric_limits<int64_t>::min();
964 const int block_start = ChildStart(j);
965 const int block_end = ChildEnd(i, j);
966 for (int k = block_start; k <= block_end; ++k) {
967 max_min = std::max(max_min, Min(i + 1, k));
968 max_max = std::max(max_max, Max(i + 1, k));
969 }
970 InitNode(i, j, max_min, max_max);
971 }
972 }
973 // Propagate to min_var.
974 target_var_->SetRange(RootMin(), RootMax());
975
976 // Push down.
977 MaxVarChanged();
978 }
979
980 void MaxVarChanged() {
981 PushDown(0, 0, target_var_->Min(), target_var_->Max());
982 }
983
984 void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
985 // Nothing to do?
986 if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
987 return;
988 }
989
990 // Leaf node -> push to leaf var.
991 if (IsLeaf(depth)) {
992 vars_[position]->SetRange(new_min, new_max);
993 return;
994 }
995
996 const int64_t node_min = Min(depth, position);
997 const int64_t node_max = Max(depth, position);
998
999 int candidate = -1;
1000 int active = 0;
1001 const int block_start = ChildStart(position);
1002 const int block_end = ChildEnd(depth, position);
1003
1004 if (node_min < new_min) {
1005 // Look if only one candidat to push the max down.
1006 for (int i = block_start; i <= block_end; ++i) {
1007 if (Max(depth + 1, i) >= new_min) {
1008 if (active++ >= 1) {
1009 break;
1010 }
1011 candidate = i;
1012 }
1013 }
1014 if (active == 0) {
1015 solver()->Fail();
1016 }
1017 }
1018
1019 if (node_max > new_max) {
1020 for (int i = block_start; i <= block_end; ++i) {
1021 if (i == candidate && active == 1) {
1022 PushDown(depth + 1, i, new_min, new_max);
1023 } else {
1024 PushDown(depth + 1, i, Min(depth + 1, i), new_max);
1025 }
1026 }
1027 } else if (active == 1) {
1028 PushDown(depth + 1, candidate, new_min, Max(depth + 1, candidate));
1029 }
1030 }
1031
1032 void LeafChanged(int term_index) {
1033 IntVar* const var = vars_[term_index];
1034 SetRange(MaxDepth(), term_index, var->Min(), var->Max());
1035 const int parent_depth = MaxDepth() - 1;
1036 const int parent = Parent(term_index);
1037 const int64_t old_max = var->OldMax();
1038 const int64_t var_min = var->Min();
1039 const int64_t var_max = var->Max();
1040 if ((old_max == Max(parent_depth, parent) && old_max != var_max) ||
1041 var_min > Min(parent_depth, parent)) {
1042 // Can influence the parent bounds.
1043 PushUp(term_index);
1044 }
1045 }
1046
1047 void PushUp(int position) {
1048 int depth = MaxDepth();
1049 while (depth > 0) {
1050 const int parent = Parent(position);
1051 const int parent_depth = depth - 1;
1052 int64_t max_min = std::numeric_limits<int64_t>::min();
1053 int64_t max_max = std::numeric_limits<int64_t>::min();
1054 const int block_start = ChildStart(parent);
1055 const int block_end = ChildEnd(parent_depth, parent);
1056 for (int k = block_start; k <= block_end; ++k) {
1057 max_min = std::max(max_min, Min(depth, k));
1058 max_max = std::max(max_max, Max(depth, k));
1059 }
1060 if (max_min > Min(parent_depth, parent) ||
1061 max_max < Max(parent_depth, parent)) {
1062 SetRange(parent_depth, parent, max_min, max_max);
1063 } else {
1064 break;
1065 }
1066 depth = parent_depth;
1067 position = parent;
1068 }
1069 if (depth == 0) { // We have pushed all the way up.
1070 target_var_->SetRange(RootMin(), RootMax());
1071 }
1072 MaxVarChanged();
1073 }
1074
1075 std::string DebugString() const override {
1076 return DebugStringInternal("Max");
1077 }
1078
1079 void Accept(ModelVisitor* const visitor) const override {
1080 AcceptInternal(ModelVisitor::kMaxEqual, visitor);
1081 }
1082
1083 private:
1084 Demon* max_demon_;
1085};
1086
1087class SmallMaxConstraint : public Constraint {
1088 public:
1089 SmallMaxConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
1090 IntVar* const target_var)
1091 : Constraint(solver),
1092 vars_(vars),
1093 target_var_(target_var),
1094 computed_min_(0),
1095 computed_max_(0) {}
1096
1097 ~SmallMaxConstraint() override {}
1098
1099 void Post() override {
1100 for (int i = 0; i < vars_.size(); ++i) {
1101 if (!vars_[i]->Bound()) {
1102 Demon* const demon = MakeConstraintDemon1(
1103 solver(), this, &SmallMaxConstraint::VarChanged, "VarChanged",
1104 vars_[i]);
1105 vars_[i]->WhenRange(demon);
1106 }
1107 }
1108 Demon* const mdemon = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
1109 solver(), this, &SmallMaxConstraint::MaxVarChanged, "MinVarChanged"));
1110 target_var_->WhenRange(mdemon);
1111 }
1112
1113 void InitialPropagate() override {
1114 int64_t max_min = std::numeric_limits<int64_t>::min();
1115 int64_t max_max = std::numeric_limits<int64_t>::min();
1116 for (IntVar* const var : vars_) {
1117 max_min = std::max(max_min, var->Min());
1118 max_max = std::max(max_max, var->Max());
1119 }
1120 computed_min_.SetValue(solver(), max_min);
1121 computed_max_.SetValue(solver(), max_max);
1122 // Propagate to min_var.
1123 target_var_->SetRange(max_min, max_max);
1124
1125 // Push down.
1126 MaxVarChanged();
1127 }
1128
1129 std::string DebugString() const override {
1130 return absl::StrFormat("SmallMax(%s) == %s",
1132 target_var_->DebugString());
1133 }
1134
1135 void Accept(ModelVisitor* const visitor) const override {
1136 visitor->BeginVisitConstraint(ModelVisitor::kMaxEqual, this);
1137 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1138 vars_);
1139 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
1140 target_var_);
1141 visitor->EndVisitConstraint(ModelVisitor::kMaxEqual, this);
1142 }
1143
1144 private:
1145 void VarChanged(IntVar* var) {
1146 const int64_t old_max = var->OldMax();
1147 const int64_t var_min = var->Min();
1148 const int64_t var_max = var->Max();
1149 if ((old_max == computed_max_.Value() && old_max != var_max) ||
1150 var_min > computed_min_.Value()) { // REWRITE
1151 // Can influence the min var bounds.
1152 int64_t max_min = std::numeric_limits<int64_t>::min();
1153 int64_t max_max = std::numeric_limits<int64_t>::min();
1154 for (IntVar* const var : vars_) {
1155 max_min = std::max(max_min, var->Min());
1156 max_max = std::max(max_max, var->Max());
1157 }
1158 if (max_min > computed_min_.Value() || max_max < computed_max_.Value()) {
1159 computed_min_.SetValue(solver(), max_min);
1160 computed_max_.SetValue(solver(), max_max);
1161 target_var_->SetRange(computed_min_.Value(), computed_max_.Value());
1162 }
1163 }
1164 MaxVarChanged();
1165 }
1166
1167 void MaxVarChanged() {
1168 const int64_t new_min = target_var_->Min();
1169 const int64_t new_max = target_var_->Max();
1170 // Nothing to do?
1171 if (new_min <= computed_min_.Value() && new_max >= computed_max_.Value()) {
1172 return;
1173 }
1174
1175 IntVar* candidate = nullptr;
1176 int active = 0;
1177
1178 if (new_min > computed_min_.Value()) {
1179 // Look if only one candidate to push the max down.
1180 for (IntVar* const var : vars_) {
1181 if (var->Max() >= new_min) {
1182 if (active++ >= 1) {
1183 break;
1184 }
1185 candidate = var;
1186 }
1187 }
1188 if (active == 0) {
1189 solver()->Fail();
1190 }
1191 }
1192 if (computed_max_.Value() > new_max) {
1193 if (active == 1) {
1194 candidate->SetRange(new_min, new_max);
1195 } else {
1196 for (IntVar* const var : vars_) {
1197 var->SetMax(new_max);
1198 }
1199 }
1200 } else if (active == 1) {
1201 candidate->SetMin(new_min);
1202 }
1203 }
1204
1205 std::vector<IntVar*> vars_;
1206 IntVar* const target_var_;
1207 Rev<int64_t> computed_min_;
1208 Rev<int64_t> computed_max_;
1209};
1210
1211// Boolean And and Ors
1212
1213class ArrayBoolAndEq : public CastConstraint {
1214 public:
1215 ArrayBoolAndEq(Solver* const s, const std::vector<IntVar*>& vars,
1216 IntVar* const target)
1217 : CastConstraint(s, target),
1218 vars_(vars),
1219 demons_(vars.size()),
1220 unbounded_(0) {}
1221
1222 ~ArrayBoolAndEq() override {}
1223
1224 void Post() override {
1225 for (int i = 0; i < vars_.size(); ++i) {
1226 if (!vars_[i]->Bound()) {
1227 demons_[i] =
1228 MakeConstraintDemon1(solver(), this, &ArrayBoolAndEq::PropagateVar,
1229 "PropagateVar", vars_[i]);
1230 vars_[i]->WhenBound(demons_[i]);
1231 }
1232 }
1233 if (!target_var_->Bound()) {
1234 Demon* const target_demon = MakeConstraintDemon0(
1235 solver(), this, &ArrayBoolAndEq::PropagateTarget, "PropagateTarget");
1236 target_var_->WhenBound(target_demon);
1237 }
1238 }
1239
1240 void InitialPropagate() override {
1241 target_var_->SetRange(0, 1);
1242 if (target_var_->Min() == 1) {
1243 for (int i = 0; i < vars_.size(); ++i) {
1244 vars_[i]->SetMin(1);
1245 }
1246 } else {
1247 int possible_zero = -1;
1248 int ones = 0;
1249 int unbounded = 0;
1250 for (int i = 0; i < vars_.size(); ++i) {
1251 if (!vars_[i]->Bound()) {
1252 unbounded++;
1253 possible_zero = i;
1254 } else if (vars_[i]->Max() == 0) {
1255 InhibitAll();
1256 target_var_->SetMax(0);
1257 return;
1258 } else {
1259 DCHECK_EQ(1, vars_[i]->Min());
1260 ones++;
1261 }
1262 }
1263 if (unbounded == 0) {
1264 target_var_->SetMin(1);
1265 } else if (target_var_->Max() == 0 && unbounded == 1) {
1266 CHECK_NE(-1, possible_zero);
1267 vars_[possible_zero]->SetMax(0);
1268 } else {
1269 unbounded_.SetValue(solver(), unbounded);
1270 }
1271 }
1272 }
1273
1274 void PropagateVar(IntVar* var) {
1275 if (var->Min() == 1) {
1276 unbounded_.Decr(solver());
1277 if (unbounded_.Value() == 0 && !decided_.Switched()) {
1278 target_var_->SetMin(1);
1279 decided_.Switch(solver());
1280 } else if (target_var_->Max() == 0 && unbounded_.Value() == 1 &&
1281 !decided_.Switched()) {
1282 ForceToZero();
1283 }
1284 } else {
1285 InhibitAll();
1286 target_var_->SetMax(0);
1287 }
1288 }
1289
1290 void PropagateTarget() {
1291 if (target_var_->Min() == 1) {
1292 for (int i = 0; i < vars_.size(); ++i) {
1293 vars_[i]->SetMin(1);
1294 }
1295 } else {
1296 if (unbounded_.Value() == 1 && !decided_.Switched()) {
1297 ForceToZero();
1298 }
1299 }
1300 }
1301
1302 std::string DebugString() const override {
1303 return absl::StrFormat("And(%s) == %s", JoinDebugStringPtr(vars_, ", "),
1304 target_var_->DebugString());
1305 }
1306
1307 void Accept(ModelVisitor* const visitor) const override {
1308 visitor->BeginVisitConstraint(ModelVisitor::kMinEqual, this);
1309 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1310 vars_);
1311 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
1312 target_var_);
1313 visitor->EndVisitConstraint(ModelVisitor::kMinEqual, this);
1314 }
1315
1316 private:
1317 void InhibitAll() {
1318 for (int i = 0; i < demons_.size(); ++i) {
1319 if (demons_[i] != nullptr) {
1320 demons_[i]->inhibit(solver());
1321 }
1322 }
1323 }
1324
1325 void ForceToZero() {
1326 for (int i = 0; i < vars_.size(); ++i) {
1327 if (vars_[i]->Min() == 0) {
1328 vars_[i]->SetValue(0);
1329 decided_.Switch(solver());
1330 return;
1331 }
1332 }
1333 solver()->Fail();
1334 }
1335
1336 const std::vector<IntVar*> vars_;
1337 std::vector<Demon*> demons_;
1338 NumericalRev<int> unbounded_;
1339 RevSwitch decided_;
1340};
1341
1342class ArrayBoolOrEq : public CastConstraint {
1343 public:
1344 ArrayBoolOrEq(Solver* const s, const std::vector<IntVar*>& vars,
1345 IntVar* const target)
1346 : CastConstraint(s, target),
1347 vars_(vars),
1348 demons_(vars.size()),
1349 unbounded_(0) {}
1350
1351 ~ArrayBoolOrEq() override {}
1352
1353 void Post() override {
1354 for (int i = 0; i < vars_.size(); ++i) {
1355 if (!vars_[i]->Bound()) {
1356 demons_[i] =
1357 MakeConstraintDemon1(solver(), this, &ArrayBoolOrEq::PropagateVar,
1358 "PropagateVar", vars_[i]);
1359 vars_[i]->WhenBound(demons_[i]);
1360 }
1361 }
1362 if (!target_var_->Bound()) {
1363 Demon* const target_demon = MakeConstraintDemon0(
1364 solver(), this, &ArrayBoolOrEq::PropagateTarget, "PropagateTarget");
1365 target_var_->WhenBound(target_demon);
1366 }
1367 }
1368
1369 void InitialPropagate() override {
1370 target_var_->SetRange(0, 1);
1371 if (target_var_->Max() == 0) {
1372 for (int i = 0; i < vars_.size(); ++i) {
1373 vars_[i]->SetMax(0);
1374 }
1375 } else {
1376 int zeros = 0;
1377 int possible_one = -1;
1378 int unbounded = 0;
1379 for (int i = 0; i < vars_.size(); ++i) {
1380 if (!vars_[i]->Bound()) {
1381 unbounded++;
1382 possible_one = i;
1383 } else if (vars_[i]->Min() == 1) {
1384 InhibitAll();
1385 target_var_->SetMin(1);
1386 return;
1387 } else {
1388 DCHECK_EQ(0, vars_[i]->Max());
1389 zeros++;
1390 }
1391 }
1392 if (unbounded == 0) {
1393 target_var_->SetMax(0);
1394 } else if (target_var_->Min() == 1 && unbounded == 1) {
1395 CHECK_NE(-1, possible_one);
1396 vars_[possible_one]->SetMin(1);
1397 } else {
1398 unbounded_.SetValue(solver(), unbounded);
1399 }
1400 }
1401 }
1402
1403 void PropagateVar(IntVar* var) {
1404 if (var->Min() == 0) {
1405 unbounded_.Decr(solver());
1406 if (unbounded_.Value() == 0 && !decided_.Switched()) {
1407 target_var_->SetMax(0);
1408 decided_.Switch(solver());
1409 }
1410 if (target_var_->Min() == 1 && unbounded_.Value() == 1 &&
1411 !decided_.Switched()) {
1412 ForceToOne();
1413 }
1414 } else {
1415 InhibitAll();
1416 target_var_->SetMin(1);
1417 }
1418 }
1419
1420 void PropagateTarget() {
1421 if (target_var_->Max() == 0) {
1422 for (int i = 0; i < vars_.size(); ++i) {
1423 vars_[i]->SetMax(0);
1424 }
1425 } else {
1426 if (unbounded_.Value() == 1 && !decided_.Switched()) {
1427 ForceToOne();
1428 }
1429 }
1430 }
1431
1432 std::string DebugString() const override {
1433 return absl::StrFormat("Or(%s) == %s", JoinDebugStringPtr(vars_, ", "),
1434 target_var_->DebugString());
1435 }
1436
1437 void Accept(ModelVisitor* const visitor) const override {
1438 visitor->BeginVisitConstraint(ModelVisitor::kMaxEqual, this);
1439 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1440 vars_);
1441 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
1442 target_var_);
1443 visitor->EndVisitConstraint(ModelVisitor::kMaxEqual, this);
1444 }
1445
1446 private:
1447 void InhibitAll() {
1448 for (int i = 0; i < demons_.size(); ++i) {
1449 if (demons_[i] != nullptr) {
1450 demons_[i]->inhibit(solver());
1451 }
1452 }
1453 }
1454
1455 void ForceToOne() {
1456 for (int i = 0; i < vars_.size(); ++i) {
1457 if (vars_[i]->Max() == 1) {
1458 vars_[i]->SetValue(1);
1459 decided_.Switch(solver());
1460 return;
1461 }
1462 }
1463 solver()->Fail();
1464 }
1465
1466 const std::vector<IntVar*> vars_;
1467 std::vector<Demon*> demons_;
1468 NumericalRev<int> unbounded_;
1469 RevSwitch decided_;
1470};
1471
1472// ---------- Specialized cases ----------
1473
1474class BaseSumBooleanConstraint : public Constraint {
1475 public:
1476 BaseSumBooleanConstraint(Solver* const s, const std::vector<IntVar*>& vars)
1477 : Constraint(s), vars_(vars) {}
1478
1479 ~BaseSumBooleanConstraint() override {}
1480
1481 protected:
1482 std::string DebugStringInternal(const std::string& name) const {
1483 return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", "));
1484 }
1485
1486 const std::vector<IntVar*> vars_;
1487 RevSwitch inactive_;
1488};
1489
1490// ----- Sum of Boolean <= 1 -----
1491
1492class SumBooleanLessOrEqualToOne : public BaseSumBooleanConstraint {
1493 public:
1494 SumBooleanLessOrEqualToOne(Solver* const s, const std::vector<IntVar*>& vars)
1495 : BaseSumBooleanConstraint(s, vars) {}
1496
1497 ~SumBooleanLessOrEqualToOne() override {}
1498
1499 void Post() override {
1500 for (int i = 0; i < vars_.size(); ++i) {
1501 if (!vars_[i]->Bound()) {
1502 Demon* u = MakeConstraintDemon1(solver(), this,
1503 &SumBooleanLessOrEqualToOne::Update,
1504 "Update", vars_[i]);
1505 vars_[i]->WhenBound(u);
1506 }
1507 }
1508 }
1509
1510 void InitialPropagate() override {
1511 for (int i = 0; i < vars_.size(); ++i) {
1512 if (vars_[i]->Min() == 1) {
1513 PushAllToZeroExcept(vars_[i]);
1514 return;
1515 }
1516 }
1517 }
1518
1519 void Update(IntVar* var) {
1520 if (!inactive_.Switched()) {
1521 DCHECK(var->Bound());
1522 if (var->Min() == 1) {
1523 PushAllToZeroExcept(var);
1524 }
1525 }
1526 }
1527
1528 void PushAllToZeroExcept(IntVar* var) {
1529 inactive_.Switch(solver());
1530 for (int i = 0; i < vars_.size(); ++i) {
1531 IntVar* const other = vars_[i];
1532 if (other != var && other->Max() != 0) {
1533 other->SetMax(0);
1534 }
1535 }
1536 }
1537
1538 std::string DebugString() const override {
1539 return DebugStringInternal("SumBooleanLessOrEqualToOne");
1540 }
1541
1542 void Accept(ModelVisitor* const visitor) const override {
1543 visitor->BeginVisitConstraint(ModelVisitor::kSumLessOrEqual, this);
1544 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1545 vars_);
1546 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
1547 visitor->EndVisitConstraint(ModelVisitor::kSumLessOrEqual, this);
1548 }
1549};
1550
1551// ----- Sum of Boolean >= 1 -----
1552
1553// We implement this one as a Max(array) == 1.
1554
1555class SumBooleanGreaterOrEqualToOne : public BaseSumBooleanConstraint {
1556 public:
1557 SumBooleanGreaterOrEqualToOne(Solver* const s,
1558 const std::vector<IntVar*>& vars);
1559 ~SumBooleanGreaterOrEqualToOne() override {}
1560
1561 void Post() override;
1562 void InitialPropagate() override;
1563
1564 void Update(int index);
1565 void UpdateVar();
1566
1567 std::string DebugString() const override;
1568
1569 void Accept(ModelVisitor* const visitor) const override {
1570 visitor->BeginVisitConstraint(ModelVisitor::kSumGreaterOrEqual, this);
1571 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1572 vars_);
1573 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
1574 visitor->EndVisitConstraint(ModelVisitor::kSumGreaterOrEqual, this);
1575 }
1576
1577 private:
1578 RevBitSet bits_;
1579};
1580
1581SumBooleanGreaterOrEqualToOne::SumBooleanGreaterOrEqualToOne(
1582 Solver* const s, const std::vector<IntVar*>& vars)
1583 : BaseSumBooleanConstraint(s, vars), bits_(vars.size()) {}
1584
1585void SumBooleanGreaterOrEqualToOne::Post() {
1586 for (int i = 0; i < vars_.size(); ++i) {
1587 Demon* d = MakeConstraintDemon1(
1588 solver(), this, &SumBooleanGreaterOrEqualToOne::Update, "Update", i);
1589 vars_[i]->WhenRange(d);
1590 }
1591}
1592
1593void SumBooleanGreaterOrEqualToOne::InitialPropagate() {
1594 for (int i = 0; i < vars_.size(); ++i) {
1595 IntVar* const var = vars_[i];
1596 if (var->Min() == 1LL) {
1597 inactive_.Switch(solver());
1598 return;
1599 }
1600 if (var->Max() == 1LL) {
1601 bits_.SetToOne(solver(), i);
1602 }
1603 }
1604 if (bits_.IsCardinalityZero()) {
1605 solver()->Fail();
1606 } else if (bits_.IsCardinalityOne()) {
1607 vars_[bits_.GetFirstBit(0)]->SetValue(int64_t{1});
1608 inactive_.Switch(solver());
1609 }
1610}
1611
1612void SumBooleanGreaterOrEqualToOne::Update(int index) {
1613 if (!inactive_.Switched()) {
1614 if (vars_[index]->Min() == 1LL) { // Bound to 1.
1615 inactive_.Switch(solver());
1616 } else {
1617 bits_.SetToZero(solver(), index);
1618 if (bits_.IsCardinalityZero()) {
1619 solver()->Fail();
1620 } else if (bits_.IsCardinalityOne()) {
1621 vars_[bits_.GetFirstBit(0)]->SetValue(int64_t{1});
1622 inactive_.Switch(solver());
1623 }
1624 }
1625 }
1626}
1627
1628std::string SumBooleanGreaterOrEqualToOne::DebugString() const {
1629 return DebugStringInternal("SumBooleanGreaterOrEqualToOne");
1630}
1631
1632// ----- Sum of Boolean == 1 -----
1633
1634class SumBooleanEqualToOne : public BaseSumBooleanConstraint {
1635 public:
1636 SumBooleanEqualToOne(Solver* const s, const std::vector<IntVar*>& vars)
1637 : BaseSumBooleanConstraint(s, vars), active_vars_(0) {}
1638
1639 ~SumBooleanEqualToOne() override {}
1640
1641 void Post() override {
1642 for (int i = 0; i < vars_.size(); ++i) {
1643 Demon* u = MakeConstraintDemon1(
1644 solver(), this, &SumBooleanEqualToOne::Update, "Update", i);
1645 vars_[i]->WhenBound(u);
1646 }
1647 }
1648
1649 void InitialPropagate() override {
1650 int min1 = 0;
1651 int max1 = 0;
1652 int index_min = -1;
1653 int index_max = -1;
1654 for (int i = 0; i < vars_.size(); ++i) {
1655 const IntVar* const var = vars_[i];
1656 if (var->Min() == 1) {
1657 min1++;
1658 index_min = i;
1659 }
1660 if (var->Max() == 1) {
1661 max1++;
1662 index_max = i;
1663 }
1664 }
1665 if (min1 > 1 || max1 == 0) {
1666 solver()->Fail();
1667 } else if (min1 == 1) {
1668 DCHECK_NE(-1, index_min);
1669 PushAllToZeroExcept(index_min);
1670 } else if (max1 == 1) {
1671 DCHECK_NE(-1, index_max);
1672 vars_[index_max]->SetValue(1);
1673 inactive_.Switch(solver());
1674 } else {
1675 active_vars_.SetValue(solver(), max1);
1676 }
1677 }
1678
1679 void Update(int index) {
1680 if (!inactive_.Switched()) {
1681 DCHECK(vars_[index]->Bound());
1682 const int64_t value = vars_[index]->Min(); // Faster than Value().
1683 if (value == 0) {
1684 active_vars_.Decr(solver());
1685 DCHECK_GE(active_vars_.Value(), 0);
1686 if (active_vars_.Value() == 0) {
1687 solver()->Fail();
1688 } else if (active_vars_.Value() == 1) {
1689 bool found = false;
1690 for (int i = 0; i < vars_.size(); ++i) {
1691 IntVar* const var = vars_[i];
1692 if (var->Max() == 1) {
1693 var->SetValue(1);
1694 PushAllToZeroExcept(i);
1695 found = true;
1696 break;
1697 }
1698 }
1699 if (!found) {
1700 solver()->Fail();
1701 }
1702 }
1703 } else {
1704 PushAllToZeroExcept(index);
1705 }
1706 }
1707 }
1708
1709 void PushAllToZeroExcept(int index) {
1710 inactive_.Switch(solver());
1711 for (int i = 0; i < vars_.size(); ++i) {
1712 if (i != index && vars_[i]->Max() != 0) {
1713 vars_[i]->SetMax(0);
1714 }
1715 }
1716 }
1717
1718 std::string DebugString() const override {
1719 return DebugStringInternal("SumBooleanEqualToOne");
1720 }
1721
1722 void Accept(ModelVisitor* const visitor) const override {
1723 visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
1724 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1725 vars_);
1726 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
1727 visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
1728 }
1729
1730 private:
1731 NumericalRev<int> active_vars_;
1732};
1733
1734// ----- Sum of Boolean Equal To Var -----
1735
1736class SumBooleanEqualToVar : public BaseSumBooleanConstraint {
1737 public:
1738 SumBooleanEqualToVar(Solver* const s, const std::vector<IntVar*>& bool_vars,
1739 IntVar* const sum_var)
1740 : BaseSumBooleanConstraint(s, bool_vars),
1741 num_possible_true_vars_(0),
1742 num_always_true_vars_(0),
1743 sum_var_(sum_var) {}
1744
1745 ~SumBooleanEqualToVar() override {}
1746
1747 void Post() override {
1748 for (int i = 0; i < vars_.size(); ++i) {
1749 Demon* const u = MakeConstraintDemon1(
1750 solver(), this, &SumBooleanEqualToVar::Update, "Update", i);
1751 vars_[i]->WhenBound(u);
1752 }
1753 if (!sum_var_->Bound()) {
1754 Demon* const u = MakeConstraintDemon0(
1755 solver(), this, &SumBooleanEqualToVar::UpdateVar, "UpdateVar");
1756 sum_var_->WhenRange(u);
1757 }
1758 }
1759
1760 void InitialPropagate() override {
1761 int num_always_true_vars = 0;
1762 int possible_true = 0;
1763 for (int i = 0; i < vars_.size(); ++i) {
1764 const IntVar* const var = vars_[i];
1765 if (var->Min() == 1) {
1766 num_always_true_vars++;
1767 }
1768 if (var->Max() == 1) {
1769 possible_true++;
1770 }
1771 }
1772 sum_var_->SetRange(num_always_true_vars, possible_true);
1773 const int64_t var_min = sum_var_->Min();
1774 const int64_t var_max = sum_var_->Max();
1775 if (num_always_true_vars == var_max && possible_true > var_max) {
1776 PushAllUnboundToZero();
1777 } else if (possible_true == var_min && num_always_true_vars < var_min) {
1778 PushAllUnboundToOne();
1779 } else {
1780 num_possible_true_vars_.SetValue(solver(), possible_true);
1781 num_always_true_vars_.SetValue(solver(), num_always_true_vars);
1782 }
1783 }
1784
1785 void UpdateVar() {
1786 if (!inactive_.Switched()) {
1787 if (num_possible_true_vars_.Value() == sum_var_->Min()) {
1788 PushAllUnboundToOne();
1789 sum_var_->SetValue(num_possible_true_vars_.Value());
1790 } else if (num_always_true_vars_.Value() == sum_var_->Max()) {
1791 PushAllUnboundToZero();
1792 sum_var_->SetValue(num_always_true_vars_.Value());
1793 }
1794 }
1795 }
1796
1797 void Update(int index) {
1798 if (!inactive_.Switched()) {
1799 DCHECK(vars_[index]->Bound());
1800 const int64_t value = vars_[index]->Min(); // Faster than Value().
1801 if (value == 0) {
1802 num_possible_true_vars_.Decr(solver());
1803 sum_var_->SetRange(num_always_true_vars_.Value(),
1804 num_possible_true_vars_.Value());
1805 if (num_possible_true_vars_.Value() == sum_var_->Min()) {
1806 PushAllUnboundToOne();
1807 }
1808 } else {
1809 DCHECK_EQ(1, value);
1810 num_always_true_vars_.Incr(solver());
1811 sum_var_->SetRange(num_always_true_vars_.Value(),
1812 num_possible_true_vars_.Value());
1813 if (num_always_true_vars_.Value() == sum_var_->Max()) {
1814 PushAllUnboundToZero();
1815 }
1816 }
1817 }
1818 }
1819
1820 void PushAllUnboundToZero() {
1821 int64_t counter = 0;
1822 inactive_.Switch(solver());
1823 for (int i = 0; i < vars_.size(); ++i) {
1824 if (vars_[i]->Min() == 0) {
1825 vars_[i]->SetValue(0);
1826 } else {
1827 counter++;
1828 }
1829 }
1830 if (counter < sum_var_->Min() || counter > sum_var_->Max()) {
1831 solver()->Fail();
1832 }
1833 }
1834
1835 void PushAllUnboundToOne() {
1836 int64_t counter = 0;
1837 inactive_.Switch(solver());
1838 for (int i = 0; i < vars_.size(); ++i) {
1839 if (vars_[i]->Max() == 1) {
1840 vars_[i]->SetValue(1);
1841 counter++;
1842 }
1843 }
1844 if (counter < sum_var_->Min() || counter > sum_var_->Max()) {
1845 solver()->Fail();
1846 }
1847 }
1848
1849 std::string DebugString() const override {
1850 return absl::StrFormat("%s == %s", DebugStringInternal("SumBoolean"),
1851 sum_var_->DebugString());
1852 }
1853
1854 void Accept(ModelVisitor* const visitor) const override {
1855 visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
1856 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1857 vars_);
1858 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
1859 sum_var_);
1860 visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
1861 }
1862
1863 private:
1864 NumericalRev<int> num_possible_true_vars_;
1865 NumericalRev<int> num_always_true_vars_;
1866 IntVar* const sum_var_;
1867};
1868
1869// ---------- ScalProd ----------
1870
1871// ----- Boolean Scal Prod -----
1872
1873struct Container {
1874 IntVar* var;
1875 int64_t coef;
1876 Container(IntVar* v, int64_t c) : var(v), coef(c) {}
1877 bool operator<(const Container& c) const { return (coef < c.coef); }
1878};
1879
1880// This method will sort both vars and coefficients in increasing
1881// coefficient order. Vars with null coefficients will be
1882// removed. Bound vars will be collected and the sum of the
1883// corresponding products (when the var is bound to 1) is returned by
1884// this method.
1885// If keep_inside is true, the constant will be added back into the
1886// scalprod as IntConst(1) * constant.
1887int64_t SortBothChangeConstant(std::vector<IntVar*>* const vars,
1888 std::vector<int64_t>* const coefs,
1889 bool keep_inside) {
1890 CHECK(vars != nullptr);
1891 CHECK(coefs != nullptr);
1892 if (vars->empty()) {
1893 return 0;
1894 }
1895 int64_t cst = 0;
1896 std::vector<Container> to_sort;
1897 for (int index = 0; index < vars->size(); ++index) {
1898 if ((*vars)[index]->Bound()) {
1899 cst = CapAdd(cst, CapProd((*coefs)[index], (*vars)[index]->Min()));
1900 } else if ((*coefs)[index] != 0) {
1901 to_sort.push_back(Container((*vars)[index], (*coefs)[index]));
1902 }
1903 }
1904 if (keep_inside && cst != 0) {
1905 CHECK_LT(to_sort.size(), vars->size());
1906 Solver* const solver = (*vars)[0]->solver();
1907 to_sort.push_back(Container(solver->MakeIntConst(1), cst));
1908 cst = 0;
1909 }
1910 std::sort(to_sort.begin(), to_sort.end());
1911 for (int index = 0; index < to_sort.size(); ++index) {
1912 (*vars)[index] = to_sort[index].var;
1913 (*coefs)[index] = to_sort[index].coef;
1914 }
1915 vars->resize(to_sort.size());
1916 coefs->resize(to_sort.size());
1917 return cst;
1918}
1919
1920// This constraint implements sum(vars) == var. It is delayed such
1921// that propagation only occurs when all variables have been touched.
1922class BooleanScalProdLessConstant : public Constraint {
1923 public:
1924 BooleanScalProdLessConstant(Solver* const s, const std::vector<IntVar*>& vars,
1925 const std::vector<int64_t>& coefs,
1926 int64_t upper_bound)
1927 : Constraint(s),
1928 vars_(vars),
1929 coefs_(coefs),
1930 upper_bound_(upper_bound),
1931 first_unbound_backward_(vars.size() - 1),
1932 sum_of_bound_variables_(0LL),
1933 max_coefficient_(0) {
1934 CHECK(!vars.empty());
1935 for (int i = 0; i < vars_.size(); ++i) {
1936 DCHECK_GE(coefs_[i], 0);
1937 }
1938 upper_bound_ =
1939 CapSub(upper_bound, SortBothChangeConstant(&vars_, &coefs_, false));
1940 max_coefficient_.SetValue(s, coefs_[vars_.size() - 1]);
1941 }
1942
1943 ~BooleanScalProdLessConstant() override {}
1944
1945 void Post() override {
1946 for (int var_index = 0; var_index < vars_.size(); ++var_index) {
1947 if (vars_[var_index]->Bound()) {
1948 continue;
1949 }
1950 Demon* d = MakeConstraintDemon1(solver(), this,
1951 &BooleanScalProdLessConstant::Update,
1952 "InitialPropagate", var_index);
1953 vars_[var_index]->WhenRange(d);
1954 }
1955 }
1956
1957 void PushFromTop() {
1958 const int64_t slack = CapSub(upper_bound_, sum_of_bound_variables_.Value());
1959 if (slack < 0) {
1960 solver()->Fail();
1961 }
1962 if (slack < max_coefficient_.Value()) {
1963 int64_t last_unbound = first_unbound_backward_.Value();
1964 for (; last_unbound >= 0; --last_unbound) {
1965 if (!vars_[last_unbound]->Bound()) {
1966 if (coefs_[last_unbound] <= slack) {
1967 max_coefficient_.SetValue(solver(), coefs_[last_unbound]);
1968 break;
1969 } else {
1970 vars_[last_unbound]->SetValue(0);
1971 }
1972 }
1973 }
1974 first_unbound_backward_.SetValue(solver(), last_unbound);
1975 }
1976 }
1977
1978 void InitialPropagate() override {
1979 Solver* const s = solver();
1980 int last_unbound = -1;
1981 int64_t sum = 0LL;
1982 for (int index = 0; index < vars_.size(); ++index) {
1983 if (vars_[index]->Bound()) {
1984 const int64_t value = vars_[index]->Min();
1985 sum = CapAdd(sum, CapProd(value, coefs_[index]));
1986 } else {
1987 last_unbound = index;
1988 }
1989 }
1990 sum_of_bound_variables_.SetValue(s, sum);
1991 first_unbound_backward_.SetValue(s, last_unbound);
1992 PushFromTop();
1993 }
1994
1995 void Update(int var_index) {
1996 if (vars_[var_index]->Min() == 1) {
1997 sum_of_bound_variables_.SetValue(
1998 solver(), CapAdd(sum_of_bound_variables_.Value(), coefs_[var_index]));
1999 PushFromTop();
2000 }
2001 }
2002
2003 std::string DebugString() const override {
2004 return absl::StrFormat("BooleanScalProd([%s], [%s]) <= %d)",
2006 absl::StrJoin(coefs_, ", "), upper_bound_);
2007 }
2008
2009 void Accept(ModelVisitor* const visitor) const override {
2010 visitor->BeginVisitConstraint(ModelVisitor::kScalProdLessOrEqual, this);
2011 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
2012 vars_);
2013 visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
2014 coefs_);
2015 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, upper_bound_);
2016 visitor->EndVisitConstraint(ModelVisitor::kScalProdLessOrEqual, this);
2017 }
2018
2019 private:
2020 std::vector<IntVar*> vars_;
2021 std::vector<int64_t> coefs_;
2022 int64_t upper_bound_;
2023 Rev<int> first_unbound_backward_;
2024 Rev<int64_t> sum_of_bound_variables_;
2025 Rev<int64_t> max_coefficient_;
2026};
2027
2028// ----- PositiveBooleanScalProdEqVar -----
2029
2030class PositiveBooleanScalProdEqVar : public CastConstraint {
2031 public:
2032 PositiveBooleanScalProdEqVar(Solver* const s,
2033 const std::vector<IntVar*>& vars,
2034 const std::vector<int64_t>& coefs,
2035 IntVar* const var)
2036 : CastConstraint(s, var),
2037 vars_(vars),
2038 coefs_(coefs),
2039 first_unbound_backward_(vars.size() - 1),
2040 sum_of_bound_variables_(0LL),
2041 sum_of_all_variables_(0LL),
2042 max_coefficient_(0) {
2043 SortBothChangeConstant(&vars_, &coefs_, true);
2044 max_coefficient_.SetValue(s, coefs_[vars_.size() - 1]);
2045 }
2046
2047 ~PositiveBooleanScalProdEqVar() override {}
2048
2049 void Post() override {
2050 for (int var_index = 0; var_index < vars_.size(); ++var_index) {
2051 if (vars_[var_index]->Bound()) {
2052 continue;
2053 }
2054 Demon* const d = MakeConstraintDemon1(
2055 solver(), this, &PositiveBooleanScalProdEqVar::Update, "Update",
2056 var_index);
2057 vars_[var_index]->WhenRange(d);
2058 }
2059 if (!target_var_->Bound()) {
2060 Demon* const uv = MakeConstraintDemon0(
2061 solver(), this, &PositiveBooleanScalProdEqVar::Propagate,
2062 "Propagate");
2063 target_var_->WhenRange(uv);
2064 }
2065 }
2066
2067 void Propagate() {
2068 target_var_->SetRange(sum_of_bound_variables_.Value(),
2069 sum_of_all_variables_.Value());
2070 const int64_t slack_up =
2071 CapSub(target_var_->Max(), sum_of_bound_variables_.Value());
2072 const int64_t slack_down =
2073 CapSub(sum_of_all_variables_.Value(), target_var_->Min());
2074 const int64_t max_coeff = max_coefficient_.Value();
2075 if (slack_down < max_coeff || slack_up < max_coeff) {
2076 int64_t last_unbound = first_unbound_backward_.Value();
2077 for (; last_unbound >= 0; --last_unbound) {
2078 if (!vars_[last_unbound]->Bound()) {
2079 if (coefs_[last_unbound] > slack_up) {
2080 vars_[last_unbound]->SetValue(0);
2081 } else if (coefs_[last_unbound] > slack_down) {
2082 vars_[last_unbound]->SetValue(1);
2083 } else {
2084 max_coefficient_.SetValue(solver(), coefs_[last_unbound]);
2085 break;
2086 }
2087 }
2088 }
2089 first_unbound_backward_.SetValue(solver(), last_unbound);
2090 }
2091 }
2092
2093 void InitialPropagate() override {
2094 Solver* const s = solver();
2095 int last_unbound = -1;
2096 int64_t sum_bound = 0;
2097 int64_t sum_all = 0;
2098 for (int index = 0; index < vars_.size(); ++index) {
2099 const int64_t value = CapProd(vars_[index]->Max(), coefs_[index]);
2100 sum_all = CapAdd(sum_all, value);
2101 if (vars_[index]->Bound()) {
2102 sum_bound = CapAdd(sum_bound, value);
2103 } else {
2104 last_unbound = index;
2105 }
2106 }
2107 sum_of_bound_variables_.SetValue(s, sum_bound);
2108 sum_of_all_variables_.SetValue(s, sum_all);
2109 first_unbound_backward_.SetValue(s, last_unbound);
2110 Propagate();
2111 }
2112
2113 void Update(int var_index) {
2114 if (vars_[var_index]->Min() == 1) {
2115 sum_of_bound_variables_.SetValue(
2116 solver(), CapAdd(sum_of_bound_variables_.Value(), coefs_[var_index]));
2117 } else {
2118 sum_of_all_variables_.SetValue(
2119 solver(), CapSub(sum_of_all_variables_.Value(), coefs_[var_index]));
2120 }
2121 Propagate();
2122 }
2123
2124 std::string DebugString() const override {
2125 return absl::StrFormat("PositiveBooleanScal([%s], [%s]) == %s",
2127 absl::StrJoin(coefs_, ", "),
2128 target_var_->DebugString());
2129 }
2130
2131 void Accept(ModelVisitor* const visitor) const override {
2132 visitor->BeginVisitConstraint(ModelVisitor::kScalProdEqual, this);
2133 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
2134 vars_);
2135 visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
2136 coefs_);
2137 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
2138 target_var_);
2139 visitor->EndVisitConstraint(ModelVisitor::kScalProdEqual, this);
2140 }
2141
2142 private:
2143 std::vector<IntVar*> vars_;
2144 std::vector<int64_t> coefs_;
2145 Rev<int> first_unbound_backward_;
2146 Rev<int64_t> sum_of_bound_variables_;
2147 Rev<int64_t> sum_of_all_variables_;
2148 Rev<int64_t> max_coefficient_;
2149};
2150
2151// ----- PositiveBooleanScalProd -----
2152
2153class PositiveBooleanScalProd : public BaseIntExpr {
2154 public:
2155 // this constructor will copy the array. The caller can safely delete the
2156 // exprs array himself
2157 PositiveBooleanScalProd(Solver* const s, const std::vector<IntVar*>& vars,
2158 const std::vector<int64_t>& coefs)
2159 : BaseIntExpr(s), vars_(vars), coefs_(coefs) {
2160 CHECK(!vars.empty());
2161 SortBothChangeConstant(&vars_, &coefs_, true);
2162 for (int i = 0; i < vars_.size(); ++i) {
2163 DCHECK_GE(coefs_[i], 0);
2164 }
2165 }
2166
2167 ~PositiveBooleanScalProd() override {}
2168
2169 int64_t Min() const override {
2170 int64_t min = 0;
2171 for (int i = 0; i < vars_.size(); ++i) {
2172 if (vars_[i]->Min()) {
2173 min = CapAdd(min, coefs_[i]);
2174 }
2175 }
2176 return min;
2177 }
2178
2179 void SetMin(int64_t m) override {
2180 SetRange(m, std::numeric_limits<int64_t>::max());
2181 }
2182
2183 int64_t Max() const override {
2184 int64_t max = 0;
2185 for (int i = 0; i < vars_.size(); ++i) {
2186 if (vars_[i]->Max()) {
2187 max = CapAdd(max, coefs_[i]);
2188 }
2189 }
2190 return max;
2191 }
2192
2193 void SetMax(int64_t m) override {
2194 SetRange(std::numeric_limits<int64_t>::min(), m);
2195 }
2196
2197 void SetRange(int64_t l, int64_t u) override {
2198 int64_t current_min = 0;
2199 int64_t current_max = 0;
2200 int64_t diameter = -1;
2201 for (int i = 0; i < vars_.size(); ++i) {
2202 const int64_t coefficient = coefs_[i];
2203 const int64_t var_min = CapProd(vars_[i]->Min(), coefficient);
2204 const int64_t var_max = CapProd(vars_[i]->Max(), coefficient);
2205 current_min = CapAdd(current_min, var_min);
2206 current_max = CapAdd(current_max, var_max);
2207 if (var_min != var_max) { // Coefficients are increasing.
2208 diameter = CapSub(var_max, var_min);
2209 }
2210 }
2211 if (u >= current_max && l <= current_min) {
2212 return;
2213 }
2214 if (u < current_min || l > current_max) {
2215 solver()->Fail();
2216 }
2217
2218 u = std::min(current_max, u);
2219 l = std::max(l, current_min);
2220
2221 if (CapSub(u, l) > diameter) {
2222 return;
2223 }
2224
2225 for (int i = 0; i < vars_.size(); ++i) {
2226 const int64_t coefficient = coefs_[i];
2227 IntVar* const var = vars_[i];
2228 const int64_t new_min =
2229 CapAdd(CapSub(l, current_max), CapProd(var->Max(), coefficient));
2230 const int64_t new_max =
2231 CapAdd(CapSub(u, current_min), CapProd(var->Min(), coefficient));
2232 if (new_max < 0 || new_min > coefficient || new_min > new_max) {
2233 solver()->Fail();
2234 }
2235 if (new_min > 0LL) {
2236 var->SetMin(int64_t{1});
2237 } else if (new_max < coefficient) {
2238 var->SetMax(int64_t{0});
2239 }
2240 }
2241 }
2242
2243 std::string DebugString() const override {
2244 return absl::StrFormat("PositiveBooleanScalProd([%s], [%s])",
2246 absl::StrJoin(coefs_, ", "));
2247 }
2248
2249 void WhenRange(Demon* d) override {
2250 for (int i = 0; i < vars_.size(); ++i) {
2251 vars_[i]->WhenRange(d);
2252 }
2253 }
2254 IntVar* CastToVar() override {
2255 Solver* const s = solver();
2256 int64_t vmin = 0LL;
2257 int64_t vmax = 0LL;
2258 Range(&vmin, &vmax);
2259 IntVar* const var = solver()->MakeIntVar(vmin, vmax);
2260 if (!vars_.empty()) {
2261 CastConstraint* const ct =
2262 s->RevAlloc(new PositiveBooleanScalProdEqVar(s, vars_, coefs_, var));
2263 s->AddCastConstraint(ct, var, this);
2264 }
2265 return var;
2266 }
2267
2268 void Accept(ModelVisitor* const visitor) const override {
2269 visitor->BeginVisitIntegerExpression(ModelVisitor::kScalProd, this);
2270 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
2271 vars_);
2272 visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
2273 coefs_);
2274 visitor->EndVisitIntegerExpression(ModelVisitor::kScalProd, this);
2275 }
2276
2277 private:
2278 std::vector<IntVar*> vars_;
2279 std::vector<int64_t> coefs_;
2280};
2281
2282// ----- PositiveBooleanScalProdEqCst ----- (all constants >= 0)
2283
2284class PositiveBooleanScalProdEqCst : public Constraint {
2285 public:
2286 PositiveBooleanScalProdEqCst(Solver* const s,
2287 const std::vector<IntVar*>& vars,
2288 const std::vector<int64_t>& coefs,
2289 int64_t constant)
2290 : Constraint(s),
2291 vars_(vars),
2292 coefs_(coefs),
2293 first_unbound_backward_(vars.size() - 1),
2294 sum_of_bound_variables_(0LL),
2295 sum_of_all_variables_(0LL),
2296 constant_(constant),
2297 max_coefficient_(0) {
2298 CHECK(!vars.empty());
2299 constant_ =
2300 CapSub(constant_, SortBothChangeConstant(&vars_, &coefs_, false));
2301 max_coefficient_.SetValue(s, coefs_[vars_.size() - 1]);
2302 }
2303
2304 ~PositiveBooleanScalProdEqCst() override {}
2305
2306 void Post() override {
2307 for (int var_index = 0; var_index < vars_.size(); ++var_index) {
2308 if (!vars_[var_index]->Bound()) {
2309 Demon* const d = MakeConstraintDemon1(
2310 solver(), this, &PositiveBooleanScalProdEqCst::Update, "Update",
2311 var_index);
2312 vars_[var_index]->WhenRange(d);
2313 }
2314 }
2315 }
2316
2317 void Propagate() {
2318 if (sum_of_bound_variables_.Value() > constant_ ||
2319 sum_of_all_variables_.Value() < constant_) {
2320 solver()->Fail();
2321 }
2322 const int64_t slack_up = CapSub(constant_, sum_of_bound_variables_.Value());
2323 const int64_t slack_down = CapSub(sum_of_all_variables_.Value(), constant_);
2324 const int64_t max_coeff = max_coefficient_.Value();
2325 if (slack_down < max_coeff || slack_up < max_coeff) {
2326 int64_t last_unbound = first_unbound_backward_.Value();
2327 for (; last_unbound >= 0; --last_unbound) {
2328 if (!vars_[last_unbound]->Bound()) {
2329 if (coefs_[last_unbound] > slack_up) {
2330 vars_[last_unbound]->SetValue(0);
2331 } else if (coefs_[last_unbound] > slack_down) {
2332 vars_[last_unbound]->SetValue(1);
2333 } else {
2334 max_coefficient_.SetValue(solver(), coefs_[last_unbound]);
2335 break;
2336 }
2337 }
2338 }
2339 first_unbound_backward_.SetValue(solver(), last_unbound);
2340 }
2341 }
2342
2343 void InitialPropagate() override {
2344 Solver* const s = solver();
2345 int last_unbound = -1;
2346 int64_t sum_bound = 0LL;
2347 int64_t sum_all = 0LL;
2348 for (int index = 0; index < vars_.size(); ++index) {
2349 const int64_t value = CapProd(vars_[index]->Max(), coefs_[index]);
2350 sum_all = CapAdd(sum_all, value);
2351 if (vars_[index]->Bound()) {
2352 sum_bound = CapAdd(sum_bound, value);
2353 } else {
2354 last_unbound = index;
2355 }
2356 }
2357 sum_of_bound_variables_.SetValue(s, sum_bound);
2358 sum_of_all_variables_.SetValue(s, sum_all);
2359 first_unbound_backward_.SetValue(s, last_unbound);
2360 Propagate();
2361 }
2362
2363 void Update(int var_index) {
2364 if (vars_[var_index]->Min() == 1) {
2365 sum_of_bound_variables_.SetValue(
2366 solver(), CapAdd(sum_of_bound_variables_.Value(), coefs_[var_index]));
2367 } else {
2368 sum_of_all_variables_.SetValue(
2369 solver(), CapSub(sum_of_all_variables_.Value(), coefs_[var_index]));
2370 }
2371 Propagate();
2372 }
2373
2374 std::string DebugString() const override {
2375 return absl::StrFormat("PositiveBooleanScalProd([%s], [%s]) == %d",
2377 absl::StrJoin(coefs_, ", "), constant_);
2378 }
2379
2380 void Accept(ModelVisitor* const visitor) const override {
2381 visitor->BeginVisitConstraint(ModelVisitor::kScalProdEqual, this);
2382 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
2383 vars_);
2384 visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
2385 coefs_);
2386 visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, constant_);
2387 visitor->EndVisitConstraint(ModelVisitor::kScalProdEqual, this);
2388 }
2389
2390 private:
2391 std::vector<IntVar*> vars_;
2392 std::vector<int64_t> coefs_;
2393 Rev<int> first_unbound_backward_;
2394 Rev<int64_t> sum_of_bound_variables_;
2395 Rev<int64_t> sum_of_all_variables_;
2396 int64_t constant_;
2397 Rev<int64_t> max_coefficient_;
2398};
2399
2400// ----- Linearizer -----
2401
2402#define IS_TYPE(type, tag) type.compare(ModelVisitor::tag) == 0
2403
2404class ExprLinearizer : public ModelParser {
2405 public:
2406 explicit ExprLinearizer(
2407 absl::flat_hash_map<IntVar*, int64_t>* const variables_to_coefficients)
2408 : variables_to_coefficients_(variables_to_coefficients), constant_(0) {}
2409
2410 ~ExprLinearizer() override {}
2411
2412 // Begin/End visit element.
2413 void BeginVisitModel(const std::string& solver_name) override {
2414 LOG(FATAL) << "Should not be here";
2415 }
2416
2417 void EndVisitModel(const std::string& solver_name) override {
2418 LOG(FATAL) << "Should not be here";
2419 }
2420
2421 void BeginVisitConstraint(const std::string& type_name,
2422 const Constraint* const constraint) override {
2423 LOG(FATAL) << "Should not be here";
2424 }
2425
2426 void EndVisitConstraint(const std::string& type_name,
2427 const Constraint* const constraint) override {
2428 LOG(FATAL) << "Should not be here";
2429 }
2430
2431 void BeginVisitExtension(const std::string& type) override {}
2432
2433 void EndVisitExtension(const std::string& type) override {}
2434 void BeginVisitIntegerExpression(const std::string& type_name,
2435 const IntExpr* const expr) override {
2436 BeginVisit(true);
2437 }
2438
2439 void EndVisitIntegerExpression(const std::string& type_name,
2440 const IntExpr* const expr) override {
2441 if (IS_TYPE(type_name, kSum)) {
2442 VisitSum(expr);
2443 } else if (IS_TYPE(type_name, kScalProd)) {
2444 VisitScalProd(expr);
2445 } else if (IS_TYPE(type_name, kDifference)) {
2446 VisitDifference(expr);
2447 } else if (IS_TYPE(type_name, kOpposite)) {
2448 VisitOpposite(expr);
2449 } else if (IS_TYPE(type_name, kProduct)) {
2450 VisitProduct(expr);
2451 } else if (IS_TYPE(type_name, kTrace)) {
2452 VisitTrace(expr);
2453 } else {
2454 VisitIntegerExpression(expr);
2455 }
2456 EndVisit();
2457 }
2458
2459 void VisitIntegerVariable(const IntVar* const variable,
2460 const std::string& operation, int64_t value,
2461 IntVar* const delegate) override {
2462 if (operation == ModelVisitor::kSumOperation) {
2463 AddConstant(value);
2464 VisitSubExpression(delegate);
2465 } else if (operation == ModelVisitor::kDifferenceOperation) {
2466 AddConstant(value);
2467 PushMultiplier(-1);
2468 VisitSubExpression(delegate);
2469 PopMultiplier();
2470 } else if (operation == ModelVisitor::kProductOperation) {
2471 PushMultiplier(value);
2472 VisitSubExpression(delegate);
2473 PopMultiplier();
2474 } else if (operation == ModelVisitor::kTraceOperation) {
2475 VisitSubExpression(delegate);
2476 }
2477 }
2478
2479 void VisitIntegerVariable(const IntVar* const variable,
2480 IntExpr* const delegate) override {
2481 if (delegate != nullptr) {
2482 VisitSubExpression(delegate);
2483 } else {
2484 if (variable->Bound()) {
2485 AddConstant(variable->Min());
2486 } else {
2487 RegisterExpression(variable, 1);
2488 }
2489 }
2490 }
2491
2492 // Visit integer arguments.
2493 void VisitIntegerArgument(const std::string& arg_name,
2494 int64_t value) override {
2495 Top()->SetIntegerArgument(arg_name, value);
2496 }
2497
2498 void VisitIntegerArrayArgument(const std::string& arg_name,
2499 const std::vector<int64_t>& values) override {
2500 Top()->SetIntegerArrayArgument(arg_name, values);
2501 }
2502
2503 void VisitIntegerMatrixArgument(const std::string& arg_name,
2504 const IntTupleSet& values) override {
2505 Top()->SetIntegerMatrixArgument(arg_name, values);
2506 }
2507
2508 // Visit integer expression argument.
2509 void VisitIntegerExpressionArgument(const std::string& arg_name,
2510 IntExpr* const argument) override {
2511 Top()->SetIntegerExpressionArgument(arg_name, argument);
2512 }
2513
2514 void VisitIntegerVariableArrayArgument(
2515 const std::string& arg_name,
2516 const std::vector<IntVar*>& arguments) override {
2517 Top()->SetIntegerVariableArrayArgument(arg_name, arguments);
2518 }
2519
2520 // Visit interval argument.
2521 void VisitIntervalArgument(const std::string& arg_name,
2522 IntervalVar* const argument) override {}
2523
2524 void VisitIntervalArrayArgument(
2525 const std::string& arg_name,
2526 const std::vector<IntervalVar*>& argument) override {}
2527
2528 void Visit(const IntExpr* const expr, int64_t multiplier) {
2529 if (expr->Min() == expr->Max()) {
2530 constant_ = CapAdd(constant_, CapProd(expr->Min(), multiplier));
2531 } else {
2532 PushMultiplier(multiplier);
2533 expr->Accept(this);
2534 PopMultiplier();
2535 }
2536 }
2537
2538 int64_t Constant() const { return constant_; }
2539
2540 std::string DebugString() const override { return "ExprLinearizer"; }
2541
2542 private:
2543 void BeginVisit(bool active) { PushArgumentHolder(); }
2544
2545 void EndVisit() { PopArgumentHolder(); }
2546
2547 void VisitSubExpression(const IntExpr* const cp_expr) {
2548 cp_expr->Accept(this);
2549 }
2550
2551 void VisitSum(const IntExpr* const cp_expr) {
2552 if (Top()->HasIntegerVariableArrayArgument(ModelVisitor::kVarsArgument)) {
2553 const std::vector<IntVar*>& cp_vars =
2554 Top()->FindIntegerVariableArrayArgumentOrDie(
2555 ModelVisitor::kVarsArgument);
2556 for (int i = 0; i < cp_vars.size(); ++i) {
2557 VisitSubExpression(cp_vars[i]);
2558 }
2559 } else if (Top()->HasIntegerExpressionArgument(
2560 ModelVisitor::kLeftArgument)) {
2561 const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie(
2562 ModelVisitor::kLeftArgument);
2563 const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie(
2564 ModelVisitor::kRightArgument);
2565 VisitSubExpression(left);
2566 VisitSubExpression(right);
2567 } else {
2568 const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
2569 ModelVisitor::kExpressionArgument);
2570 const int64_t value =
2571 Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
2572 VisitSubExpression(expr);
2573 AddConstant(value);
2574 }
2575 }
2576
2577 void VisitScalProd(const IntExpr* const cp_expr) {
2578 const std::vector<IntVar*>& cp_vars =
2579 Top()->FindIntegerVariableArrayArgumentOrDie(
2580 ModelVisitor::kVarsArgument);
2581 const std::vector<int64_t>& cp_coefficients =
2582 Top()->FindIntegerArrayArgumentOrDie(
2583 ModelVisitor::kCoefficientsArgument);
2584 CHECK_EQ(cp_vars.size(), cp_coefficients.size());
2585 for (int i = 0; i < cp_vars.size(); ++i) {
2586 const int64_t coefficient = cp_coefficients[i];
2587 PushMultiplier(coefficient);
2588 VisitSubExpression(cp_vars[i]);
2589 PopMultiplier();
2590 }
2591 }
2592
2593 void VisitDifference(const IntExpr* const cp_expr) {
2594 if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) {
2595 const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie(
2596 ModelVisitor::kLeftArgument);
2597 const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie(
2598 ModelVisitor::kRightArgument);
2599 VisitSubExpression(left);
2600 PushMultiplier(-1);
2601 VisitSubExpression(right);
2602 PopMultiplier();
2603 } else {
2604 const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
2605 ModelVisitor::kExpressionArgument);
2606 const int64_t value =
2607 Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
2608 AddConstant(value);
2609 PushMultiplier(-1);
2610 VisitSubExpression(expr);
2611 PopMultiplier();
2612 }
2613 }
2614
2615 void VisitOpposite(const IntExpr* const cp_expr) {
2616 const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
2617 ModelVisitor::kExpressionArgument);
2618 PushMultiplier(-1);
2619 VisitSubExpression(expr);
2620 PopMultiplier();
2621 }
2622
2623 void VisitProduct(const IntExpr* const cp_expr) {
2624 if (Top()->HasIntegerExpressionArgument(
2625 ModelVisitor::kExpressionArgument)) {
2626 const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
2627 ModelVisitor::kExpressionArgument);
2628 const int64_t value =
2629 Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
2630 PushMultiplier(value);
2631 VisitSubExpression(expr);
2632 PopMultiplier();
2633 } else {
2634 RegisterExpression(cp_expr, 1);
2635 }
2636 }
2637
2638 void VisitTrace(const IntExpr* const cp_expr) {
2639 const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
2640 ModelVisitor::kExpressionArgument);
2641 VisitSubExpression(expr);
2642 }
2643
2644 void VisitIntegerExpression(const IntExpr* const cp_expr) {
2645 RegisterExpression(cp_expr, 1);
2646 }
2647
2648 void RegisterExpression(const IntExpr* const expr, int64_t coef) {
2649 int64_t& value =
2650 (*variables_to_coefficients_)[const_cast<IntExpr*>(expr)->Var()];
2651 value = CapAdd(value, CapProd(coef, multipliers_.back()));
2652 }
2653
2654 void AddConstant(int64_t constant) {
2655 constant_ = CapAdd(constant_, CapProd(constant, multipliers_.back()));
2656 }
2657
2658 void PushMultiplier(int64_t multiplier) {
2659 if (multipliers_.empty()) {
2660 multipliers_.push_back(multiplier);
2661 } else {
2662 multipliers_.push_back(CapProd(multiplier, multipliers_.back()));
2663 }
2664 }
2665
2666 void PopMultiplier() { multipliers_.pop_back(); }
2667
2668 // We do need a IntVar* as key, and not const IntVar*, because clients of this
2669 // class typically iterate over the map keys and use them as mutable IntVar*.
2670 absl::flat_hash_map<IntVar*, int64_t>* const variables_to_coefficients_;
2671 std::vector<int64_t> multipliers_;
2672 int64_t constant_;
2673};
2674#undef IS_TYPE
2675
2676// ----- Factory functions -----
2677
2678void DeepLinearize(Solver* const solver, const std::vector<IntVar*>& pre_vars,
2679 const std::vector<int64_t>& pre_coefs,
2680 std::vector<IntVar*>* vars, std::vector<int64_t>* coefs,
2681 int64_t* constant) {
2682 CHECK(solver != nullptr);
2683 CHECK(vars != nullptr);
2684 CHECK(coefs != nullptr);
2685 CHECK(constant != nullptr);
2686 *constant = 0;
2687 vars->reserve(pre_vars.size());
2688 coefs->reserve(pre_coefs.size());
2689 // Try linear scan of the variables to check if there is nothing to do.
2690 bool need_linearization = false;
2691 for (int i = 0; i < pre_vars.size(); ++i) {
2692 IntVar* const variable = pre_vars[i];
2693 const int64_t coefficient = pre_coefs[i];
2694 if (variable->Bound()) {
2695 *constant = CapAdd(*constant, CapProd(coefficient, variable->Min()));
2696 } else if (solver->CastExpression(variable) == nullptr) {
2697 vars->push_back(variable);
2698 coefs->push_back(coefficient);
2699 } else {
2700 need_linearization = true;
2701 vars->clear();
2702 coefs->clear();
2703 break;
2704 }
2705 }
2706 if (need_linearization) {
2707 // Instrospect the variables to simplify the sum.
2708 absl::flat_hash_map<IntVar*, int64_t> variables_to_coefficients;
2709 ExprLinearizer linearizer(&variables_to_coefficients);
2710 for (int i = 0; i < pre_vars.size(); ++i) {
2711 linearizer.Visit(pre_vars[i], pre_coefs[i]);
2712 }
2713 *constant = linearizer.Constant();
2714 for (const auto& variable_to_coefficient : variables_to_coefficients) {
2715 if (variable_to_coefficient.second != 0) {
2716 vars->push_back(variable_to_coefficient.first);
2717 coefs->push_back(variable_to_coefficient.second);
2718 }
2719 }
2720 }
2721}
2722
2723Constraint* MakeScalProdEqualityFct(Solver* const solver,
2724 const std::vector<IntVar*>& pre_vars,
2725 const std::vector<int64_t>& pre_coefs,
2726 int64_t cst) {
2727 int64_t constant = 0;
2728 std::vector<IntVar*> vars;
2729 std::vector<int64_t> coefs;
2730 DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
2731 cst = CapSub(cst, constant);
2732
2733 const int size = vars.size();
2734 if (size == 0 || AreAllNull(coefs)) {
2735 return cst == 0 ? solver->MakeTrueConstraint()
2736 : solver->MakeFalseConstraint();
2737 }
2738 if (AreAllBoundOrNull(vars, coefs)) {
2739 int64_t sum = 0;
2740 for (int i = 0; i < size; ++i) {
2741 sum = CapAdd(sum, CapProd(coefs[i], vars[i]->Min()));
2742 }
2743 return sum == cst ? solver->MakeTrueConstraint()
2744 : solver->MakeFalseConstraint();
2745 }
2746 if (AreAllOnes(coefs)) {
2747 return solver->MakeSumEquality(vars, cst);
2748 }
2749 if (AreAllBooleans(vars) && size > 2) {
2750 if (AreAllPositive(coefs)) {
2751 return solver->RevAlloc(
2752 new PositiveBooleanScalProdEqCst(solver, vars, coefs, cst));
2753 }
2754 if (AreAllNegative(coefs)) {
2755 std::vector<int64_t> opp_coefs(coefs.size());
2756 for (int i = 0; i < coefs.size(); ++i) {
2757 opp_coefs[i] = -coefs[i];
2758 }
2759 return solver->RevAlloc(
2760 new PositiveBooleanScalProdEqCst(solver, vars, opp_coefs, -cst));
2761 }
2762 }
2763
2764 // Simplications.
2765 int constants = 0;
2766 int positives = 0;
2767 int negatives = 0;
2768 for (int i = 0; i < size; ++i) {
2769 if (coefs[i] == 0 || vars[i]->Bound()) {
2770 constants++;
2771 } else if (coefs[i] > 0) {
2772 positives++;
2773 } else {
2774 negatives++;
2775 }
2776 }
2777 if (positives > 0 && negatives > 0) {
2778 std::vector<IntVar*> pos_terms;
2779 std::vector<IntVar*> neg_terms;
2780 int64_t rhs = cst;
2781 for (int i = 0; i < size; ++i) {
2782 if (coefs[i] == 0 || vars[i]->Bound()) {
2783 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
2784 } else if (coefs[i] > 0) {
2785 pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
2786 } else {
2787 neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
2788 }
2789 }
2790 if (negatives == 1) {
2791 if (rhs != 0) {
2792 pos_terms.push_back(solver->MakeIntConst(-rhs));
2793 }
2794 return solver->MakeSumEquality(pos_terms, neg_terms[0]);
2795 } else if (positives == 1) {
2796 if (rhs != 0) {
2797 neg_terms.push_back(solver->MakeIntConst(rhs));
2798 }
2799 return solver->MakeSumEquality(neg_terms, pos_terms[0]);
2800 } else {
2801 if (rhs != 0) {
2802 neg_terms.push_back(solver->MakeIntConst(rhs));
2803 }
2804 return solver->MakeEquality(solver->MakeSum(pos_terms),
2805 solver->MakeSum(neg_terms));
2806 }
2807 } else if (positives == 1) {
2808 IntExpr* pos_term = nullptr;
2809 int64_t rhs = cst;
2810 for (int i = 0; i < size; ++i) {
2811 if (coefs[i] == 0 || vars[i]->Bound()) {
2812 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
2813 } else if (coefs[i] > 0) {
2814 pos_term = solver->MakeProd(vars[i], coefs[i]);
2815 } else {
2816 LOG(FATAL) << "Should not be here";
2817 }
2818 }
2819 return solver->MakeEquality(pos_term, rhs);
2820 } else if (negatives == 1) {
2821 IntExpr* neg_term = nullptr;
2822 int64_t rhs = cst;
2823 for (int i = 0; i < size; ++i) {
2824 if (coefs[i] == 0 || vars[i]->Bound()) {
2825 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
2826 } else if (coefs[i] > 0) {
2827 LOG(FATAL) << "Should not be here";
2828 } else {
2829 neg_term = solver->MakeProd(vars[i], -coefs[i]);
2830 }
2831 }
2832 return solver->MakeEquality(neg_term, -rhs);
2833 } else if (positives > 1) {
2834 std::vector<IntVar*> pos_terms;
2835 int64_t rhs = cst;
2836 for (int i = 0; i < size; ++i) {
2837 if (coefs[i] == 0 || vars[i]->Bound()) {
2838 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
2839 } else if (coefs[i] > 0) {
2840 pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
2841 } else {
2842 LOG(FATAL) << "Should not be here";
2843 }
2844 }
2845 return solver->MakeSumEquality(pos_terms, rhs);
2846 } else if (negatives > 1) {
2847 std::vector<IntVar*> neg_terms;
2848 int64_t rhs = cst;
2849 for (int i = 0; i < size; ++i) {
2850 if (coefs[i] == 0 || vars[i]->Bound()) {
2851 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
2852 } else if (coefs[i] > 0) {
2853 LOG(FATAL) << "Should not be here";
2854 } else {
2855 neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
2856 }
2857 }
2858 return solver->MakeSumEquality(neg_terms, -rhs);
2859 }
2860 std::vector<IntVar*> terms;
2861 for (int i = 0; i < size; ++i) {
2862 terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
2863 }
2864 return solver->MakeSumEquality(terms, solver->MakeIntConst(cst));
2865}
2866
2867Constraint* MakeScalProdEqualityVarFct(Solver* const solver,
2868 const std::vector<IntVar*>& pre_vars,
2869 const std::vector<int64_t>& pre_coefs,
2870 IntVar* const target) {
2871 int64_t constant = 0;
2872 std::vector<IntVar*> vars;
2873 std::vector<int64_t> coefs;
2874 DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
2875
2876 const int size = vars.size();
2877 if (size == 0 || AreAllNull<int64_t>(coefs)) {
2878 return solver->MakeEquality(target, constant);
2879 }
2880 if (AreAllOnes(coefs)) {
2881 return solver->MakeSumEquality(vars,
2882 solver->MakeSum(target, -constant)->Var());
2883 }
2884 if (AreAllBooleans(vars) && AreAllPositive<int64_t>(coefs)) {
2885 // TODO(user) : bench BooleanScalProdEqVar with IntConst.
2886 return solver->RevAlloc(new PositiveBooleanScalProdEqVar(
2887 solver, vars, coefs, solver->MakeSum(target, -constant)->Var()));
2888 }
2889 std::vector<IntVar*> terms;
2890 for (int i = 0; i < size; ++i) {
2891 terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
2892 }
2893 return solver->MakeSumEquality(terms,
2894 solver->MakeSum(target, -constant)->Var());
2895}
2896
2897Constraint* MakeScalProdGreaterOrEqualFct(Solver* solver,
2898 const std::vector<IntVar*>& pre_vars,
2899 const std::vector<int64_t>& pre_coefs,
2900 int64_t cst) {
2901 int64_t constant = 0;
2902 std::vector<IntVar*> vars;
2903 std::vector<int64_t> coefs;
2904 DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
2905 cst = CapSub(cst, constant);
2906
2907 const int size = vars.size();
2908 if (size == 0 || AreAllNull<int64_t>(coefs)) {
2909 return cst <= 0 ? solver->MakeTrueConstraint()
2910 : solver->MakeFalseConstraint();
2911 }
2912 if (AreAllOnes(coefs)) {
2913 return solver->MakeSumGreaterOrEqual(vars, cst);
2914 }
2915 if (cst == 1 && AreAllBooleans(vars) && AreAllPositive(coefs)) {
2916 // can move all coefs to 1.
2917 std::vector<IntVar*> terms;
2918 for (int i = 0; i < size; ++i) {
2919 if (coefs[i] > 0) {
2920 terms.push_back(vars[i]);
2921 }
2922 }
2923 return solver->MakeSumGreaterOrEqual(terms, 1);
2924 }
2925 std::vector<IntVar*> terms;
2926 for (int i = 0; i < size; ++i) {
2927 terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
2928 }
2929 return solver->MakeSumGreaterOrEqual(terms, cst);
2930}
2931
2932Constraint* MakeScalProdLessOrEqualFct(Solver* solver,
2933 const std::vector<IntVar*>& pre_vars,
2934 const std::vector<int64_t>& pre_coefs,
2935 int64_t upper_bound) {
2936 int64_t constant = 0;
2937 std::vector<IntVar*> vars;
2938 std::vector<int64_t> coefs;
2939 DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
2940 upper_bound = CapSub(upper_bound, constant);
2941
2942 const int size = vars.size();
2943 if (size == 0 || AreAllNull<int64_t>(coefs)) {
2944 return upper_bound >= 0 ? solver->MakeTrueConstraint()
2945 : solver->MakeFalseConstraint();
2946 }
2947 // TODO(user) : compute constant on the fly.
2948 if (AreAllBoundOrNull(vars, coefs)) {
2949 int64_t cst = 0;
2950 for (int i = 0; i < size; ++i) {
2951 cst = CapAdd(cst, CapProd(vars[i]->Min(), coefs[i]));
2952 }
2953 return cst <= upper_bound ? solver->MakeTrueConstraint()
2954 : solver->MakeFalseConstraint();
2955 }
2956 if (AreAllOnes(coefs)) {
2957 return solver->MakeSumLessOrEqual(vars, upper_bound);
2958 }
2959 if (AreAllBooleans(vars) && AreAllPositive<int64_t>(coefs)) {
2960 return solver->RevAlloc(
2961 new BooleanScalProdLessConstant(solver, vars, coefs, upper_bound));
2962 }
2963 // Some simplications
2964 int constants = 0;
2965 int positives = 0;
2966 int negatives = 0;
2967 for (int i = 0; i < size; ++i) {
2968 if (coefs[i] == 0 || vars[i]->Bound()) {
2969 constants++;
2970 } else if (coefs[i] > 0) {
2971 positives++;
2972 } else {
2973 negatives++;
2974 }
2975 }
2976 if (positives > 0 && negatives > 0) {
2977 std::vector<IntVar*> pos_terms;
2978 std::vector<IntVar*> neg_terms;
2979 int64_t rhs = upper_bound;
2980 for (int i = 0; i < size; ++i) {
2981 if (coefs[i] == 0 || vars[i]->Bound()) {
2982 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
2983 } else if (coefs[i] > 0) {
2984 pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
2985 } else {
2986 neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
2987 }
2988 }
2989 if (negatives == 1) {
2990 IntExpr* const neg_term = solver->MakeSum(neg_terms[0], rhs);
2991 return solver->MakeLessOrEqual(solver->MakeSum(pos_terms), neg_term);
2992 } else if (positives == 1) {
2993 IntExpr* const pos_term = solver->MakeSum(pos_terms[0], -rhs);
2994 return solver->MakeGreaterOrEqual(solver->MakeSum(neg_terms), pos_term);
2995 } else {
2996 if (rhs != 0) {
2997 neg_terms.push_back(solver->MakeIntConst(rhs));
2998 }
2999 return solver->MakeLessOrEqual(solver->MakeSum(pos_terms),
3000 solver->MakeSum(neg_terms));
3001 }
3002 } else if (positives == 1) {
3003 IntExpr* pos_term = nullptr;
3004 int64_t rhs = upper_bound;
3005 for (int i = 0; i < size; ++i) {
3006 if (coefs[i] == 0 || vars[i]->Bound()) {
3007 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
3008 } else if (coefs[i] > 0) {
3009 pos_term = solver->MakeProd(vars[i], coefs[i]);
3010 } else {
3011 LOG(FATAL) << "Should not be here";
3012 }
3013 }
3014 return solver->MakeLessOrEqual(pos_term, rhs);
3015 } else if (negatives == 1) {
3016 IntExpr* neg_term = nullptr;
3017 int64_t rhs = upper_bound;
3018 for (int i = 0; i < size; ++i) {
3019 if (coefs[i] == 0 || vars[i]->Bound()) {
3020 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
3021 } else if (coefs[i] > 0) {
3022 LOG(FATAL) << "Should not be here";
3023 } else {
3024 neg_term = solver->MakeProd(vars[i], -coefs[i]);
3025 }
3026 }
3027 return solver->MakeGreaterOrEqual(neg_term, -rhs);
3028 } else if (positives > 1) {
3029 std::vector<IntVar*> pos_terms;
3030 int64_t rhs = upper_bound;
3031 for (int i = 0; i < size; ++i) {
3032 if (coefs[i] == 0 || vars[i]->Bound()) {
3033 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
3034 } else if (coefs[i] > 0) {
3035 pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
3036 } else {
3037 LOG(FATAL) << "Should not be here";
3038 }
3039 }
3040 return solver->MakeSumLessOrEqual(pos_terms, rhs);
3041 } else if (negatives > 1) {
3042 std::vector<IntVar*> neg_terms;
3043 int64_t rhs = upper_bound;
3044 for (int i = 0; i < size; ++i) {
3045 if (coefs[i] == 0 || vars[i]->Bound()) {
3046 rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
3047 } else if (coefs[i] > 0) {
3048 LOG(FATAL) << "Should not be here";
3049 } else {
3050 neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
3051 }
3052 }
3053 return solver->MakeSumGreaterOrEqual(neg_terms, -rhs);
3054 }
3055 std::vector<IntVar*> terms;
3056 for (int i = 0; i < size; ++i) {
3057 terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
3058 }
3059 return solver->MakeLessOrEqual(solver->MakeSum(terms), upper_bound);
3060}
3061
3062IntExpr* MakeSumArrayAux(Solver* const solver, const std::vector<IntVar*>& vars,
3063 int64_t constant) {
3064 const int size = vars.size();
3065 DCHECK_GT(size, 2);
3066 int64_t new_min = 0;
3067 int64_t new_max = 0;
3068 for (int i = 0; i < size; ++i) {
3069 if (new_min != std::numeric_limits<int64_t>::min()) {
3070 new_min = CapAdd(vars[i]->Min(), new_min);
3071 }
3072 if (new_max != std::numeric_limits<int64_t>::max()) {
3073 new_max = CapAdd(vars[i]->Max(), new_max);
3074 }
3075 }
3076 IntExpr* const cache =
3077 solver->Cache()->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_SUM);
3078 if (cache != nullptr) {
3079 return solver->MakeSum(cache, constant);
3080 } else {
3081 const std::string name =
3082 absl::StrFormat("Sum([%s])", JoinNamePtr(vars, ", "));
3083 IntVar* const sum_var = solver->MakeIntVar(new_min, new_max, name);
3084 if (AreAllBooleans(vars)) {
3085 solver->AddConstraint(
3086 solver->RevAlloc(new SumBooleanEqualToVar(solver, vars, sum_var)));
3087 } else if (size <= solver->parameters().array_split_size()) {
3088 solver->AddConstraint(
3089 solver->RevAlloc(new SmallSumConstraint(solver, vars, sum_var)));
3090 } else {
3091 solver->AddConstraint(
3092 solver->RevAlloc(new SumConstraint(solver, vars, sum_var)));
3093 }
3094 solver->Cache()->InsertVarArrayExpression(sum_var, vars,
3095 ModelCache::VAR_ARRAY_SUM);
3096 return solver->MakeSum(sum_var, constant);
3097 }
3098}
3099
3100IntExpr* MakeSumAux(Solver* const solver, const std::vector<IntVar*>& vars,
3101 int64_t constant) {
3102 const int size = vars.size();
3103 if (size == 0) {
3104 return solver->MakeIntConst(constant);
3105 } else if (size == 1) {
3106 return solver->MakeSum(vars[0], constant);
3107 } else if (size == 2) {
3108 return solver->MakeSum(solver->MakeSum(vars[0], vars[1]), constant);
3109 } else {
3110 return MakeSumArrayAux(solver, vars, constant);
3111 }
3112}
3113
3114IntExpr* MakeScalProdAux(Solver* solver, const std::vector<IntVar*>& vars,
3115 const std::vector<int64_t>& coefs, int64_t constant) {
3116 if (AreAllOnes(coefs)) {
3117 return MakeSumAux(solver, vars, constant);
3118 }
3119
3120 const int size = vars.size();
3121 if (size == 0) {
3122 return solver->MakeIntConst(constant);
3123 } else if (size == 1) {
3124 return solver->MakeSum(solver->MakeProd(vars[0], coefs[0]), constant);
3125 } else if (size == 2) {
3126 if (coefs[0] > 0 && coefs[1] < 0) {
3127 return solver->MakeSum(
3128 solver->MakeDifference(solver->MakeProd(vars[0], coefs[0]),
3129 solver->MakeProd(vars[1], -coefs[1])),
3130 constant);
3131 } else if (coefs[0] < 0 && coefs[1] > 0) {
3132 return solver->MakeSum(
3133 solver->MakeDifference(solver->MakeProd(vars[1], coefs[1]),
3134 solver->MakeProd(vars[0], -coefs[0])),
3135 constant);
3136 } else {
3137 return solver->MakeSum(
3138 solver->MakeSum(solver->MakeProd(vars[0], coefs[0]),
3139 solver->MakeProd(vars[1], coefs[1])),
3140 constant);
3141 }
3142 } else {
3143 if (AreAllBooleans(vars)) {
3144 if (AreAllPositive(coefs)) {
3145 if (vars.size() > 8) {
3146 return solver->MakeSum(
3147 solver
3148 ->RegisterIntExpr(solver->RevAlloc(
3149 new PositiveBooleanScalProd(solver, vars, coefs)))
3150 ->Var(),
3151 constant);
3152 } else {
3153 return solver->MakeSum(
3154 solver->RegisterIntExpr(solver->RevAlloc(
3155 new PositiveBooleanScalProd(solver, vars, coefs))),
3156 constant);
3157 }
3158 } else {
3159 // If some coefficients are non-positive, partition coefficients in two
3160 // sets, one for the positive coefficients P and one for the negative
3161 // ones N.
3162 // Create two PositiveBooleanScalProd expressions, one on P (s1), the
3163 // other on Opposite(N) (s2).
3164 // The final expression is then s1 - s2.
3165 // If P is empty, the expression is Opposite(s2).
3166 std::vector<int64_t> positive_coefs;
3167 std::vector<int64_t> negative_coefs;
3168 std::vector<IntVar*> positive_coef_vars;
3169 std::vector<IntVar*> negative_coef_vars;
3170 for (int i = 0; i < size; ++i) {
3171 const int coef = coefs[i];
3172 if (coef > 0) {
3173 positive_coefs.push_back(coef);
3174 positive_coef_vars.push_back(vars[i]);
3175 } else if (coef < 0) {
3176 negative_coefs.push_back(-coef);
3177 negative_coef_vars.push_back(vars[i]);
3178 }
3179 }
3180 CHECK_GT(negative_coef_vars.size(), 0);
3181 IntExpr* const negatives =
3182 MakeScalProdAux(solver, negative_coef_vars, negative_coefs, 0);
3183 if (!positive_coef_vars.empty()) {
3184 IntExpr* const positives = MakeScalProdAux(solver, positive_coef_vars,
3185 positive_coefs, constant);
3186 return solver->MakeDifference(positives, negatives);
3187 } else {
3188 return solver->MakeDifference(constant, negatives);
3189 }
3190 }
3191 }
3192 }
3193 std::vector<IntVar*> terms;
3194 for (int i = 0; i < size; ++i) {
3195 terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
3196 }
3197 return MakeSumArrayAux(solver, terms, constant);
3198}
3199
3200IntExpr* MakeScalProdFct(Solver* solver, const std::vector<IntVar*>& pre_vars,
3201 const std::vector<int64_t>& pre_coefs) {
3202 int64_t constant = 0;
3203 std::vector<IntVar*> vars;
3204 std::vector<int64_t> coefs;
3205 DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
3206
3207 if (vars.empty()) {
3208 return solver->MakeIntConst(constant);
3209 }
3210 // Can we simplify using some gcd computation.
3211 int64_t gcd = std::abs(coefs[0]);
3212 for (int i = 1; i < coefs.size(); ++i) {
3213 gcd = MathUtil::GCD64(gcd, std::abs(coefs[i]));
3214 if (gcd == 1) {
3215 break;
3216 }
3217 }
3218 if (constant != 0 && gcd != 1) {
3219 gcd = MathUtil::GCD64(gcd, std::abs(constant));
3220 }
3221 if (gcd > 1) {
3222 for (int i = 0; i < coefs.size(); ++i) {
3223 coefs[i] /= gcd;
3224 }
3225 return solver->MakeProd(
3226 MakeScalProdAux(solver, vars, coefs, constant / gcd), gcd);
3227 }
3228 return MakeScalProdAux(solver, vars, coefs, constant);
3229}
3230
3231IntExpr* MakeSumFct(Solver* solver, const std::vector<IntVar*>& pre_vars) {
3232 absl::flat_hash_map<IntVar*, int64_t> variables_to_coefficients;
3233 ExprLinearizer linearizer(&variables_to_coefficients);
3234 for (int i = 0; i < pre_vars.size(); ++i) {
3235 linearizer.Visit(pre_vars[i], 1);
3236 }
3237 const int64_t constant = linearizer.Constant();
3238 std::vector<IntVar*> vars;
3239 std::vector<int64_t> coefs;
3240 for (const auto& variable_to_coefficient : variables_to_coefficients) {
3241 if (variable_to_coefficient.second != 0) {
3242 vars.push_back(variable_to_coefficient.first);
3243 coefs.push_back(variable_to_coefficient.second);
3244 }
3245 }
3246 return MakeScalProdAux(solver, vars, coefs, constant);
3247}
3248} // namespace
3249
3250// ----- API -----
3251
3252IntExpr* Solver::MakeSum(const std::vector<IntVar*>& vars) {
3253 const int size = vars.size();
3254 if (size == 0) {
3255 return MakeIntConst(int64_t{0});
3256 } else if (size == 1) {
3257 return vars[0];
3258 } else if (size == 2) {
3259 return MakeSum(vars[0], vars[1]);
3260 } else {
3261 IntExpr* const cache =
3262 model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_SUM);
3263 if (cache != nullptr) {
3264 return cache;
3265 } else {
3266 int64_t new_min = 0;
3267 int64_t new_max = 0;
3268 for (int i = 0; i < size; ++i) {
3269 if (new_min != std::numeric_limits<int64_t>::min()) {
3270 new_min = CapAdd(vars[i]->Min(), new_min);
3271 }
3272 if (new_max != std::numeric_limits<int64_t>::max()) {
3273 new_max = CapAdd(vars[i]->Max(), new_max);
3274 }
3275 }
3276 IntExpr* sum_expr = nullptr;
3277 const bool all_booleans = AreAllBooleans(vars);
3278 if (all_booleans) {
3279 const std::string name =
3280 absl::StrFormat("BooleanSum([%s])", JoinNamePtr(vars, ", "));
3281 sum_expr = MakeIntVar(new_min, new_max, name);
3282 AddConstraint(
3283 RevAlloc(new SumBooleanEqualToVar(this, vars, sum_expr->Var())));
3284 } else if (new_min != std::numeric_limits<int64_t>::min() &&
3285 new_max != std::numeric_limits<int64_t>::max()) {
3286 sum_expr = MakeSumFct(this, vars);
3287 } else {
3288 const std::string name =
3289 absl::StrFormat("Sum([%s])", JoinNamePtr(vars, ", "));
3290 sum_expr = MakeIntVar(new_min, new_max, name);
3291 AddConstraint(
3292 RevAlloc(new SafeSumConstraint(this, vars, sum_expr->Var())));
3293 }
3294 model_cache_->InsertVarArrayExpression(sum_expr, vars,
3295 ModelCache::VAR_ARRAY_SUM);
3296 return sum_expr;
3297 }
3298 }
3299}
3300
3301IntExpr* Solver::MakeMin(const std::vector<IntVar*>& vars) {
3302 const int size = vars.size();
3303 if (size == 0) {
3304 LOG(WARNING) << "operations_research::Solver::MakeMin() was called with an "
3305 "empty list of variables. Was this intentional?";
3306 return MakeIntConst(std::numeric_limits<int64_t>::max());
3307 } else if (size == 1) {
3308 return vars[0];
3309 } else if (size == 2) {
3310 return MakeMin(vars[0], vars[1]);
3311 } else {
3312 IntExpr* const cache =
3313 model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_MIN);
3314 if (cache != nullptr) {
3315 return cache;
3316 } else {
3317 if (AreAllBooleans(vars)) {
3318 IntVar* const new_var = MakeBoolVar();
3319 AddConstraint(RevAlloc(new ArrayBoolAndEq(this, vars, new_var)));
3320 model_cache_->InsertVarArrayExpression(new_var, vars,
3321 ModelCache::VAR_ARRAY_MIN);
3322 return new_var;
3323 } else {
3324 int64_t new_min = std::numeric_limits<int64_t>::max();
3325 int64_t new_max = std::numeric_limits<int64_t>::max();
3326 for (int i = 0; i < size; ++i) {
3327 new_min = std::min(new_min, vars[i]->Min());
3328 new_max = std::min(new_max, vars[i]->Max());
3329 }
3330 IntVar* const new_var = MakeIntVar(new_min, new_max);
3331 if (size <= parameters_.array_split_size()) {
3332 AddConstraint(RevAlloc(new SmallMinConstraint(this, vars, new_var)));
3333 } else {
3334 AddConstraint(RevAlloc(new MinConstraint(this, vars, new_var)));
3335 }
3336 model_cache_->InsertVarArrayExpression(new_var, vars,
3337 ModelCache::VAR_ARRAY_MIN);
3338 return new_var;
3339 }
3340 }
3341 }
3342}
3343
3344IntExpr* Solver::MakeMax(const std::vector<IntVar*>& vars) {
3345 const int size = vars.size();
3346 if (size == 0) {
3347 LOG(WARNING) << "operations_research::Solver::MakeMax() was called with an "
3348 "empty list of variables. Was this intentional?";
3349 return MakeIntConst(std::numeric_limits<int64_t>::min());
3350 } else if (size == 1) {
3351 return vars[0];
3352 } else if (size == 2) {
3353 return MakeMax(vars[0], vars[1]);
3354 } else {
3355 IntExpr* const cache =
3356 model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_MAX);
3357 if (cache != nullptr) {
3358 return cache;
3359 } else {
3360 if (AreAllBooleans(vars)) {
3361 IntVar* const new_var = MakeBoolVar();
3362 AddConstraint(RevAlloc(new ArrayBoolOrEq(this, vars, new_var)));
3363 model_cache_->InsertVarArrayExpression(new_var, vars,
3364 ModelCache::VAR_ARRAY_MIN);
3365 return new_var;
3366 } else {
3367 int64_t new_min = std::numeric_limits<int64_t>::min();
3368 int64_t new_max = std::numeric_limits<int64_t>::min();
3369 for (int i = 0; i < size; ++i) {
3370 new_min = std::max(new_min, vars[i]->Min());
3371 new_max = std::max(new_max, vars[i]->Max());
3372 }
3373 IntVar* const new_var = MakeIntVar(new_min, new_max);
3374 if (size <= parameters_.array_split_size()) {
3375 AddConstraint(RevAlloc(new SmallMaxConstraint(this, vars, new_var)));
3376 } else {
3377 AddConstraint(RevAlloc(new MaxConstraint(this, vars, new_var)));
3378 }
3379 model_cache_->InsertVarArrayExpression(new_var, vars,
3380 ModelCache::VAR_ARRAY_MAX);
3381 return new_var;
3382 }
3383 }
3384 }
3385}
3386
3387Constraint* Solver::MakeMinEquality(const std::vector<IntVar*>& vars,
3388 IntVar* const min_var) {
3389 const int size = vars.size();
3390 if (size > 2) {
3391 if (AreAllBooleans(vars)) {
3392 return RevAlloc(new ArrayBoolAndEq(this, vars, min_var));
3393 } else if (size <= parameters_.array_split_size()) {
3394 return RevAlloc(new SmallMinConstraint(this, vars, min_var));
3395 } else {
3396 return RevAlloc(new MinConstraint(this, vars, min_var));
3397 }
3398 } else if (size == 2) {
3399 return MakeEquality(MakeMin(vars[0], vars[1]), min_var);
3400 } else if (size == 1) {
3401 return MakeEquality(vars[0], min_var);
3402 } else {
3403 LOG(WARNING) << "operations_research::Solver::MakeMinEquality() was called "
3404 "with an empty list of variables. Was this intentional?";
3405 return MakeEquality(min_var, std::numeric_limits<int64_t>::max());
3406 }
3407}
3408
3409Constraint* Solver::MakeMaxEquality(const std::vector<IntVar*>& vars,
3410 IntVar* const max_var) {
3411 const int size = vars.size();
3412 if (size > 2) {
3413 if (AreAllBooleans(vars)) {
3414 return RevAlloc(new ArrayBoolOrEq(this, vars, max_var));
3415 } else if (size <= parameters_.array_split_size()) {
3416 return RevAlloc(new SmallMaxConstraint(this, vars, max_var));
3417 } else {
3418 return RevAlloc(new MaxConstraint(this, vars, max_var));
3419 }
3420 } else if (size == 2) {
3421 return MakeEquality(MakeMax(vars[0], vars[1]), max_var);
3422 } else if (size == 1) {
3423 return MakeEquality(vars[0], max_var);
3424 } else {
3425 LOG(WARNING) << "operations_research::Solver::MakeMaxEquality() was called "
3426 "with an empty list of variables. Was this intentional?";
3427 return MakeEquality(max_var, std::numeric_limits<int64_t>::min());
3428 }
3429}
3430
3431Constraint* Solver::MakeSumLessOrEqual(const std::vector<IntVar*>& vars,
3432 int64_t cst) {
3433 const int size = vars.size();
3434 if (cst == 1LL && AreAllBooleans(vars) && size > 2) {
3435 return RevAlloc(new SumBooleanLessOrEqualToOne(this, vars));
3436 } else {
3437 return MakeLessOrEqual(MakeSum(vars), cst);
3438 }
3439}
3440
3441Constraint* Solver::MakeSumGreaterOrEqual(const std::vector<IntVar*>& vars,
3442 int64_t cst) {
3443 const int size = vars.size();
3444 if (cst == 1LL && AreAllBooleans(vars) && size > 2) {
3445 return RevAlloc(new SumBooleanGreaterOrEqualToOne(this, vars));
3446 } else {
3447 return MakeGreaterOrEqual(MakeSum(vars), cst);
3448 }
3449}
3450
3451Constraint* Solver::MakeSumEquality(const std::vector<IntVar*>& vars,
3452 int64_t cst) {
3453 const int size = vars.size();
3454 if (size == 0) {
3455 return cst == 0 ? MakeTrueConstraint() : MakeFalseConstraint();
3456 }
3457 if (AreAllBooleans(vars) && size > 2) {
3458 if (cst == 1) {
3459 return RevAlloc(new SumBooleanEqualToOne(this, vars));
3460 } else if (cst < 0 || cst > size) {
3461 return MakeFalseConstraint();
3462 } else {
3463 return RevAlloc(new SumBooleanEqualToVar(this, vars, MakeIntConst(cst)));
3464 }
3465 } else {
3466 if (vars.size() == 1) {
3467 return MakeEquality(vars[0], cst);
3468 } else if (vars.size() == 2) {
3469 return MakeEquality(vars[0], MakeDifference(cst, vars[1]));
3470 }
3471 if (DetectSumOverflow(vars)) {
3472 return RevAlloc(new SafeSumConstraint(this, vars, MakeIntConst(cst)));
3473 } else if (size <= parameters_.array_split_size()) {
3474 return RevAlloc(new SmallSumConstraint(this, vars, MakeIntConst(cst)));
3475 } else {
3476 return RevAlloc(new SumConstraint(this, vars, MakeIntConst(cst)));
3477 }
3478 }
3479}
3480
3481Constraint* Solver::MakeSumEquality(const std::vector<IntVar*>& vars,
3482 IntVar* const var) {
3483 const int size = vars.size();
3484 if (size == 0) {
3485 return MakeEquality(var, Zero());
3486 }
3487 if (AreAllBooleans(vars) && size > 2) {
3488 return RevAlloc(new SumBooleanEqualToVar(this, vars, var));
3489 } else if (size == 0) {
3490 return MakeEquality(var, Zero());
3491 } else if (size == 1) {
3492 return MakeEquality(vars[0], var);
3493 } else if (size == 2) {
3494 return MakeEquality(MakeSum(vars[0], vars[1]), var);
3495 } else {
3496 if (DetectSumOverflow(vars)) {
3497 return RevAlloc(new SafeSumConstraint(this, vars, var));
3498 } else if (size <= parameters_.array_split_size()) {
3499 return RevAlloc(new SmallSumConstraint(this, vars, var));
3500 } else {
3501 return RevAlloc(new SumConstraint(this, vars, var));
3502 }
3503 }
3504}
3505
3506Constraint* Solver::MakeScalProdEquality(
3507 const std::vector<IntVar*>& vars, const std::vector<int64_t>& coefficients,
3508 int64_t cst) {
3509 DCHECK_EQ(vars.size(), coefficients.size());
3510 return MakeScalProdEqualityFct(this, vars, coefficients, cst);
3511}
3512
3513Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
3514 const std::vector<int>& coefficients,
3515 int64_t cst) {
3516 DCHECK_EQ(vars.size(), coefficients.size());
3517 return MakeScalProdEqualityFct(this, vars, ToInt64Vector(coefficients), cst);
3518}
3519
3520Constraint* Solver::MakeScalProdEquality(
3521 const std::vector<IntVar*>& vars, const std::vector<int64_t>& coefficients,
3522 IntVar* const target) {
3523 DCHECK_EQ(vars.size(), coefficients.size());
3524 return MakeScalProdEqualityVarFct(this, vars, coefficients, target);
3525}
3526
3527Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
3528 const std::vector<int>& coefficients,
3529 IntVar* const target) {
3530 DCHECK_EQ(vars.size(), coefficients.size());
3531 return MakeScalProdEqualityVarFct(this, vars, ToInt64Vector(coefficients),
3532 target);
3533}
3534
3535Constraint* Solver::MakeScalProdGreaterOrEqual(
3536 const std::vector<IntVar*>& vars, const std::vector<int64_t>& coeffs,
3537 int64_t cst) {
3538 DCHECK_EQ(vars.size(), coeffs.size());
3539 return MakeScalProdGreaterOrEqualFct(this, vars, coeffs, cst);
3540}
3541
3542Constraint* Solver::MakeScalProdGreaterOrEqual(const std::vector<IntVar*>& vars,
3543 const std::vector<int>& coeffs,
3544 int64_t cst) {
3545 DCHECK_EQ(vars.size(), coeffs.size());
3546 return MakeScalProdGreaterOrEqualFct(this, vars, ToInt64Vector(coeffs), cst);
3547}
3548
3549Constraint* Solver::MakeScalProdLessOrEqual(
3550 const std::vector<IntVar*>& vars, const std::vector<int64_t>& coefficients,
3551 int64_t cst) {
3552 DCHECK_EQ(vars.size(), coefficients.size());
3553 return MakeScalProdLessOrEqualFct(this, vars, coefficients, cst);
3554}
3555
3556Constraint* Solver::MakeScalProdLessOrEqual(
3557 const std::vector<IntVar*>& vars, const std::vector<int>& coefficients,
3558 int64_t cst) {
3559 DCHECK_EQ(vars.size(), coefficients.size());
3560 return MakeScalProdLessOrEqualFct(this, vars, ToInt64Vector(coefficients),
3561 cst);
3562}
3563
3564IntExpr* Solver::MakeScalProd(const std::vector<IntVar*>& vars,
3565 const std::vector<int64_t>& coefs) {
3566 DCHECK_EQ(vars.size(), coefs.size());
3567 return MakeScalProdFct(this, vars, coefs);
3568}
3569
3570IntExpr* Solver::MakeScalProd(const std::vector<IntVar*>& vars,
3571 const std::vector<int>& coefs) {
3572 DCHECK_EQ(vars.size(), coefs.size());
3573 return MakeScalProdFct(this, vars, ToInt64Vector(coefs));
3574}
3575} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:491
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:891
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
#define LOG(severity)
Definition: base/logging.h:416
#define DCHECK(condition)
Definition: base/logging.h:885
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
A constraint is the main modeling object.
The class IntExpr is the base of all integer expressions in constraint programming.
virtual IntVar * Var()=0
Creates a variable from the expression.
The class IntVar is a subset of IntExpr.
SatParameters parameters
const std::string name
const Constraint * ct
int64_t value
const std::vector< IntVar * > vars_
Definition: expr_array.cc:138
IntVar * var
Definition: expr_array.cc:1874
Rev< int64_t > node_max
Definition: expr_array.cc:144
#define IS_TYPE(type, tag)
Definition: expr_array.cc:2402
int64_t coef
Definition: expr_array.cc:1875
RevSwitch inactive_
Definition: expr_array.cc:1487
Rev< int64_t > node_min
Definition: expr_array.cc:143
double upper_bound
absl::Span< const double > coefficients
const int WARNING
Definition: log_severity.h:31
const int FATAL
Definition: log_severity.h:32
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
bool AreAllNegative(const std::vector< T > &values)
int64_t CapSub(int64_t x, int64_t y)
bool AreAllBoundOrNull(const std::vector< IntVar * > &vars, const std::vector< T > &values)
Returns true if all the variables are assigned to a single value, or if their corresponding value is ...
int64_t Zero()
NOLINT.
Demon * MakeDelayedConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
bool AreAllBooleans(const std::vector< IntVar * > &vars)
std::string JoinDebugStringPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:45
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
int64_t CapProd(int64_t x, int64_t y)
bool AreAllNull(const std::vector< T > &values)
bool AreAllPositive(const std::vector< T > &values)
std::vector< int64_t > ToInt64Vector(const std::vector< int > &input)
Definition: utilities.cc:828
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)
std::string JoinNamePtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:52
STL namespace.
int index
Definition: pack.cc:509
int64_t coefficient
IntervalVar *const target_var_