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