Google OR-Tools v9.9
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
graph_constraints.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#include <algorithm>
15#include <cstdint>
16#include <deque>
17#include <memory>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_set.h"
23#include "absl/strings/str_cat.h"
24#include "absl/strings/str_format.h"
25#include "absl/strings/str_join.h"
26#include "absl/types/span.h"
28#include "ortools/base/types.h"
33
34namespace operations_research {
35// ---------- No cycle ----------
36
37// This constraint ensures there are no cycles in the variable/value graph.
38// "Sink" values are values outside the range of the array of variables; they
39// are used to end paths.
40// The constraint does essentially two things:
41// - forbid partial paths from looping back to themselves
42// - ensure each variable/node can be connected to a "sink".
43// If assume_paths is true, the constraint assumes the 'next' variables
44// represent paths (and performs a faster propagation); otherwise the
45// constraint assumes the 'next' variables represent a forest.
46// TODO(user): improve code when assume_paths is false (currently does an
47// expensive n^2 loop).
48
49namespace {
50class NoCycle : public Constraint {
51 public:
52 NoCycle(Solver* s, const std::vector<IntVar*>& nexts,
53 const std::vector<IntVar*>& active, Solver::IndexFilter1 sink_handler,
54 bool assume_paths);
55 ~NoCycle() override {}
56 void Post() override;
57 void InitialPropagate() override;
58 void NextChange(int index);
59 void ActiveBound(int index);
60 void NextBound(int index);
61 void ComputeSupports();
62 void ComputeSupport(int index);
63 std::string DebugString() const override;
64
65 void Accept(ModelVisitor* const visitor) const override {
66 visitor->BeginVisitConstraint(ModelVisitor::kNoCycle, this);
67 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
68 nexts_);
69 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
70 active_);
71 visitor->VisitIntegerArgument("assume_paths", assume_paths_);
72 visitor->VisitInt64ToBoolExtension(sink_handler_, -size(), size());
73 visitor->EndVisitConstraint(ModelVisitor::kNoCycle, this);
74 }
75
76 private:
77 int64_t size() const { return nexts_.size(); }
78
79 const std::vector<IntVar*> nexts_;
80 const std::vector<IntVar*> active_;
81 std::vector<IntVarIterator*> iterators_;
82 RevArray<int64_t> starts_;
83 RevArray<int64_t> ends_;
84 RevArray<bool> marked_;
85 bool all_nexts_bound_;
86 std::vector<int64_t> outbound_supports_;
87 std::vector<int64_t> support_leaves_;
88 std::vector<int64_t> unsupported_;
89 Solver::IndexFilter1 sink_handler_;
90 std::vector<int64_t> sinks_;
91 bool assume_paths_;
92};
93
94NoCycle::NoCycle(Solver* const s, const std::vector<IntVar*>& nexts,
95 const std::vector<IntVar*>& active,
96 Solver::IndexFilter1 sink_handler, bool assume_paths)
97 : Constraint(s),
98 nexts_(nexts),
99 active_(active),
100 iterators_(nexts.size(), nullptr),
101 starts_(nexts.size(), -1),
102 ends_(nexts.size(), -1),
103 marked_(nexts.size(), false),
104 all_nexts_bound_(false),
105 outbound_supports_(nexts.size(), -1),
106 sink_handler_(std::move(sink_handler)),
107 assume_paths_(assume_paths) {
108 support_leaves_.reserve(size());
109 unsupported_.reserve(size());
110 for (int i = 0; i < size(); ++i) {
111 starts_.SetValue(s, i, i);
112 ends_.SetValue(s, i, i);
113 iterators_[i] = nexts_[i]->MakeDomainIterator(true);
114 }
115}
116
117void NoCycle::InitialPropagate() {
118 // Reduce next domains to sinks + range of nexts
119 for (int i = 0; i < size(); ++i) {
120 outbound_supports_[i] = -1;
121 IntVar* next = nexts_[i];
122 for (int j = next->Min(); j < 0; ++j) {
123 if (!sink_handler_(j)) {
124 next->RemoveValue(j);
125 }
126 }
127 for (int j = next->Max(); j >= size(); --j) {
128 if (!sink_handler_(j)) {
129 next->RemoveValue(j);
130 }
131 }
132 }
133 solver()->SaveAndSetValue(&all_nexts_bound_, true);
134 for (int i = 0; i < size(); ++i) {
135 if (nexts_[i]->Bound()) {
136 NextBound(i);
137 } else {
138 solver()->SaveAndSetValue(&all_nexts_bound_, false);
139 }
140 }
141 ComputeSupports();
142}
143
144void NoCycle::Post() {
145 if (size() == 0) return;
146 for (int i = 0; i < size(); ++i) {
147 IntVar* next = nexts_[i];
148 Demon* support_demon = MakeConstraintDemon1(
149 solver(), this, &NoCycle::NextChange, "NextChange", i);
150 next->WhenDomain(support_demon);
151 Demon* active_demon = MakeConstraintDemon1(
152 solver(), this, &NoCycle::ActiveBound, "ActiveBound", i);
153 active_[i]->WhenBound(active_demon);
154 }
155 // Setting up sinks
156 int64_t min_min = nexts_[0]->Min();
157 int64_t max_max = nexts_[0]->Max();
158 for (int i = 1; i < size(); ++i) {
159 const IntVar* next = nexts_[i];
160 min_min = std::min(min_min, next->Min());
161 max_max = std::max(max_max, next->Max());
162 }
163 sinks_.clear();
164 for (int i = min_min; i <= max_max; ++i) {
165 if (sink_handler_(i)) {
166 sinks_.push_back(i);
167 }
168 }
169}
170
171void NoCycle::NextChange(int index) {
172 IntVar* const next_var = nexts_[index];
173 if (next_var->Bound()) {
174 NextBound(index);
175 }
176 if (!all_nexts_bound_) {
177 bool all_nexts_bound = true;
178 for (int i = 0; i < size(); ++i) {
179 if (!nexts_[i]->Bound()) {
180 all_nexts_bound = false;
181 break;
182 }
183 }
184 solver()->SaveAndSetValue(&all_nexts_bound_, all_nexts_bound);
185 }
186 if (all_nexts_bound_) {
187 return;
188 }
189 if (!next_var->Contains(outbound_supports_[index])) {
190 ComputeSupport(index);
191 }
192}
193
194void NoCycle::ActiveBound(int index) {
195 if (nexts_[index]->Bound()) {
196 NextBound(index);
197 }
198}
199
200void NoCycle::NextBound(int index) {
201 if (active_[index]->Min() == 0) return;
202 if (marked_[index]) return;
203 Solver* const s = solver();
204 // Subtle: marking indices to avoid overwriting chain starts and ends if
205 // propagation for active_[index] or nexts_[index] has already been done.
206 marked_.SetValue(s, index, true);
207 const int64_t next = nexts_[index]->Value();
208 const int64_t chain_start = starts_[index];
209 const int64_t chain_end = !sink_handler_(next) ? ends_[next] : next;
210 if (!sink_handler_(chain_start)) {
211 ends_.SetValue(s, chain_start, chain_end);
212 if (!sink_handler_(chain_end)) {
213 starts_.SetValue(s, chain_end, chain_start);
214 nexts_[chain_end]->RemoveValue(chain_start);
215 if (!assume_paths_) {
216 for (int i = 0; i < size(); ++i) {
217 int64_t current = i;
218 bool found = (current == chain_end);
219 // Counter to detect implicit cycles.
220 int count = 0;
221 while (!found && count < size() && !sink_handler_(current) &&
222 nexts_[current]->Bound()) {
223 current = nexts_[current]->Value();
224 found = (current == chain_end);
225 ++count;
226 }
227 if (found) {
228 nexts_[chain_end]->RemoveValue(i);
229 }
230 }
231 }
232 }
233 }
234}
235
236// Compute the support tree. For each variable, find a path connecting to a
237// sink. Starts partial paths from the sinks down to all unconnected variables.
238// If some variables remain unconnected, make the corresponding active_
239// variable false.
240// Resulting tree is used as supports for next variables.
241// TODO(user): Try to see if we can find an algorithm which is less than
242// quadratic to do this (note that if the tree is flat we are already linear
243// for a given number of sinks).
244void NoCycle::ComputeSupports() {
245 // unsupported_ contains nodes not connected to sinks.
246 unsupported_.clear();
247 // supported_leaves_ contains the current frontier containing nodes surely
248 // connected to sinks.
249 support_leaves_.clear();
250 // Initial phase: find direct connections to sinks and initialize
251 // support_leaves_ and unsupported_ accordingly.
252 const int sink_size = sinks_.size();
253 for (int i = 0; i < size(); ++i) {
254 const IntVar* next = nexts_[i];
255 // If node is not active, no need to try to connect it to a sink.
256 if (active_[i]->Max() != 0) {
257 const int64_t current_support = outbound_supports_[i];
258 // Optimization: if this node was already supported by a sink, check if
259 // it's still a valid support.
260 if (current_support >= 0 && sink_handler_(current_support) &&
261 next->Contains(current_support)) {
262 support_leaves_.push_back(i);
263 } else {
264 // Optimization: iterate on sinks or next domain depending on which is
265 // smaller.
266 outbound_supports_[i] = -1;
267 if (sink_size < next->Size()) {
268 for (int j = 0; j < sink_size; ++j) {
269 if (next->Contains(sinks_[j])) {
270 outbound_supports_[i] = sinks_[j];
271 support_leaves_.push_back(i);
272 break;
273 }
274 }
275 } else {
276 for (const int64_t value : InitAndGetValues(iterators_[i])) {
277 if (sink_handler_(value)) {
278 outbound_supports_[i] = value;
279 support_leaves_.push_back(i);
280 break;
281 }
282 }
283 }
284 }
285 if (outbound_supports_[i] == -1) {
286 unsupported_.push_back(i);
287 }
288 }
289 }
290 // No need to iterate on all nodes connected to sinks but just on the ones
291 // added in the last iteration; leaves_begin and leaves_end mark the block
292 // in support_leaves_ corresponding to such nodes.
293 size_t leaves_begin = 0;
294 size_t leaves_end = support_leaves_.size();
295 while (!unsupported_.empty()) {
296 // Try to connected unsupported nodes to nodes connected to sinks.
297 for (int64_t unsupported_index = 0; unsupported_index < unsupported_.size();
298 ++unsupported_index) {
299 const int64_t unsupported = unsupported_[unsupported_index];
300 const IntVar* const next = nexts_[unsupported];
301 for (int i = leaves_begin; i < leaves_end; ++i) {
302 if (next->Contains(support_leaves_[i])) {
303 outbound_supports_[unsupported] = support_leaves_[i];
304 support_leaves_.push_back(unsupported);
305 // Remove current node from unsupported vector.
306 unsupported_[unsupported_index] = unsupported_.back();
307 unsupported_.pop_back();
308 --unsupported_index;
309 break;
310 }
311 // TODO(user): evaluate same trick as with the sinks by keeping a
312 // bitmap with supported nodes.
313 }
314 }
315 // No new leaves were added, we can bail out.
316 if (leaves_end == support_leaves_.size()) {
317 break;
318 }
319 leaves_begin = leaves_end;
320 leaves_end = support_leaves_.size();
321 }
322 // Mark as inactive any unsupported node.
323 for (int64_t unsupported_index = 0; unsupported_index < unsupported_.size();
324 ++unsupported_index) {
325 active_[unsupported_[unsupported_index]]->SetMax(0);
326 }
327}
328
329void NoCycle::ComputeSupport(int index) {
330 // Try to reconnect the node to the support tree by finding a next node
331 // which is both supported and was not a descendant of the node in the tree.
332 if (active_[index]->Max() != 0) {
333 for (const int64_t next : InitAndGetValues(iterators_[index])) {
334 if (sink_handler_(next)) {
335 outbound_supports_[index] = next;
336 return;
337 }
338 if (next != index && next < outbound_supports_.size()) {
339 int64_t next_support = outbound_supports_[next];
340 if (next_support >= 0) {
341 // Check if next is not already a descendant of index.
342 bool ancestor_found = false;
343 while (next_support < outbound_supports_.size() &&
344 !sink_handler_(next_support)) {
345 if (next_support == index) {
346 ancestor_found = true;
347 break;
348 }
349 next_support = outbound_supports_[next_support];
350 }
351 if (!ancestor_found) {
352 outbound_supports_[index] = next;
353 return;
354 }
355 }
356 }
357 }
358 }
359 // No support was found, rebuild the support tree.
360 ComputeSupports();
361}
362
363std::string NoCycle::DebugString() const {
364 return absl::StrFormat("NoCycle(%s)", JoinDebugStringPtr(nexts_, ", "));
365}
366
367// ----- Circuit constraint -----
368
369class Circuit : public Constraint {
370 public:
371 Circuit(Solver* const s, const std::vector<IntVar*>& nexts, bool sub_circuit)
372 : Constraint(s),
373 nexts_(nexts),
374 size_(nexts_.size()),
375 starts_(size_, -1),
376 ends_(size_, -1),
377 lengths_(size_, 1),
378 domains_(size_),
379 outbound_support_(size_, -1),
380 inbound_support_(size_, -1),
381 temp_support_(size_, -1),
382 inbound_demon_(nullptr),
383 outbound_demon_(nullptr),
384 root_(-1),
385 num_inactives_(0),
386 sub_circuit_(sub_circuit) {
387 for (int i = 0; i < size_; ++i) {
388 domains_[i] = nexts_[i]->MakeDomainIterator(true);
389 }
390 }
391
392 ~Circuit() override {}
393
394 void Post() override {
395 inbound_demon_ = MakeDelayedConstraintDemon0(
396 solver(), this, &Circuit::CheckReachabilityToRoot,
397 "CheckReachabilityToRoot");
398 outbound_demon_ = MakeDelayedConstraintDemon0(
399 solver(), this, &Circuit::CheckReachabilityFromRoot,
400 "CheckReachabilityFromRoot");
401 for (int i = 0; i < size_; ++i) {
402 if (!nexts_[i]->Bound()) {
403 Demon* const bound_demon = MakeConstraintDemon1(
404 solver(), this, &Circuit::NextBound, "NextBound", i);
405 nexts_[i]->WhenBound(bound_demon);
406 Demon* const domain_demon = MakeConstraintDemon1(
407 solver(), this, &Circuit::NextDomain, "NextDomain", i);
408 nexts_[i]->WhenDomain(domain_demon);
409 }
410 }
411 solver()->AddConstraint(solver()->MakeAllDifferent(nexts_));
412 }
413
414 void InitialPropagate() override {
415 Solver* const s = solver();
416 if (!sub_circuit_) {
417 root_.SetValue(solver(), 0);
418 }
419 for (int i = 0; i < size_; ++i) {
420 nexts_[i]->SetRange(0, size_ - 1);
421 if (!sub_circuit_) {
422 nexts_[i]->RemoveValue(i);
423 }
424 }
425 for (int i = 0; i < size_; ++i) {
426 starts_.SetValue(s, i, i);
427 ends_.SetValue(s, i, i);
428 lengths_.SetValue(s, i, 1);
429 }
430 for (int i = 0; i < size_; ++i) {
431 if (nexts_[i]->Bound()) {
432 NextBound(i);
433 }
434 }
435 CheckReachabilityFromRoot();
436 CheckReachabilityToRoot();
437 }
438
439 std::string DebugString() const override {
440 return absl::StrFormat("%sCircuit(%s)", sub_circuit_ ? "Sub" : "",
441 JoinDebugStringPtr(nexts_, " "));
442 }
443
444 void Accept(ModelVisitor* const visitor) const override {
445 visitor->BeginVisitConstraint(ModelVisitor::kCircuit, this);
446 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
447 nexts_);
448 visitor->VisitIntegerArgument(ModelVisitor::kPartialArgument, sub_circuit_);
449 visitor->EndVisitConstraint(ModelVisitor::kCircuit, this);
450 }
451
452 private:
453 bool Inactive(int index) const {
454 return nexts_[index]->Bound() && nexts_[index]->Min() == index;
455 }
456
457 void NextBound(int index) {
458 Solver* const s = solver();
459 const int destination = nexts_[index]->Value();
460 const int root = root_.Value();
461 if (destination != index) {
462 if (root == -1) {
463 root_.SetValue(s, index);
464 }
465 const int new_end = ends_.Value(destination);
466 const int new_start = starts_.Value(index);
467 starts_.SetValue(s, new_end, new_start);
468 ends_.SetValue(s, new_start, new_end);
469 lengths_.SetValue(
470 s, new_start,
471 lengths_.Value(new_start) + lengths_.Value(destination));
472 if (sub_circuit_) {
473 // You are creating the only path. Nexts can no longer loop upon itself.
474 nexts_[destination]->RemoveValue(destination);
475 } else {
476 if (lengths_.Value(new_start) < size_ - 1 - num_inactives_.Value()) {
477 nexts_[new_end]->RemoveValue(new_start);
478 }
479 }
480 } else {
481 num_inactives_.Incr(solver());
482 }
483 // TODO(user): You might get more propagation if you maintain
484 // num_undecided_actives_;
485 // then
486 // if (num_undecided_actives_ == 0 &&
487 // lengths_.Value(new_start) < size_ - 1 - num_inactives_.Value()) {
488 // nexts_[new_end]->RemoveValue(new_start);
489 // }
490 // for both complete == true and false.
491 }
492
493 void NextDomain(int index) {
494 if (root_.Value() == -1) {
495 return;
496 }
497 if (!nexts_[index]->Contains(outbound_support_[index])) {
498 EnqueueDelayedDemon(outbound_demon_);
499 }
500 if (!nexts_[index]->Contains(inbound_support_[index])) {
501 EnqueueDelayedDemon(inbound_demon_);
502 }
503 }
504
505 void TryInsertReached(int candidate, int64_t after) {
506 if (!reached_[after]) {
507 reached_[after] = true;
508 insertion_queue_.push_back(after);
509 temp_support_[candidate] = after;
510 }
511 }
512
513 void CheckReachabilityFromRoot() {
514 if (root_.Value() == -1) { // Root is not yet defined. Nothing to deduce.
515 return;
516 }
517
518 // Assign temp_support_ to a dummy value.
519 temp_support_.assign(size_, -1);
520 // Clear the spanning tree.
521 int processed = 0;
522 reached_.assign(size_, false);
523 insertion_queue_.clear();
524 // Add the root node.
525 const int root_value = root_.Value();
526 reached_[root_value] = true;
527 insertion_queue_.push_back(root_value);
528 // Compute reachable nodes.
529 while (processed < insertion_queue_.size() &&
530 insertion_queue_.size() + num_inactives_.Value() < size_) {
531 const int candidate = insertion_queue_[processed++];
532 IntVar* const var = nexts_[candidate];
533 switch (var->Size()) {
534 case 1: {
535 TryInsertReached(candidate, var->Min());
536 break;
537 }
538 case 2: {
539 TryInsertReached(candidate, var->Min());
540 TryInsertReached(candidate, var->Max());
541 break;
542 }
543 default: {
544 IntVarIterator* const domain = domains_[candidate];
545 for (const int64_t value : InitAndGetValues(domain)) {
546 TryInsertReached(candidate, value);
547 }
548 }
549 }
550 }
551 // All non reachable nodes should point to themselves in the incomplete
552 // case
553 for (int i = 0; i < size_; ++i) {
554 if (!reached_[i]) {
555 nexts_[i]->SetValue(i);
556 }
557 }
558 // Update the outbound_support_ vector.
559 outbound_support_.swap(temp_support_);
560 }
561
562 void CheckReachabilityToRoot() {
563 // TODO(user): Improve with prev_ data structure.
564 if (root_.Value() == -1) {
565 return;
566 }
567
568 insertion_queue_.clear();
569 insertion_queue_.push_back(root_.Value());
570 temp_support_[root_.Value()] = nexts_[root_.Value()]->Min();
571 int processed = 0;
572 to_visit_.clear();
573 for (int i = 0; i < size_; ++i) {
574 if (!Inactive(i) && i != root_.Value()) {
575 to_visit_.push_back(i);
576 }
577 }
578 const int inactive = num_inactives_.Value();
579 while (processed < insertion_queue_.size() &&
580 insertion_queue_.size() + inactive < size_) {
581 const int inserted = insertion_queue_[processed++];
582 std::vector<int> rejected;
583 for (int index = 0; index < to_visit_.size(); ++index) {
584 const int candidate = to_visit_[index];
585 if (nexts_[candidate]->Contains(inserted)) {
586 insertion_queue_.push_back(candidate);
587 temp_support_[candidate] = inserted;
588 } else {
589 rejected.push_back(candidate);
590 }
591 }
592 to_visit_.swap(rejected);
593 }
594 for (int i = 0; i < to_visit_.size(); ++i) {
595 const int node = to_visit_[i];
596 nexts_[node]->SetValue(node);
597 }
598 temp_support_.swap(inbound_support_);
599 }
600
601 const std::vector<IntVar*> nexts_;
602 const int size_;
603 std::vector<int> insertion_queue_;
604 std::vector<int> to_visit_;
605 std::vector<bool> reached_;
606 RevArray<int> starts_;
607 RevArray<int> ends_;
608 RevArray<int> lengths_;
609 std::vector<IntVarIterator*> domains_;
610 std::vector<int> outbound_support_;
611 std::vector<int> inbound_support_;
612 std::vector<int> temp_support_;
613 Demon* inbound_demon_;
614 Demon* outbound_demon_;
615 Rev<int> root_;
616 NumericalRev<int> num_inactives_;
617 const bool sub_circuit_;
618};
619} // namespace
620
621Constraint* Solver::MakeNoCycle(const std::vector<IntVar*>& nexts,
622 const std::vector<IntVar*>& active,
623 Solver::IndexFilter1 sink_handler,
624 bool assume_paths) {
625 CHECK_EQ(nexts.size(), active.size());
626 if (sink_handler == nullptr) {
627 const int64_t size = nexts.size();
628 sink_handler = [size](int64_t index) { return index >= size; };
629 }
630 return RevAlloc(new NoCycle(this, nexts, active, sink_handler, assume_paths));
631}
632
633Constraint* Solver::MakeNoCycle(const std::vector<IntVar*>& nexts,
634 const std::vector<IntVar*>& active,
635 Solver::IndexFilter1 sink_handler) {
636 return MakeNoCycle(nexts, active, std::move(sink_handler), true);
637}
638
639// TODO(user): Merge NoCycle and Circuit.
640Constraint* Solver::MakeCircuit(const std::vector<IntVar*>& nexts) {
641 return RevAlloc(new Circuit(this, nexts, false));
642}
643
644Constraint* Solver::MakeSubCircuit(const std::vector<IntVar*>& nexts) {
645 return RevAlloc(new Circuit(this, nexts, true));
646}
647
648// ----- Path cumul constraints -----
649
650namespace {
651class BasePathCumul : public Constraint {
652 public:
653 BasePathCumul(Solver* s, const std::vector<IntVar*>& nexts,
654 const std::vector<IntVar*>& active,
655 const std::vector<IntVar*>& cumuls);
656 ~BasePathCumul() override {}
657 void Post() override;
658 void InitialPropagate() override;
659 void ActiveBound(int index);
660 virtual void NextBound(int index) = 0;
661 virtual bool AcceptLink(int i, int j) const = 0;
662 void UpdateSupport(int index);
663 void CumulRange(int index);
664 std::string DebugString() const override;
665
666 protected:
667 int64_t size() const { return nexts_.size(); }
668 int cumul_size() const { return cumuls_.size(); }
669
670 const std::vector<IntVar*> nexts_;
671 const std::vector<IntVar*> active_;
672 const std::vector<IntVar*> cumuls_;
673 RevArray<int> prevs_;
674 std::vector<int> supports_;
675};
676
677BasePathCumul::BasePathCumul(Solver* const s, const std::vector<IntVar*>& nexts,
678 const std::vector<IntVar*>& active,
679 const std::vector<IntVar*>& cumuls)
680 : Constraint(s),
681 nexts_(nexts),
682 active_(active),
683 cumuls_(cumuls),
684 prevs_(cumuls.size(), -1),
685 supports_(nexts.size()) {
686 CHECK_GE(cumul_size(), size());
687 for (int i = 0; i < size(); ++i) {
688 supports_[i] = -1;
689 }
690}
691
692void BasePathCumul::InitialPropagate() {
693 for (int i = 0; i < size(); ++i) {
694 if (nexts_[i]->Bound()) {
695 NextBound(i);
696 } else {
697 UpdateSupport(i);
698 }
699 }
700}
701
702void BasePathCumul::Post() {
703 for (int i = 0; i < size(); ++i) {
704 IntVar* var = nexts_[i];
705 Demon* d = MakeConstraintDemon1(solver(), this, &BasePathCumul::NextBound,
706 "NextBound", i);
707 var->WhenBound(d);
708 Demon* ds = MakeConstraintDemon1(
709 solver(), this, &BasePathCumul::UpdateSupport, "UpdateSupport", i);
710 var->WhenDomain(ds);
711 Demon* active_demon = MakeConstraintDemon1(
712 solver(), this, &BasePathCumul::ActiveBound, "ActiveBound", i);
713 active_[i]->WhenBound(active_demon);
714 }
715 for (int i = 0; i < cumul_size(); ++i) {
716 IntVar* cumul = cumuls_[i];
717 Demon* d = MakeConstraintDemon1(solver(), this, &BasePathCumul::CumulRange,
718 "CumulRange", i);
719 cumul->WhenRange(d);
720 }
721}
722
723void BasePathCumul::ActiveBound(int index) {
724 if (nexts_[index]->Bound()) {
725 NextBound(index);
726 }
727}
728
729void BasePathCumul::CumulRange(int index) {
730 if (index < size()) {
731 if (nexts_[index]->Bound()) {
732 NextBound(index);
733 } else {
734 UpdateSupport(index);
735 }
736 }
737 if (prevs_[index] >= 0) {
738 NextBound(prevs_[index]);
739 } else {
740 for (int i = 0; i < size(); ++i) {
741 if (index == supports_[i]) {
742 UpdateSupport(i);
743 }
744 }
745 }
746}
747
748void BasePathCumul::UpdateSupport(int index) {
749 int support = supports_[index];
750 if (support < 0 || !AcceptLink(index, support)) {
751 IntVar* var = nexts_[index];
752 for (int i = var->Min(); i <= var->Max(); ++i) {
753 if (i != support && AcceptLink(index, i)) {
754 supports_[index] = i;
755 return;
756 }
757 }
758 active_[index]->SetMax(0);
759 }
760}
761
762std::string BasePathCumul::DebugString() const {
763 std::string out = "PathCumul(";
764 for (int i = 0; i < size(); ++i) {
765 out += nexts_[i]->DebugString() + " " + cumuls_[i]->DebugString();
766 }
767 out += ")";
768 return out;
769}
770
771// cumuls[next[i]] = cumuls[i] + transits[i]
772
773class PathCumul : public BasePathCumul {
774 public:
775 PathCumul(Solver* const s, const std::vector<IntVar*>& nexts,
776 const std::vector<IntVar*>& active,
777 const std::vector<IntVar*>& cumuls,
778 const std::vector<IntVar*>& transits)
779 : BasePathCumul(s, nexts, active, cumuls), transits_(transits) {}
780 ~PathCumul() override {}
781 void Post() override;
782 void NextBound(int index) override;
783 bool AcceptLink(int i, int j) const override;
784 void TransitRange(int index);
785
786 void Accept(ModelVisitor* const visitor) const override {
787 visitor->BeginVisitConstraint(ModelVisitor::kPathCumul, this);
788 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
789 nexts_);
790 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
791 active_);
792 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
793 cumuls_);
794 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kTransitsArgument,
795 transits_);
796 visitor->EndVisitConstraint(ModelVisitor::kPathCumul, this);
797 }
798
799 private:
800 const std::vector<IntVar*> transits_;
801};
802
803void PathCumul::Post() {
804 BasePathCumul::Post();
805 for (int i = 0; i < size(); ++i) {
806 Demon* transit_demon = MakeConstraintDemon1(
807 solver(), this, &PathCumul::TransitRange, "TransitRange", i);
808 transits_[i]->WhenRange(transit_demon);
809 }
810}
811
812void PathCumul::NextBound(int index) {
813 if (active_[index]->Min() == 0) return;
814 const int64_t next = nexts_[index]->Value();
815 IntVar* cumul = cumuls_[index];
816 IntVar* cumul_next = cumuls_[next];
817 IntVar* transit = transits_[index];
818 cumul_next->SetMin(cumul->Min() + transit->Min());
819 cumul_next->SetMax(CapAdd(cumul->Max(), transit->Max()));
820 cumul->SetMin(CapSub(cumul_next->Min(), transit->Max()));
821 cumul->SetMax(CapSub(cumul_next->Max(), transit->Min()));
822 transit->SetMin(CapSub(cumul_next->Min(), cumul->Max()));
823 transit->SetMax(CapSub(cumul_next->Max(), cumul->Min()));
824 if (prevs_[next] < 0) {
825 prevs_.SetValue(solver(), next, index);
826 }
827}
828
829void PathCumul::TransitRange(int index) {
830 if (nexts_[index]->Bound()) {
831 NextBound(index);
832 } else {
833 UpdateSupport(index);
834 }
835 if (prevs_[index] >= 0) {
836 NextBound(prevs_[index]);
837 } else {
838 for (int i = 0; i < size(); ++i) {
839 if (index == supports_[i]) {
840 UpdateSupport(i);
841 }
842 }
843 }
844}
845
846bool PathCumul::AcceptLink(int i, int j) const {
847 const IntVar* const cumul_i = cumuls_[i];
848 const IntVar* const cumul_j = cumuls_[j];
849 const IntVar* const transit_i = transits_[i];
850 return transit_i->Min() <= CapSub(cumul_j->Max(), cumul_i->Min()) &&
851 CapSub(cumul_j->Min(), cumul_i->Max()) <= transit_i->Max();
852}
853
854namespace {
855template <class T>
856class StampedVector {
857 public:
858 StampedVector() : stamp_(0) {}
859 const std::vector<T>& Values(Solver* solver) {
860 CheckStamp(solver);
861 return values_;
862 }
863 void PushBack(Solver* solver, const T& value) {
864 CheckStamp(solver);
865 values_.push_back(value);
866 }
867 void Clear(Solver* solver) {
868 values_.clear();
869 stamp_ = solver->fail_stamp();
870 }
871
872 private:
873 void CheckStamp(Solver* solver) {
874 if (solver->fail_stamp() > stamp_) {
875 Clear(solver);
876 }
877 }
878
879 std::vector<T> values_;
880 uint64_t stamp_;
881};
882} // namespace
883
884class DelayedPathCumul : public Constraint {
885 public:
886 DelayedPathCumul(Solver* const solver, const std::vector<IntVar*>& nexts,
887 const std::vector<IntVar*>& active,
888 const std::vector<IntVar*>& cumuls,
889 const std::vector<IntVar*>& transits)
890 : Constraint(solver),
891 nexts_(nexts),
892 active_(active),
893 cumuls_(cumuls),
894 transits_(transits),
895 cumul_transit_demons_(cumuls.size(), nullptr),
896 path_demon_(nullptr),
897 touched_(),
898 chain_starts_(cumuls.size(), -1),
899 chain_ends_(cumuls.size(), -1),
900 is_chain_start_(cumuls.size(), false),
901 prevs_(cumuls.size(), -1),
902 supports_(nexts.size()),
903 was_bound_(nexts.size(), false),
904 has_cumul_demon_(cumuls.size(), false) {
905 for (int64_t i = 0; i < cumuls_.size(); ++i) {
906 cumul_transit_demons_[i] = MakeDelayedConstraintDemon1(
907 solver, this, &DelayedPathCumul::CumulRange, "CumulRange", i);
908 chain_starts_[i] = i;
909 chain_ends_[i] = i;
910 }
911 path_demon_ = MakeDelayedConstraintDemon0(
912 solver, this, &DelayedPathCumul::PropagatePaths, "PropagatePaths");
913 for (int i = 0; i < nexts_.size(); ++i) {
914 supports_[i] = -1;
915 }
916 }
917 ~DelayedPathCumul() override {}
918 void Post() override {
919 solver()->RegisterDemon(path_demon_);
920 for (int i = 0; i < nexts_.size(); ++i) {
921 if (!nexts_[i]->Bound()) {
922 Demon* const demon = MakeConstraintDemon1(
923 solver(), this, &DelayedPathCumul::NextBound, "NextBound", i);
924 nexts_[i]->WhenBound(demon);
925 }
926 }
927 for (int i = 0; i < active_.size(); ++i) {
928 if (!active_[i]->Bound()) {
929 Demon* const demon = MakeConstraintDemon1(
930 solver(), this, &DelayedPathCumul::ActiveBound, "ActiveBound", i);
931 active_[i]->WhenBound(demon);
932 }
933 }
934 }
935 void InitialPropagate() override {
936 touched_.Clear(solver());
937 for (int i = 0; i < nexts_.size(); ++i) {
938 if (nexts_[i]->Bound()) {
939 NextBound(i);
940 }
941 }
942 for (int i = 0; i < active_.size(); ++i) {
943 if (active_[i]->Bound()) {
944 ActiveBound(i);
945 }
946 }
947 }
948 // TODO(user): Merge NextBound and ActiveBound to re-use the same demon
949 // for next and active variables.
950 void NextBound(int index) {
951 if (active_[index]->Min() > 0) {
952 const int next = nexts_[index]->Min();
953 PropagateLink(index, next);
954 touched_.PushBack(solver(), index);
955 EnqueueDelayedDemon(path_demon_);
956 }
957 }
958 void ActiveBound(int index) {
959 if (nexts_[index]->Bound()) {
960 NextBound(index);
961 }
962 }
963 void PropagatePaths() {
964 // Detecting new chains.
965 const std::vector<int>& touched_values = touched_.Values(solver());
966 for (const int touched : touched_values) {
967 chain_starts_[touched] = touched;
968 chain_ends_[touched] = touched;
969 is_chain_start_[touched] = false;
970 const int next = nexts_[touched]->Min();
971 chain_starts_[next] = next;
972 chain_ends_[next] = next;
973 is_chain_start_[next] = false;
974 }
975 for (const int touched : touched_values) {
976 if (touched >= nexts_.size()) continue;
977 IntVar* const next_var = nexts_[touched];
978 if (!was_bound_[touched] && next_var->Bound() &&
979 active_[touched]->Min() > 0) {
980 const int64_t next = next_var->Min();
981 was_bound_.SetValue(solver(), touched, true);
982 chain_starts_[chain_ends_[next]] = chain_starts_[touched];
983 chain_ends_[chain_starts_[touched]] = chain_ends_[next];
984 is_chain_start_[next] = false;
985 is_chain_start_[chain_starts_[touched]] = true;
986 }
987 }
988 // Propagating new chains.
989 for (const int touched : touched_values) {
990 // Is touched the start of a chain ?
991 if (is_chain_start_[touched]) {
992 // Propagate min cumuls from chain_starts[touch] to chain_ends_[touch].
993 int64_t current = touched;
994 int64_t next = nexts_[current]->Min();
995 while (current != chain_ends_[touched]) {
996 prevs_.SetValue(solver(), next, current);
997 PropagateLink(current, next);
998 current = next;
999 if (current != chain_ends_[touched]) {
1000 next = nexts_[current]->Min();
1001 }
1002 }
1003 // Propagate max cumuls from chain_ends_[i] to chain_starts_[i].
1004 int64_t prev = prevs_[current];
1005 while (current != touched) {
1006 PropagateLink(prev, current);
1007 current = prev;
1008 if (current != touched) {
1009 prev = prevs_[current];
1010 }
1011 }
1012 // Now that the chain has been propagated in both directions, adding
1013 // demons for the corresponding cumul and transit variables for
1014 // future changes in their range.
1015 current = touched;
1016 while (current != chain_ends_[touched]) {
1017 if (!has_cumul_demon_[current]) {
1018 Demon* const demon = cumul_transit_demons_[current];
1019 cumuls_[current]->WhenRange(demon);
1020 transits_[current]->WhenRange(demon);
1021 has_cumul_demon_.SetValue(solver(), current, true);
1022 }
1023 current = nexts_[current]->Min();
1024 }
1025 if (!has_cumul_demon_[current]) {
1026 Demon* const demon = cumul_transit_demons_[current];
1027 cumuls_[current]->WhenRange(demon);
1028 if (current < transits_.size()) {
1029 transits_[current]->WhenRange(demon);
1030 UpdateSupport(current);
1031 }
1032 has_cumul_demon_.SetValue(solver(), current, true);
1033 }
1034 }
1035 }
1036 touched_.Clear(solver());
1037 }
1038
1039 void Accept(ModelVisitor* const visitor) const override {
1040 visitor->BeginVisitConstraint(ModelVisitor::kDelayedPathCumul, this);
1041 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
1042 nexts_);
1043 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
1044 active_);
1045 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
1046 cumuls_);
1047 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kTransitsArgument,
1048 transits_);
1049 visitor->EndVisitConstraint(ModelVisitor::kDelayedPathCumul, this);
1050 }
1051
1052 std::string DebugString() const override {
1053 std::string out = "DelayedPathCumul(";
1054 for (int i = 0; i < nexts_.size(); ++i) {
1055 out += nexts_[i]->DebugString() + " " + cumuls_[i]->DebugString();
1056 }
1057 out += ")";
1058 return out;
1059 }
1060
1061 private:
1062 void CumulRange(int64_t index) {
1063 if (index < nexts_.size()) {
1064 if (nexts_[index]->Bound()) {
1065 if (active_[index]->Min() > 0) {
1066 PropagateLink(index, nexts_[index]->Min());
1067 }
1068 } else {
1069 UpdateSupport(index);
1070 }
1071 }
1072 if (prevs_[index] >= 0) {
1073 PropagateLink(prevs_[index], index);
1074 } else {
1075 for (int i = 0; i < nexts_.size(); ++i) {
1076 if (index == supports_[i]) {
1077 UpdateSupport(i);
1078 }
1079 }
1080 }
1081 }
1082 void UpdateSupport(int index) {
1083 int support = supports_[index];
1084 if (support < 0 || !AcceptLink(index, support)) {
1085 IntVar* const next = nexts_[index];
1086 for (int i = next->Min(); i <= next->Max(); ++i) {
1087 if (i != support && AcceptLink(index, i)) {
1088 supports_[index] = i;
1089 return;
1090 }
1091 }
1092 active_[index]->SetMax(0);
1093 }
1094 }
1095 void PropagateLink(int64_t index, int64_t next) {
1096 IntVar* const cumul_var = cumuls_[index];
1097 IntVar* const next_cumul_var = cumuls_[next];
1098 IntVar* const transit = transits_[index];
1099 const int64_t transit_min = transit->Min();
1100 const int64_t transit_max = transit->Max();
1101 next_cumul_var->SetMin(CapAdd(cumul_var->Min(), transit_min));
1102 next_cumul_var->SetMax(CapAdd(cumul_var->Max(), transit_max));
1103 const int64_t next_cumul_min = next_cumul_var->Min();
1104 const int64_t next_cumul_max = next_cumul_var->Max();
1105 cumul_var->SetMin(CapSub(next_cumul_min, transit_max));
1106 cumul_var->SetMax(CapSub(next_cumul_max, transit_min));
1107 transit->SetMin(CapSub(next_cumul_min, cumul_var->Max()));
1108 transit->SetMax(CapSub(next_cumul_max, cumul_var->Min()));
1109 }
1110 bool AcceptLink(int index, int next) const {
1111 IntVar* const cumul_var = cumuls_[index];
1112 IntVar* const next_cumul_var = cumuls_[next];
1113 IntVar* const transit = transits_[index];
1114 return transit->Min() <= CapSub(next_cumul_var->Max(), cumul_var->Min()) &&
1115 CapSub(next_cumul_var->Min(), cumul_var->Max()) <= transit->Max();
1116 }
1117
1118 const std::vector<IntVar*> nexts_;
1119 const std::vector<IntVar*> active_;
1120 const std::vector<IntVar*> cumuls_;
1121 const std::vector<IntVar*> transits_;
1122 std::vector<Demon*> cumul_transit_demons_;
1123 Demon* path_demon_;
1124 StampedVector<int> touched_;
1125 std::vector<int64_t> chain_starts_;
1126 std::vector<int64_t> chain_ends_;
1127 std::vector<bool> is_chain_start_;
1128 RevArray<int> prevs_;
1129 std::vector<int> supports_;
1130 RevArray<bool> was_bound_;
1131 RevArray<bool> has_cumul_demon_;
1132};
1133
1134// cumuls[next[i]] = cumuls[i] + transit_evaluator(i, next[i])
1135
1136class IndexEvaluator2PathCumul : public BasePathCumul {
1137 public:
1138 IndexEvaluator2PathCumul(Solver* s, const std::vector<IntVar*>& nexts,
1139 const std::vector<IntVar*>& active,
1140 const std::vector<IntVar*>& cumuls,
1141 Solver::IndexEvaluator2 transit_evaluator);
1142 ~IndexEvaluator2PathCumul() override {}
1143 void NextBound(int index) override;
1144 bool AcceptLink(int i, int j) const override;
1145
1146 void Accept(ModelVisitor* const visitor) const override {
1147 visitor->BeginVisitConstraint(ModelVisitor::kPathCumul, this);
1148 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
1149 nexts_);
1150 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
1151 active_);
1152 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
1153 cumuls_);
1154 // TODO(user): Visit transit correctly.
1155 // visitor->VisitIntegerVariableArrayArgument(
1156 // ModelVisitor::kTransitsArgument,
1157 // transit_evaluator);
1158 visitor->EndVisitConstraint(ModelVisitor::kPathCumul, this);
1159 }
1160
1161 private:
1162 Solver::IndexEvaluator2 transits_evaluator_;
1163};
1164
1165IndexEvaluator2PathCumul::IndexEvaluator2PathCumul(
1166 Solver* const s, const std::vector<IntVar*>& nexts,
1167 const std::vector<IntVar*>& active, const std::vector<IntVar*>& cumuls,
1168 Solver::IndexEvaluator2 transit_evaluator)
1169 : BasePathCumul(s, nexts, active, cumuls),
1170 transits_evaluator_(std::move(transit_evaluator)) {}
1171
1172void IndexEvaluator2PathCumul::NextBound(int index) {
1173 if (active_[index]->Min() == 0) return;
1174 const int64_t next = nexts_[index]->Value();
1175 IntVar* cumul = cumuls_[index];
1176 IntVar* cumul_next = cumuls_[next];
1177 const int64_t transit = transits_evaluator_(index, next);
1178 cumul_next->SetMin(cumul->Min() + transit);
1179 cumul_next->SetMax(CapAdd(cumul->Max(), transit));
1180 cumul->SetMin(CapSub(cumul_next->Min(), transit));
1181 cumul->SetMax(CapSub(cumul_next->Max(), transit));
1182 if (prevs_[next] < 0) {
1183 prevs_.SetValue(solver(), next, index);
1184 }
1185}
1186
1187bool IndexEvaluator2PathCumul::AcceptLink(int i, int j) const {
1188 const IntVar* const cumul_i = cumuls_[i];
1189 const IntVar* const cumul_j = cumuls_[j];
1190 const int64_t transit = transits_evaluator_(i, j);
1191 return transit <= CapSub(cumul_j->Max(), cumul_i->Min()) &&
1192 CapSub(cumul_j->Min(), cumul_i->Max()) <= transit;
1193}
1194
1195// ----- ResulatCallback2SlackPathCumul -----
1196
1197class IndexEvaluator2SlackPathCumul : public BasePathCumul {
1198 public:
1199 IndexEvaluator2SlackPathCumul(Solver* s, const std::vector<IntVar*>& nexts,
1200 const std::vector<IntVar*>& active,
1201 const std::vector<IntVar*>& cumuls,
1202 const std::vector<IntVar*>& slacks,
1203 Solver::IndexEvaluator2 transit_evaluator);
1204 ~IndexEvaluator2SlackPathCumul() override {}
1205 void Post() override;
1206 void NextBound(int index) override;
1207 bool AcceptLink(int i, int j) const override;
1208 void SlackRange(int index);
1209
1210 void Accept(ModelVisitor* const visitor) const override {
1211 visitor->BeginVisitConstraint(ModelVisitor::kPathCumul, this);
1212 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
1213 nexts_);
1214 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
1215 active_);
1216 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
1217 cumuls_);
1218 // TODO(user): Visit transit correctly.
1219 // visitor->VisitIntegerVariableArrayArgument(
1220 // ModelVisitor::kTransitsArgument,
1221 // transit_evaluator);
1222 visitor->EndVisitConstraint(ModelVisitor::kPathCumul, this);
1223 }
1224
1225 private:
1226 const std::vector<IntVar*> slacks_;
1227 Solver::IndexEvaluator2 transits_evaluator_;
1228};
1229
1230IndexEvaluator2SlackPathCumul::IndexEvaluator2SlackPathCumul(
1231 Solver* const s, const std::vector<IntVar*>& nexts,
1232 const std::vector<IntVar*>& active, const std::vector<IntVar*>& cumuls,
1233 const std::vector<IntVar*>& slacks,
1234 Solver::IndexEvaluator2 transit_evaluator)
1235 : BasePathCumul(s, nexts, active, cumuls),
1236 slacks_(slacks),
1237 transits_evaluator_(std::move(transit_evaluator)) {}
1238
1239void IndexEvaluator2SlackPathCumul::Post() {
1240 BasePathCumul::Post();
1241 for (int i = 0; i < size(); ++i) {
1242 Demon* slack_demon = MakeConstraintDemon1(
1243 solver(), this, &IndexEvaluator2SlackPathCumul::SlackRange,
1244 "SlackRange", i);
1245 slacks_[i]->WhenRange(slack_demon);
1246 }
1247}
1248
1249void IndexEvaluator2SlackPathCumul::SlackRange(int index) {
1250 if (nexts_[index]->Bound()) {
1251 NextBound(index);
1252 } else {
1253 UpdateSupport(index);
1254 }
1255 if (prevs_[index] >= 0) {
1256 NextBound(prevs_[index]);
1257 } else {
1258 for (int i = 0; i < size(); ++i) {
1259 if (index == supports_[i]) {
1260 UpdateSupport(i);
1261 }
1262 }
1263 }
1264}
1265
1266void IndexEvaluator2SlackPathCumul::NextBound(int index) {
1267 if (active_[index]->Min() == 0) return;
1268 const int64_t next = nexts_[index]->Value();
1269 IntVar* const cumul = cumuls_[index];
1270 IntVar* const cumul_next = cumuls_[next];
1271 IntVar* const slack = slacks_[index];
1272 const int64_t transit = transits_evaluator_(index, next);
1273 const int64_t cumul_next_minus_transit_min =
1274 CapSub(cumul_next->Min(), transit);
1275 const int64_t cumul_next_minus_transit_max =
1276 CapSub(cumul_next->Max(), transit);
1277 cumul_next->SetMin(CapAdd(CapAdd(cumul->Min(), transit), slack->Min()));
1278 cumul_next->SetMax(CapAdd(CapAdd(cumul->Max(), transit), slack->Max()));
1279 cumul->SetMin(CapSub(cumul_next_minus_transit_min, slack->Max()));
1280 cumul->SetMax(CapSub(cumul_next_minus_transit_max, slack->Min()));
1281 slack->SetMin(CapSub(cumul_next_minus_transit_min, cumul->Max()));
1282 slack->SetMax(CapSub(cumul_next_minus_transit_max, cumul->Min()));
1283 if (prevs_[next] < 0) {
1284 prevs_.SetValue(solver(), next, index);
1285 }
1286}
1287
1288bool IndexEvaluator2SlackPathCumul::AcceptLink(int i, int j) const {
1289 const IntVar* const cumul_i = cumuls_[i];
1290 const IntVar* const cumul_j = cumuls_[j];
1291 const IntVar* const slack = slacks_[i];
1292 const int64_t transit = transits_evaluator_(i, j);
1293 return CapAdd(transit, slack->Min()) <=
1294 CapSub(cumul_j->Max(), cumul_i->Min()) &&
1295 CapSub(cumul_j->Min(), cumul_i->Max()) <=
1296 CapAdd(slack->Max(), transit);
1297}
1298} // namespace
1299
1300Constraint* Solver::MakePathCumul(const std::vector<IntVar*>& nexts,
1301 const std::vector<IntVar*>& active,
1302 const std::vector<IntVar*>& cumuls,
1303 const std::vector<IntVar*>& transits) {
1304 CHECK_EQ(nexts.size(), active.size());
1305 CHECK_EQ(transits.size(), nexts.size());
1306 return RevAlloc(new PathCumul(this, nexts, active, cumuls, transits));
1307}
1308
1309Constraint* Solver::MakePathCumul(const std::vector<IntVar*>& nexts,
1310 const std::vector<IntVar*>& active,
1311 const std::vector<IntVar*>& cumuls,
1312 Solver::IndexEvaluator2 transit_evaluator) {
1313 CHECK_EQ(nexts.size(), active.size());
1314 return RevAlloc(new IndexEvaluator2PathCumul(this, nexts, active, cumuls,
1315 std::move(transit_evaluator)));
1316}
1317
1318Constraint* Solver::MakePathCumul(const std::vector<IntVar*>& nexts,
1319 const std::vector<IntVar*>& active,
1320 const std::vector<IntVar*>& cumuls,
1321 const std::vector<IntVar*>& slacks,
1322 Solver::IndexEvaluator2 transit_evaluator) {
1323 CHECK_EQ(nexts.size(), active.size());
1324 return RevAlloc(new IndexEvaluator2SlackPathCumul(
1325 this, nexts, active, cumuls, slacks, std::move(transit_evaluator)));
1326}
1327
1328Constraint* Solver::MakeDelayedPathCumul(const std::vector<IntVar*>& nexts,
1329 const std::vector<IntVar*>& active,
1330 const std::vector<IntVar*>& cumuls,
1331 const std::vector<IntVar*>& transits) {
1332 CHECK_EQ(nexts.size(), active.size());
1333 CHECK_EQ(transits.size(), nexts.size());
1334 return RevAlloc(new DelayedPathCumul(this, nexts, active, cumuls, transits));
1335}
1336
1337// Constraint enforcing that status[i] is true iff there's a path defined on
1338// next variables from sources[i] to sinks[i].
1339namespace {
1340class PathConnectedConstraint : public Constraint {
1341 public:
1342 PathConnectedConstraint(Solver* solver, std::vector<IntVar*> nexts,
1343 absl::Span<const int64_t> sources,
1344 std::vector<int64_t> sinks,
1345 std::vector<IntVar*> status)
1346 : Constraint(solver),
1347 sources_(sources.size(), -1),
1348 index_to_path_(nexts.size(), -1),
1349 sinks_(std::move(sinks)),
1350 nexts_(std::move(nexts)),
1351 status_(std::move(status)),
1352 touched_(nexts_.size()) {
1353 CHECK_EQ(status_.size(), sources_.size());
1354 CHECK_EQ(status_.size(), sinks_.size());
1355 for (int i = 0; i < status_.size(); ++i) {
1356 const int64_t source = sources[i];
1357 sources_.SetValue(solver, i, source);
1358 if (source < index_to_path_.size()) {
1359 index_to_path_.SetValue(solver, source, i);
1360 }
1361 }
1362 }
1363 void Post() override {
1364 for (int i = 0; i < nexts_.size(); ++i) {
1365 nexts_[i]->WhenBound(MakeConstraintDemon1(
1366 solver(), this, &PathConnectedConstraint::NextBound, "NextValue", i));
1367 }
1368 for (int i = 0; i < status_.size(); ++i) {
1369 if (sources_[i] < nexts_.size()) {
1370 status_[i]->SetRange(0, 1);
1371 } else {
1372 status_[i]->SetValue(0);
1373 }
1374 }
1375 }
1376 void InitialPropagate() override {
1377 for (int i = 0; i < status_.size(); ++i) {
1378 EvaluatePath(i);
1379 }
1380 }
1381 std::string DebugString() const override {
1382 std::string output = "PathConnected(";
1383 std::vector<std::string> elements;
1384 for (IntVar* const next : nexts_) {
1385 elements.push_back(next->DebugString());
1386 }
1387 for (int i = 0; i < sources_.size(); ++i) {
1388 elements.push_back(absl::StrCat(sources_[i]));
1389 }
1390 for (int64_t sink : sinks_) {
1391 elements.push_back(absl::StrCat(sink));
1392 }
1393 for (IntVar* const status : status_) {
1394 elements.push_back(status->DebugString());
1395 }
1396 output += absl::StrJoin(elements, ",") + ")";
1397 return output;
1398 }
1399
1400 private:
1401 void NextBound(int index) {
1402 const int path = index_to_path_[index];
1403 if (path >= 0) {
1404 EvaluatePath(path);
1405 }
1406 }
1407 void EvaluatePath(int path) {
1408 touched_.SparseClearAll();
1409 int64_t source = sources_[path];
1410 const int64_t end = sinks_[path];
1411 while (source != end) {
1412 if (source >= nexts_.size() || touched_[source]) {
1413 status_[path]->SetValue(0);
1414 return;
1415 }
1416 touched_.Set(source);
1417 IntVar* const next = nexts_[source];
1418 if (next->Bound()) {
1419 source = next->Min();
1420 } else {
1421 sources_.SetValue(solver(), path, source);
1422 index_to_path_.SetValue(solver(), source, path);
1423 return;
1424 }
1425 }
1426 status_[path]->SetValue(1);
1427 }
1428
1429 RevArray<int64_t> sources_;
1430 RevArray<int> index_to_path_;
1431 const std::vector<int64_t> sinks_;
1432 const std::vector<IntVar*> nexts_;
1433 const std::vector<IntVar*> status_;
1434 SparseBitset<int64_t> touched_;
1435};
1436} // namespace
1437
1438Constraint* Solver::MakePathConnected(std::vector<IntVar*> nexts,
1439 std::vector<int64_t> sources,
1440 std::vector<int64_t> sinks,
1441 std::vector<IntVar*> status) {
1442 return RevAlloc(new PathConnectedConstraint(
1443 this, std::move(nexts), sources, std::move(sinks), std::move(status)));
1444}
1445
1446namespace {
1447class PathTransitPrecedenceConstraint : public Constraint {
1448 public:
1449 enum PrecedenceType {
1450 ANY,
1451 LIFO,
1452 FIFO,
1453 };
1454 PathTransitPrecedenceConstraint(
1455 Solver* solver, std::vector<IntVar*> nexts, std::vector<IntVar*> transits,
1456 absl::Span<const std::pair<int, int>> precedences,
1457 absl::flat_hash_map<int, PrecedenceType> precedence_types)
1458 : Constraint(solver),
1459 nexts_(std::move(nexts)),
1460 transits_(std::move(transits)),
1461 predecessors_(nexts_.size()),
1462 successors_(nexts_.size()),
1463 precedence_types_(std::move(precedence_types)),
1464 starts_(nexts_.size(), -1),
1465 ends_(nexts_.size(), -1),
1466 transit_cumuls_(nexts_.size(), 0) {
1467 for (int i = 0; i < nexts_.size(); ++i) {
1468 starts_.SetValue(solver, i, i);
1469 ends_.SetValue(solver, i, i);
1470 }
1471 for (const auto& precedence : precedences) {
1472 if (precedence.second < nexts_.size()) {
1473 predecessors_[precedence.second].push_back(precedence.first);
1474 }
1475 if (precedence.first < nexts_.size()) {
1476 successors_[precedence.first].push_back(precedence.second);
1477 }
1478 }
1479 }
1480 ~PathTransitPrecedenceConstraint() override {}
1481 void Post() override {
1482 for (int i = 0; i < nexts_.size(); ++i) {
1483 nexts_[i]->WhenBound(MakeDelayedConstraintDemon1(
1484 solver(), this, &PathTransitPrecedenceConstraint::NextBound,
1485 "NextBound", i));
1486 }
1487 for (int i = 0; i < transits_.size(); ++i) {
1488 transits_[i]->WhenRange(MakeDelayedConstraintDemon1(
1489 solver(), this, &PathTransitPrecedenceConstraint::NextBound,
1490 "TransitRange", i));
1491 }
1492 }
1493 void InitialPropagate() override {
1494 for (int i = 0; i < nexts_.size(); ++i) {
1495 if (nexts_[i]->Bound()) {
1496 NextBound(i);
1497 }
1498 }
1499 }
1500 std::string DebugString() const override {
1501 std::string output = "PathPrecedence(";
1502 std::vector<std::string> elements = {JoinDebugStringPtr(nexts_, ",")};
1503 if (!transits_.empty()) {
1504 elements.push_back(JoinDebugStringPtr(transits_, ","));
1505 }
1506 for (int i = 0; i < predecessors_.size(); ++i) {
1507 for (const int predecessor : predecessors_[i]) {
1508 elements.push_back(absl::StrCat("(", predecessor, ", ", i, ")"));
1509 }
1510 }
1511 output += absl::StrJoin(elements, ",") + ")";
1512 return output;
1513 }
1514 void Accept(ModelVisitor* const visitor) const override {
1515 // TODO(user): Implement.
1516 }
1517
1518 private:
1519 void NextBound(int index) {
1520 if (!nexts_[index]->Bound()) return;
1521 const int next = nexts_[index]->Min();
1522 const int start = starts_[index];
1523 const int end = (next < nexts_.size()) ? ends_[next] : next;
1524 if (end < nexts_.size()) starts_.SetValue(solver(), end, start);
1525 ends_.SetValue(solver(), start, end);
1526 int current = start;
1527 PrecedenceType type = ANY;
1528 auto it = precedence_types_.find(start);
1529 if (it != precedence_types_.end()) {
1530 type = it->second;
1531 }
1532 forbidden_.clear();
1533 marked_.clear();
1534 pushed_.clear();
1535 int64_t transit_cumul = 0;
1536 const bool has_transits = !transits_.empty();
1537 while (current < nexts_.size() && current != end) {
1538 transit_cumuls_[current] = transit_cumul;
1539 marked_.insert(current);
1540 // If current has predecessors and we are in LIFO/FIFO mode.
1541 if (!predecessors_[current].empty() && !pushed_.empty()) {
1542 bool found = false;
1543 // One of the predecessors must be at the top of the stack.
1544 for (const int predecessor : predecessors_[current]) {
1545 if (pushed_.back() == predecessor) {
1546 found = true;
1547 break;
1548 }
1549 }
1550 if (!found) solver()->Fail();
1551 pushed_.pop_back();
1552 }
1553 if (forbidden_.find(current) != forbidden_.end()) {
1554 for (const int successor : successors_[current]) {
1555 if (marked_.find(successor) != marked_.end()) {
1556 if (!has_transits ||
1557 CapSub(transit_cumul, transit_cumuls_[successor]) > 0) {
1558 solver()->Fail();
1559 }
1560 }
1561 }
1562 }
1563 if (!successors_[current].empty()) {
1564 switch (type) {
1565 case LIFO:
1566 pushed_.push_back(current);
1567 break;
1568 case FIFO:
1569 pushed_.push_front(current);
1570 break;
1571 case ANY:
1572 break;
1573 }
1574 }
1575 for (const int predecessor : predecessors_[current]) {
1576 forbidden_.insert(predecessor);
1577 }
1578 if (has_transits) {
1579 transit_cumul = CapAdd(transit_cumul, transits_[current]->Min());
1580 }
1581 current = nexts_[current]->Min();
1582 }
1583 if (forbidden_.find(current) != forbidden_.end()) {
1584 for (const int successor : successors_[current]) {
1585 if (marked_.find(successor) != marked_.end()) {
1586 if (!has_transits ||
1587 CapSub(transit_cumul, transit_cumuls_[successor]) > 0) {
1588 solver()->Fail();
1589 }
1590 }
1591 }
1592 }
1593 }
1594
1595 const std::vector<IntVar*> nexts_;
1596 const std::vector<IntVar*> transits_;
1597 std::vector<std::vector<int>> predecessors_;
1598 std::vector<std::vector<int>> successors_;
1599 const absl::flat_hash_map<int, PrecedenceType> precedence_types_;
1600 RevArray<int> starts_;
1601 RevArray<int> ends_;
1602 absl::flat_hash_set<int> forbidden_;
1603 absl::flat_hash_set<int> marked_;
1604 std::deque<int> pushed_;
1605 std::vector<int64_t> transit_cumuls_;
1606};
1607
1608Constraint* MakePathTransitTypedPrecedenceConstraint(
1609 Solver* solver, std::vector<IntVar*> nexts, std::vector<IntVar*> transits,
1610 absl::Span<const std::pair<int, int>> precedences,
1611 absl::flat_hash_map<int, PathTransitPrecedenceConstraint::PrecedenceType>
1612 precedence_types) {
1613 if (precedences.empty()) {
1614 return solver->MakeTrueConstraint();
1615 }
1616 return solver->RevAlloc(new PathTransitPrecedenceConstraint(
1617 solver, std::move(nexts), std::move(transits), precedences,
1618 std::move(precedence_types)));
1619}
1620
1621} // namespace
1622
1623Constraint* Solver::MakePathPrecedenceConstraint(
1624 std::vector<IntVar*> nexts,
1625 const std::vector<std::pair<int, int>>& precedences) {
1626 return MakePathTransitPrecedenceConstraint(std::move(nexts), {}, precedences);
1627}
1628
1629Constraint* Solver::MakePathPrecedenceConstraint(
1630 std::vector<IntVar*> nexts,
1631 const std::vector<std::pair<int, int>>& precedences,
1632 absl::Span<const int> lifo_path_starts,
1633 absl::Span<const int> fifo_path_starts) {
1634 absl::flat_hash_map<int, PathTransitPrecedenceConstraint::PrecedenceType>
1635 precedence_types;
1636 for (int start : lifo_path_starts) {
1637 precedence_types[start] = PathTransitPrecedenceConstraint::LIFO;
1638 }
1639 for (int start : fifo_path_starts) {
1640 precedence_types[start] = PathTransitPrecedenceConstraint::FIFO;
1641 }
1642 return MakePathTransitTypedPrecedenceConstraint(
1643 this, std::move(nexts), {}, precedences, std::move(precedence_types));
1644}
1645
1646Constraint* Solver::MakePathTransitPrecedenceConstraint(
1647 std::vector<IntVar*> nexts, std::vector<IntVar*> transits,
1648 const std::vector<std::pair<int, int>>& precedences) {
1649 return MakePathTransitTypedPrecedenceConstraint(
1650 this, std::move(nexts), std::move(transits), precedences, {{}});
1651}
1652
1653namespace {
1654
1655class PathEnergyCostConstraint : public Constraint {
1656 public:
1657 PathEnergyCostConstraint(
1658 Solver* solver,
1660 : Constraint(solver), specification_(std::move(specification)) {
1661 const int num_nexts = specification_.nexts.size();
1662 DCHECK_EQ(num_nexts, specification_.distances.size());
1663
1664 const int num_paths = specification_.path_unit_costs.size();
1665 DCHECK_EQ(num_paths, specification_.costs.size());
1666 DCHECK_EQ(num_paths, specification_.path_used_when_empty.size());
1667 DCHECK_EQ(num_paths, specification_.path_starts.size());
1668 DCHECK_EQ(num_paths, specification_.path_ends.size());
1669
1670 const int num_nodes = specification_.forces.size();
1671 DCHECK_EQ(num_nodes, specification_.paths.size());
1672 }
1673
1674 void Post() override;
1675 void InitialPropagate() override;
1676
1677 private:
1678 void NodeDispatcher(int node);
1679 void PropagatePath(int path);
1680 Solver::PathEnergyCostConstraintSpecification specification_;
1681 std::vector<Demon*> path_demons_;
1682};
1683
1684// This constraint avoids unnecessary work by grouping per-path calls
1685// after all per-node calls have triggered.
1686void PathEnergyCostConstraint::Post() {
1687 // path demons are delayed, to be run after all node demons.
1688 path_demons_.resize(specification_.path_unit_costs.size(), nullptr);
1689 for (int path = 0; path < specification_.path_unit_costs.size(); ++path) {
1690 // If unit cost is 0, initial propagation takes care of setting the cost
1691 // to 0, no need to register a demon.
1692 if (specification_.path_unit_costs[path] == 0) continue;
1693 path_demons_[path] = MakeDelayedConstraintDemon1(
1694 solver(), this, &PathEnergyCostConstraint::PropagatePath,
1695 "PropagatePath", path);
1696 }
1697 // Node demons are not delayed, so they are all called before path demons.
1698 const int num_nodes = specification_.forces.size();
1699 const int num_nexts = specification_.nexts.size();
1700 for (int node = 0; node < num_nodes; ++node) {
1701 auto* demon = MakeConstraintDemon1(
1702 solver(), this, &PathEnergyCostConstraint::NodeDispatcher,
1703 "NodeDispatcher", node);
1704 specification_.forces[node]->WhenRange(demon);
1705 specification_.paths[node]->WhenBound(demon);
1706 if (node < num_nexts) {
1707 specification_.nexts[node]->WhenBound(demon);
1708 specification_.distances[node]->WhenRange(demon);
1709 }
1710 }
1711}
1712
1713void PathEnergyCostConstraint::InitialPropagate() {
1714 const int num_paths = specification_.path_unit_costs.size();
1715 for (int path = 0; path < num_paths; ++path) {
1716 if (specification_.path_unit_costs[path] == 0) {
1717 specification_.costs[path]->SetValue(0);
1718 } else {
1719 PropagatePath(path);
1720 }
1721 }
1722}
1723
1724// When a node has its path decided, schedule path propagation.
1725void PathEnergyCostConstraint::NodeDispatcher(int node) {
1726 if (!specification_.paths[node]->Bound()) return;
1727 const int path = specification_.paths[node]->Min();
1728 if (path < 0) return;
1729 if (path_demons_[path] == nullptr) return;
1730 EnqueueDelayedDemon(path_demons_[path]);
1731}
1732
1733// Propagate only forces, distance -> energy.
1734void PathEnergyCostConstraint::PropagatePath(int path) {
1735 // Compute energy along mandatory path.
1736 int64_t energy_min = 0;
1737 int64_t energy_max = 0;
1738 int current = specification_.path_starts[path];
1739 const int num_nodes = specification_.nexts.size();
1740 int num_nonend_nodes = 0;
1741 while (current < num_nodes) {
1742 ++num_nonend_nodes;
1743 if (!specification_.nexts[current]->Bound()) break;
1744 const int next = specification_.nexts[current]->Min();
1745 // Energy += force * distance.
1746 const int64_t distance_min = specification_.distances[current]->Min();
1747 const int64_t distance_max = specification_.distances[current]->Max();
1748 DCHECK_GE(distance_min, 0); // Bounds are correct when distance is >= 0.
1749 energy_min = CapAdd(
1750 energy_min, CapProd(specification_.forces[next]->Min(), distance_min));
1751 energy_max = CapAdd(
1752 energy_max, CapProd(specification_.forces[next]->Max(), distance_max));
1753 current = next;
1754 }
1755 // TODO(user): test more incremental propagation. Energy can be computed
1756 // as unordered sum of force[next] * distance[node], it can be done without
1757 // browsing the whole path at every trigger.
1758 if (current == specification_.path_ends[path]) {
1759 if (num_nonend_nodes == 1 && !specification_.path_used_when_empty[path]) {
1760 specification_.costs[path]->SetValue(0);
1761 } else {
1762 specification_.costs[path]->SetRange(
1763 CapProd(energy_min, specification_.path_unit_costs[path]),
1764 CapProd(energy_max, specification_.path_unit_costs[path]));
1765 }
1766 }
1767}
1768
1769} // namespace
1770
1771Constraint* Solver::MakePathEnergyCostConstraint(
1773 return RevAlloc(new PathEnergyCostConstraint(this, std::move(specification)));
1774}
1775
1776} // namespace operations_research
static const char kActiveArgument[]
argument names:
void SetValue(Solver *const s, const T &val)
For the time being, Solver is neither MT_SAFE nor MT_HOT.
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
std::function< bool(int64_t)> IndexFilter1
Block * next
int64_t value
IntVar * var
absl::Status status
Definition g_gurobi.cc:44
const std::vector< IntVar * > cumuls_
RevArray< int > prevs_
std::vector< int > supports_
int index
std::vector< typename Map::mapped_type > Values(const Map &map, const Keys &keys)
Definition key_types.h:136
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
Definition matchers.h:453
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().
int64_t CapSub(int64_t x, int64_t y)
Demon * MakeDelayedConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
int64_t CapProd(int64_t x, int64_t y)
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
STL namespace.
std::optional< int64_t > end
int64_t start