Google OR-Tools: ortools/constraint_solver/graph_constraints.cc Source File
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"
52 NoCycle(Solver* s, const std::vector<IntVar*>& nexts,
57 void InitialPropagate() override;
58 void NextChange(int index);
59 void ActiveBound(int index);
62 void ComputeSupport(int index);
63 std::string DebugString() const override;
65 void Accept(ModelVisitor* const visitor) const override {
71 visitor->VisitIntegerArgument("assume_paths", assume_paths_);
72 visitor->VisitInt64ToBoolExtension(sink_handler_, -size(), size());
77 int64_t size() const { return nexts_.size(); }
79 const std::vector<IntVar*> nexts_;
80 const std::vector<IntVar*> active_;
81 std::vector<IntVarIterator*> iterators_;
86 std::vector<int64_t> outbound_supports_;
87 std::vector<int64_t> support_leaves_;
88 std::vector<int64_t> unsupported_;
90 std::vector<int64_t> sinks_;
94NoCycle::NoCycle(Solver* const s, const std::vector<IntVar*>& nexts,
95 const std::vector<IntVar*>& active,
100 iterators_(nexts.size(), nullptr),
101 starts_(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);
113 iterators_[i] = nexts_[i]->MakeDomainIterator(true);
117void NoCycle::InitialPropagate() {
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) {
127 for (int j = next->Max(); j >= size(); --j) {
133 solver()->SaveAndSetValue(&all_nexts_bound_, true);
134 for (int i = 0; i < size(); ++i) {
138 solver()->SaveAndSetValue(&all_nexts_bound_, false);
146 for (int i = 0; i < size(); ++i) {
147 IntVar* next = nexts_[i];
149 solver(), this, &NoCycle::NextChange, "NextChange", i);
150 next->WhenDomain(support_demon);
152 solver(), this, &NoCycle::ActiveBound, "ActiveBound", i);
153 active_[i]->WhenBound(active_demon);
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());
164 for (int i = min_min; i <= max_max; ++i) {
171void NoCycle::NextChange(int index) {
172 IntVar* const next_var = nexts_[index];
177 bool all_nexts_bound = true;
178 for (int i = 0; i < size(); ++i) {
179 if (!nexts_[i]->Bound()) {
184 solver()->SaveAndSetValue(&all_nexts_bound_, all_nexts_bound);
189 if (!next_var->Contains(outbound_supports_[index])) {
194void NoCycle::ActiveBound(int index) {
195 if (nexts_[index]->Bound()) {
200void NoCycle::NextBound(int index) {
201 if (active_[index]->Min() == 0) return;
202 if (marked_[index]) return;
203 Solver* const s = solver();
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);
216 for (int i = 0; i < size(); ++i) {
217 int64_t current = i;
218 bool found = (current == chain_end);
221 while (!found && count < size() && !sink_handler_(current) &&
222 nexts_[current]->Bound()) {
223 current = nexts_[current]->Value();
224 found = (current == chain_end);
228 nexts_[chain_end]->RemoveValue(i);
244void NoCycle::ComputeSupports() {
252 const int sink_size = sinks_.size();
253 for (int i = 0; i < size(); ++i) {
254 const IntVar* next = nexts_[i];
256 if (active_[i]->Max() != 0) {
257 const int64_t current_support = outbound_supports_[i];
260 if (current_support >= 0 && sink_handler_(current_support) &&
261 next->Contains(current_support)) {
262 support_leaves_.push_back(i);
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);
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);
285 if (outbound_supports_[i] == -1) {
286 unsupported_.push_back(i);
294 size_t leaves_end = support_leaves_.size();
295 while (!unsupported_.empty()) {
297 for (int64_t unsupported_index = 0; unsupported_index < unsupported_.size();
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);
306 unsupported_[unsupported_index] = unsupported_.back();
316 if (leaves_end == support_leaves_.size()) {
319 leaves_begin = leaves_end;
320 leaves_end = support_leaves_.size();
323 for (int64_t unsupported_index = 0; unsupported_index < unsupported_.size();
325 active_[unsupported_[unsupported_index]]->SetMax(0);
329void NoCycle::ComputeSupport(int index) {
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;
338 if (next != index && next < outbound_supports_.size()) {
339 int64_t next_support = outbound_supports_[next];
342 bool ancestor_found = false;
343 while (next_support < outbound_supports_.size() &&
344 !sink_handler_(next_support)) {
345 if (next_support == index) {
349 next_support = outbound_supports_[next_support];
352 outbound_supports_[index] = next;
363std::string NoCycle::DebugString() const {
364 return absl::StrFormat("NoCycle(%s)", JoinDebugStringPtr(nexts_, ", "));
371 Circuit(Solver* const s, const std::vector<IntVar*>& nexts, bool sub_circuit)
379 outbound_support_(size_, -1),
380 inbound_support_(size_, -1),
386 sub_circuit_(sub_circuit) {
387 for (int i = 0; i < size_; ++i) {
388 domains_[i] = nexts_[i]->MakeDomainIterator(true);
396 solver(), this, &Circuit::CheckReachabilityToRoot,
397 "CheckReachabilityToRoot");
399 solver(), this, &Circuit::CheckReachabilityFromRoot,
400 "CheckReachabilityFromRoot");
401 for (int i = 0; i < size_; ++i) {
402 if (!nexts_[i]->Bound()) {
404 solver(), this, &Circuit::NextBound, "NextBound", i);
405 nexts_[i]->WhenBound(bound_demon);
407 solver(), this, &Circuit::NextDomain, "NextDomain", i);
408 nexts_[i]->WhenDomain(domain_demon);
411 solver()->AddConstraint(solver()->MakeAllDifferent(nexts_));
414 void InitialPropagate() override {
415 Solver* const s = solver();
417 root_.SetValue(solver(), 0);
419 for (int i = 0; i < size_; ++i) {
420 nexts_[i]->SetRange(0, size_ - 1);
422 nexts_[i]->RemoveValue(i);
425 for (int i = 0; i < size_; ++i) {
426 starts_.SetValue(s, i, i);
428 lengths_.SetValue(s, i, 1);
430 for (int i = 0; i < size_; ++i) {
435 CheckReachabilityFromRoot();
436 CheckReachabilityToRoot();
439 std::string DebugString() const override {
440 return absl::StrFormat("%sCircuit(%s)", sub_circuit_ ? "Sub" : "",
444 void Accept(ModelVisitor* const visitor) const override {
445 visitor->BeginVisitConstraint(ModelVisitor::kCircuit, this);
446 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
448 visitor->VisitIntegerArgument(ModelVisitor::kPartialArgument, sub_circuit_);
449 visitor->EndVisitConstraint(ModelVisitor::kCircuit, this);
453 bool Inactive(int index) const {
454 return nexts_[index]->Bound() && nexts_[index]->Min() == index;
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) {
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);
471 lengths_.Value(new_start) + lengths_.Value(destination));
474 nexts_[destination]->RemoveValue(destination);
476 if (lengths_.Value(new_start) < size_ - 1 - num_inactives_.Value()) {
477 nexts_[new_end]->RemoveValue(new_start);
481 num_inactives_.Incr(solver());
493 void NextDomain(int index) {
494 if (root_.Value() == -1) {
497 if (!nexts_[index]->Contains(outbound_support_[index])) {
498 EnqueueDelayedDemon(outbound_demon_);
500 if (!nexts_[index]->Contains(inbound_support_[index])) {
501 EnqueueDelayedDemon(inbound_demon_);
505 void TryInsertReached(int candidate, int64_t after) {
508 insertion_queue_.push_back(after);
509 temp_support_[candidate] = after;
513 void CheckReachabilityFromRoot() {
514 if (root_.Value() == -1) {
519 temp_support_.assign(size_, -1);
522 reached_.assign(size_, false);
525 const int root_value = root_.Value();
526 reached_[root_value] = true;
527 insertion_queue_.push_back(root_value);
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];
535 TryInsertReached(candidate, var->Min());
539 TryInsertReached(candidate, var->Min());
540 TryInsertReached(candidate, var->Max());
544 IntVarIterator* const domain = domains_[candidate];
545 for (const int64_t value : InitAndGetValues(domain)) {
546 TryInsertReached(candidate, value);
553 for (int i = 0; i < size_; ++i) {
555 nexts_[i]->SetValue(i);
559 outbound_support_.swap(temp_support_);
562 void CheckReachabilityToRoot() {
564 if (root_.Value() == -1) {
569 insertion_queue_.push_back(root_.Value());
570 temp_support_[root_.Value()] = nexts_[root_.Value()]->Min();
573 for (int i = 0; i < size_; ++i) {
574 if (!Inactive(i) && i != root_.Value()) {
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;
589 rejected.push_back(candidate);
594 for (int i = 0; i < to_visit_.size(); ++i) {
595 const int node = to_visit_[i];
596 nexts_[node]->SetValue(node);
598 temp_support_.swap(inbound_support_);
601 const std::vector<IntVar*> nexts_;
603 std::vector<int> insertion_queue_;
604 std::vector<int> to_visit_;
605 std::vector<bool> reached_;
609 std::vector<IntVarIterator*> domains_;
610 std::vector<int> outbound_support_;
611 std::vector<int> inbound_support_;
612 std::vector<int> temp_support_;
616 NumericalRev<int> num_inactives_;
622 const std::vector<IntVar*>& active,
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; };
631 new NoCycle(this, nexts, active, std::move(sink_handler), assume_paths));
635 const std::vector<IntVar*>& active,
637 return MakeNoCycle(nexts, active, std::move(sink_handler), true);
642 return RevAlloc(new Circuit(this, nexts, false));
646 return RevAlloc(new Circuit(this, nexts, true));
652class BasePathCumul : public Constraint {
654 BasePathCumul(Solver* s, const std::vector<IntVar*>& nexts,
655 const std::vector<IntVar*>& active,
656 const std::vector<IntVar*>& cumuls);
657 ~BasePathCumul() override {}
659 void InitialPropagate() override;
660 void ActiveBound(int index);
661 virtual void NextBound(int index) = 0;
662 virtual bool AcceptLink(int i, int j) const = 0;
663 void UpdateSupport(int index);
664 void CumulRange(int index);
665 std::string DebugString() const override;
668 int64_t size() const { return nexts_.size(); }
669 int cumul_size() const { return cumuls_.size(); }
671 const std::vector<IntVar*> nexts_;
672 const std::vector<IntVar*> active_;
673 const std::vector<IntVar*> cumuls_;
675 std::vector<int> supports_;
678BasePathCumul::BasePathCumul(Solver* const s, const std::vector<IntVar*>& nexts,
679 const std::vector<IntVar*>& active,
680 const std::vector<IntVar*>& cumuls)
685 prevs_(cumuls.size(), -1),
687 CHECK_GE(cumul_size(), size());
688 for (int i = 0; i < size(); ++i) {
689 supports_[i] = -1;
693void BasePathCumul::InitialPropagate() {
694 for (int i = 0; i < size(); ++i) {
703void BasePathCumul::Post() {
704 for (int i = 0; i < size(); ++i) {
705 IntVar* var = nexts_[i];
710 solver(), this, &BasePathCumul::UpdateSupport, "UpdateSupport", i);
713 solver(), this, &BasePathCumul::ActiveBound, "ActiveBound", i);
714 active_[i]->WhenBound(active_demon);
716 for (int i = 0; i < cumul_size(); ++i) {
717 IntVar* cumul = cumuls_[i];
724void BasePathCumul::ActiveBound(int index) {
725 if (nexts_[index]->Bound()) {
730void BasePathCumul::CumulRange(int index) {
732 if (nexts_[index]->Bound()) {
741 for (int i = 0; i < size(); ++i) {
742 if (index == supports_[i]) {
749void BasePathCumul::UpdateSupport(int index) {
750 int support = supports_[index];
751 if (support < 0 || !AcceptLink(index, support)) {
752 IntVar* var = nexts_[index];
753 for (int i = var->Min(); i <= var->Max(); ++i) {
754 if (i != support && AcceptLink(index, i)) {
755 supports_[index] = i;
759 active_[index]->SetMax(0);
763std::string BasePathCumul::DebugString() const {
764 std::string out = "PathCumul(";
765 for (int i = 0; i < size(); ++i) {
766 out += nexts_[i]->DebugString() + " " + cumuls_[i]->DebugString();
774class PathCumul : public BasePathCumul {
776 PathCumul(Solver* const s, const std::vector<IntVar*>& nexts,
777 const std::vector<IntVar*>& active,
778 const std::vector<IntVar*>& cumuls,
779 const std::vector<IntVar*>& transits)
780 : BasePathCumul(s, nexts, active, cumuls), transits_(transits) {}
783 void NextBound(int index) override;
784 bool AcceptLink(int i, int j) const override;
785 void TransitRange(int index);
787 void Accept(ModelVisitor* const visitor) const override {
788 visitor->BeginVisitConstraint(ModelVisitor::kPathCumul, this);
789 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
791 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
793 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
795 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kTransitsArgument,
797 visitor->EndVisitConstraint(ModelVisitor::kPathCumul, this);
801 const std::vector<IntVar*> transits_;
806 for (int i = 0; i < size(); ++i) {
808 solver(), this, &PathCumul::TransitRange, "TransitRange", i);
809 transits_[i]->WhenRange(transit_demon);
813void PathCumul::NextBound(int index) {
814 if (active_[index]->Min() == 0) return;
815 const int64_t next = nexts_[index]->Value();
816 IntVar* cumul = cumuls_[index];
817 IntVar* cumul_next = cumuls_[next];
818 IntVar* transit = transits_[index];
819 cumul_next->SetMin(cumul->Min() + transit->Min());
820 cumul_next->SetMax(CapAdd(cumul->Max(), transit->Max()));
821 cumul->SetMin(CapSub(cumul_next->Min(), transit->Max()));
822 cumul->SetMax(CapSub(cumul_next->Max(), transit->Min()));
823 transit->SetMin(CapSub(cumul_next->Min(), cumul->Max()));
824 transit->SetMax(CapSub(cumul_next->Max(), cumul->Min()));
826 prevs_.SetValue(solver(), next, index);
830void PathCumul::TransitRange(int index) {
831 if (nexts_[index]->Bound()) {
839 for (int i = 0; i < size(); ++i) {
840 if (index == supports_[i]) {
847bool PathCumul::AcceptLink(int i, int j) const {
848 const IntVar* const cumul_i = cumuls_[i];
849 const IntVar* const cumul_j = cumuls_[j];
850 const IntVar* const transit_i = transits_[i];
851 return transit_i->Min() <= CapSub(cumul_j->Max(), cumul_i->Min()) &&
852 CapSub(cumul_j->Min(), cumul_i->Max()) <= transit_i->Max();
859 StampedVector() : stamp_(0) {}
860 const std::vector<T>& Values(Solver* solver) {
864 void PushBack(Solver* solver, const T& value) {
868 void Clear(Solver* solver) {
870 stamp_ = solver->fail_stamp();
874 void CheckStamp(Solver* solver) {
875 if (solver->fail_stamp() > stamp_) {
885class DelayedPathCumul : public Constraint {
887 DelayedPathCumul(Solver* const solver, const std::vector<IntVar*>& nexts,
888 const std::vector<IntVar*>& active,
889 const std::vector<IntVar*>& cumuls,
890 const std::vector<IntVar*>& transits)
896 cumul_transit_demons_(cumuls.size(), nullptr),
899 chain_starts_(cumuls.size(), -1),
900 chain_ends_(cumuls.size(), -1),
901 is_chain_start_(cumuls.size(), false),
902 prevs_(cumuls.size(), -1),
904 was_bound_(nexts.size(), false),
905 has_cumul_demon_(cumuls.size(), false) {
906 for (int64_t i = 0; i < cumuls_.size(); ++i) {
908 solver, this, &DelayedPathCumul::CumulRange, "CumulRange", i);
913 solver, this, &DelayedPathCumul::PropagatePaths, "PropagatePaths");
914 for (int i = 0; i < nexts_.size(); ++i) {
915 supports_[i] = -1;
918 ~DelayedPathCumul() override {}
920 solver()->RegisterDemon(path_demon_);
921 for (int i = 0; i < nexts_.size(); ++i) {
922 if (!nexts_[i]->Bound()) {
924 solver(), this, &DelayedPathCumul::NextBound, "NextBound", i);
925 nexts_[i]->WhenBound(demon);
928 for (int i = 0; i < active_.size(); ++i) {
929 if (!active_[i]->Bound()) {
931 solver(), this, &DelayedPathCumul::ActiveBound, "ActiveBound", i);
932 active_[i]->WhenBound(demon);
936 void InitialPropagate() override {
938 for (int i = 0; i < nexts_.size(); ++i) {
943 for (int i = 0; i < active_.size(); ++i) {
944 if (active_[i]->Bound()) {
951 void NextBound(int index) {
952 if (active_[index]->Min() > 0) {
953 const int next = nexts_[index]->Min();
954 PropagateLink(index, next);
955 touched_.PushBack(solver(), index);
956 EnqueueDelayedDemon(path_demon_);
959 void ActiveBound(int index) {
960 if (nexts_[index]->Bound()) {
966 const std::vector<int>& touched_values = touched_.Values(solver());
967 for (const int touched : touched_values) {
968 chain_starts_[touched] = touched;
969 chain_ends_[touched] = touched;
970 is_chain_start_[touched] = false;
971 const int next = nexts_[touched]->Min();
972 chain_starts_[next] = next;
974 is_chain_start_[next] = false;
976 for (const int touched : touched_values) {
977 if (touched >= nexts_.size()) continue;
978 IntVar* const next_var = nexts_[touched];
979 if (!was_bound_[touched] && next_var->Bound() &&
980 active_[touched]->Min() > 0) {
981 const int64_t next = next_var->Min();
982 was_bound_.SetValue(solver(), touched, true);
983 chain_starts_[chain_ends_[next]] = chain_starts_[touched];
984 chain_ends_[chain_starts_[touched]] = chain_ends_[next];
985 is_chain_start_[next] = false;
986 is_chain_start_[chain_starts_[touched]] = true;
990 for (const int touched : touched_values) {
992 if (is_chain_start_[touched]) {
994 int64_t current = touched;
995 int64_t next = nexts_[current]->Min();
996 while (current != chain_ends_[touched]) {
997 prevs_.SetValue(solver(), next, current);
998 PropagateLink(current, next);
1000 if (current != chain_ends_[touched]) {
1001 next = nexts_[current]->Min();
1005 int64_t prev = prevs_[current];
1006 while (current != touched) {
1007 PropagateLink(prev, current);
1009 if (current != touched) {
1017 while (current != chain_ends_[touched]) {
1018 if (!has_cumul_demon_[current]) {
1019 Demon* const demon = cumul_transit_demons_[current];
1020 cumuls_[current]->WhenRange(demon);
1021 transits_[current]->WhenRange(demon);
1022 has_cumul_demon_.SetValue(solver(), current, true);
1024 current = nexts_[current]->Min();
1026 if (!has_cumul_demon_[current]) {
1027 Demon* const demon = cumul_transit_demons_[current];
1028 cumuls_[current]->WhenRange(demon);
1029 if (current < transits_.size()) {
1030 transits_[current]->WhenRange(demon);
1033 has_cumul_demon_.SetValue(solver(), current, true);
1037 touched_.Clear(solver());
1040 void Accept(ModelVisitor* const visitor) const override {
1041 visitor->BeginVisitConstraint(ModelVisitor::kDelayedPathCumul, this);
1042 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
1044 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
1046 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
1048 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kTransitsArgument,
1050 visitor->EndVisitConstraint(ModelVisitor::kDelayedPathCumul, this);
1053 std::string DebugString() const override {
1054 std::string out = "DelayedPathCumul(";
1055 for (int i = 0; i < nexts_.size(); ++i) {
1056 out += nexts_[i]->DebugString() + " " + cumuls_[i]->DebugString();
1063 void CumulRange(int64_t index) {
1064 if (index < nexts_.size()) {
1065 if (nexts_[index]->Bound()) {
1066 if (active_[index]->Min() > 0) {
1067 PropagateLink(index, nexts_[index]->Min());
1073 if (prevs_[index] >= 0) {
1074 PropagateLink(prevs_[index], index);
1076 for (int i = 0; i < nexts_.size(); ++i) {
1077 if (index == supports_[i]) {
1083 void UpdateSupport(int index) {
1084 int support = supports_[index];
1085 if (support < 0 || !AcceptLink(index, support)) {
1086 IntVar* const next = nexts_[index];
1087 for (int i = next->Min(); i <= next->Max(); ++i) {
1088 if (i != support && AcceptLink(index, i)) {
1089 supports_[index] = i;
1093 active_[index]->SetMax(0);
1096 void PropagateLink(int64_t index, int64_t next) {
1097 IntVar* const cumul_var = cumuls_[index];
1098 IntVar* const next_cumul_var = cumuls_[next];
1099 IntVar* const transit = transits_[index];
1100 const int64_t transit_min = transit->Min();
1101 const int64_t transit_max = transit->Max();
1102 next_cumul_var->SetMin(CapAdd(cumul_var->Min(), transit_min));
1103 next_cumul_var->SetMax(CapAdd(cumul_var->Max(), transit_max));
1104 const int64_t next_cumul_min = next_cumul_var->Min();
1105 const int64_t next_cumul_max = next_cumul_var->Max();
1106 cumul_var->SetMin(CapSub(next_cumul_min, transit_max));
1107 cumul_var->SetMax(CapSub(next_cumul_max, transit_min));
1108 transit->SetMin(CapSub(next_cumul_min, cumul_var->Max()));
1109 transit->SetMax(CapSub(next_cumul_max, cumul_var->Min()));
1111 bool AcceptLink(int index, int next) const {
1112 IntVar* const cumul_var = cumuls_[index];
1113 IntVar* const next_cumul_var = cumuls_[next];
1114 IntVar* const transit = transits_[index];
1115 return transit->Min() <= CapSub(next_cumul_var->Max(), cumul_var->Min()) &&
1116 CapSub(next_cumul_var->Min(), cumul_var->Max()) <= transit->Max();
1119 const std::vector<IntVar*> nexts_;
1120 const std::vector<IntVar*> active_;
1121 const std::vector<IntVar*> cumuls_;
1122 const std::vector<IntVar*> transits_;
1123 std::vector<Demon*> cumul_transit_demons_;
1125 StampedVector<int> touched_;
1126 std::vector<int64_t> chain_starts_;
1127 std::vector<int64_t> chain_ends_;
1128 std::vector<bool> is_chain_start_;
1130 std::vector<int> supports_;
1131 RevArray<bool> was_bound_;
1132 RevArray<bool> has_cumul_demon_;
1137class IndexEvaluator2PathCumul : public BasePathCumul {
1139 IndexEvaluator2PathCumul(Solver* s, const std::vector<IntVar*>& nexts,
1140 const std::vector<IntVar*>& active,
1141 const std::vector<IntVar*>& cumuls,
1142 Solver::IndexEvaluator2 transit_evaluator);
1143 ~IndexEvaluator2PathCumul() override {}
1144 void NextBound(int index) override;
1145 bool AcceptLink(int i, int j) const override;
1147 void Accept(ModelVisitor* const visitor) const override {
1148 visitor->BeginVisitConstraint(ModelVisitor::kPathCumul, this);
1149 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
1151 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
1153 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
1159 visitor->EndVisitConstraint(ModelVisitor::kPathCumul, this);
1163 Solver::IndexEvaluator2 transits_evaluator_;
1166IndexEvaluator2PathCumul::IndexEvaluator2PathCumul(
1167 Solver* const s, const std::vector<IntVar*>& nexts,
1168 const std::vector<IntVar*>& active, const std::vector<IntVar*>& cumuls,
1170 : BasePathCumul(s, nexts, active, cumuls),
1171 transits_evaluator_(std::move(transit_evaluator)) {}
1173void IndexEvaluator2PathCumul::NextBound(int index) {
1174 if (active_[index]->Min() == 0) return;
1175 const int64_t next = nexts_[index]->Value();
1176 IntVar* cumul = cumuls_[index];
1177 IntVar* cumul_next = cumuls_[next];
1178 const int64_t transit = transits_evaluator_(index, next);
1179 cumul_next->SetMin(cumul->Min() + transit);
1180 cumul_next->SetMax(CapAdd(cumul->Max(), transit));
1181 cumul->SetMin(CapSub(cumul_next->Min(), transit));
1182 cumul->SetMax(CapSub(cumul_next->Max(), transit));
1184 prevs_.SetValue(solver(), next, index);
1188bool IndexEvaluator2PathCumul::AcceptLink(int i, int j) const {
1189 const IntVar* const cumul_i = cumuls_[i];
1190 const IntVar* const cumul_j = cumuls_[j];
1191 const int64_t transit = transits_evaluator_(i, j);
1192 return transit <= CapSub(cumul_j->Max(), cumul_i->Min()) &&
1193 CapSub(cumul_j->Min(), cumul_i->Max()) <= transit;
1198class IndexEvaluator2SlackPathCumul : public BasePathCumul {
1200 IndexEvaluator2SlackPathCumul(Solver* s, const std::vector<IntVar*>& nexts,
1201 const std::vector<IntVar*>& active,
1202 const std::vector<IntVar*>& cumuls,
1203 const std::vector<IntVar*>& slacks,
1204 Solver::IndexEvaluator2 transit_evaluator);
1205 ~IndexEvaluator2SlackPathCumul() override {}
1207 void NextBound(int index) override;
1208 bool AcceptLink(int i, int j) const override;
1209 void SlackRange(int index);
1211 void Accept(ModelVisitor* const visitor) const override {
1212 visitor->BeginVisitConstraint(ModelVisitor::kPathCumul, this);
1213 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kNextsArgument,
1215 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kActiveArgument,
1217 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kCumulsArgument,
1223 visitor->EndVisitConstraint(ModelVisitor::kPathCumul, this);
1227 const std::vector<IntVar*> slacks_;
1228 Solver::IndexEvaluator2 transits_evaluator_;
1231IndexEvaluator2SlackPathCumul::IndexEvaluator2SlackPathCumul(
1232 Solver* const s, const std::vector<IntVar*>& nexts,
1233 const std::vector<IntVar*>& active, const std::vector<IntVar*>& cumuls,
1234 const std::vector<IntVar*>& slacks,
1236 : BasePathCumul(s, nexts, active, cumuls),
1238 transits_evaluator_(std::move(transit_evaluator)) {}
1240void IndexEvaluator2SlackPathCumul::Post() {
1242 for (int i = 0; i < size(); ++i) {
1244 solver(), this, &IndexEvaluator2SlackPathCumul::SlackRange,
1246 slacks_[i]->WhenRange(slack_demon);
1250void IndexEvaluator2SlackPathCumul::SlackRange(int index) {
1251 if (nexts_[index]->Bound()) {
1256 if (prevs_[index] >= 0) {
1257 NextBound(prevs_[index]);
1259 for (int i = 0; i < size(); ++i) {
1260 if (index == supports_[i]) {
1267void IndexEvaluator2SlackPathCumul::NextBound(int index) {
1268 if (active_[index]->Min() == 0) return;
1269 const int64_t next = nexts_[index]->Value();
1270 IntVar* const cumul = cumuls_[index];
1271 IntVar* const cumul_next = cumuls_[next];
1272 IntVar* const slack = slacks_[index];
1273 const int64_t transit = transits_evaluator_(index, next);
1274 const int64_t cumul_next_minus_transit_min =
1275 CapSub(cumul_next->Min(), transit);
1276 const int64_t cumul_next_minus_transit_max =
1277 CapSub(cumul_next->Max(), transit);
1278 cumul_next->SetMin(CapAdd(CapAdd(cumul->Min(), transit), slack->Min()));
1279 cumul_next->SetMax(CapAdd(CapAdd(cumul->Max(), transit), slack->Max()));
1280 cumul->SetMin(CapSub(cumul_next_minus_transit_min, slack->Max()));
1281 cumul->SetMax(CapSub(cumul_next_minus_transit_max, slack->Min()));
1282 slack->SetMin(CapSub(cumul_next_minus_transit_min, cumul->Max()));
1283 slack->SetMax(CapSub(cumul_next_minus_transit_max, cumul->Min()));
1285 prevs_.SetValue(solver(), next, index);
1289bool IndexEvaluator2SlackPathCumul::AcceptLink(int i, int j) const {
1290 const IntVar* const cumul_i = cumuls_[i];
1291 const IntVar* const cumul_j = cumuls_[j];
1292 const IntVar* const slack = slacks_[i];
1293 const int64_t transit = transits_evaluator_(i, j);
1294 return CapAdd(transit, slack->Min()) <=
1295 CapSub(cumul_j->Max(), cumul_i->Min()) &&
1296 CapSub(cumul_j->Min(), cumul_i->Max()) <=
1297 CapAdd(slack->Max(), transit);
1302 const std::vector<IntVar*>& active,
1303 const std::vector<IntVar*>& cumuls,
1304 const std::vector<IntVar*>& transits) {
1305 CHECK_EQ(nexts.size(), active.size());
1306 CHECK_EQ(transits.size(), nexts.size());
1307 return RevAlloc(new PathCumul(this, nexts, active, cumuls, transits));
1311 const std::vector<IntVar*>& active,
1312 const std::vector<IntVar*>& cumuls,
1314 CHECK_EQ(nexts.size(), active.size());
1315 return RevAlloc(new IndexEvaluator2PathCumul(this, nexts, active, cumuls,
1320 const std::vector<IntVar*>& active,
1321 const std::vector<IntVar*>& cumuls,
1322 const std::vector<IntVar*>& slacks,
1324 CHECK_EQ(nexts.size(), active.size());
1325 return RevAlloc(new IndexEvaluator2SlackPathCumul(
1326 this, nexts, active, cumuls, slacks, std::move(transit_evaluator)));
1330 const std::vector<IntVar*>& active,
1331 const std::vector<IntVar*>& cumuls,
1332 const std::vector<IntVar*>& transits) {
1333 CHECK_EQ(nexts.size(), active.size());
1334 CHECK_EQ(transits.size(), nexts.size());
1335 return RevAlloc(new DelayedPathCumul(this, nexts, active, cumuls, transits));
1341class PathConnectedConstraint : public Constraint {
1343 PathConnectedConstraint(Solver* solver, std::vector<IntVar*> nexts,
1344 absl::Span<const int64_t> sources,
1345 std::vector<int64_t> sinks,
1346 std::vector<IntVar*> status)
1348 sources_(sources.size(), -1),
1349 index_to_path_(nexts.size(), -1),
1350 sinks_(std::move(sinks)),
1351 nexts_(std::move(nexts)),
1352 status_(std::move(status)),
1353 touched_(nexts_.size()) {
1354 CHECK_EQ(status_.size(), sources_.size());
1355 CHECK_EQ(status_.size(), sinks_.size());
1356 for (int i = 0; i < status_.size(); ++i) {
1357 const int64_t source = sources[i];
1358 sources_.SetValue(solver, i, source);
1359 if (source < index_to_path_.size()) {
1360 index_to_path_.SetValue(solver, source, i);
1365 for (int i = 0; i < nexts_.size(); ++i) {
1367 solver(), this, &PathConnectedConstraint::NextBound, "NextValue", i));
1369 for (int i = 0; i < status_.size(); ++i) {
1370 if (sources_[i] < nexts_.size()) {
1371 status_[i]->SetRange(0, 1);
1373 status_[i]->SetValue(0);
1377 void InitialPropagate() override {
1378 for (int i = 0; i < status_.size(); ++i) {
1382 std::string DebugString() const override {
1383 std::string output = "PathConnected(";
1384 std::vector<std::string> elements;
1385 for (IntVar* const next : nexts_) {
1386 elements.push_back(next->DebugString());
1388 for (int i = 0; i < sources_.size(); ++i) {
1389 elements.push_back(absl::StrCat(sources_[i]));
1391 for (int64_t sink : sinks_) {
1392 elements.push_back(absl::StrCat(sink));
1394 for (IntVar* const status : status_) {
1395 elements.push_back(status->DebugString());
1397 output += absl::StrJoin(elements, ",") + ")";
1402 void NextBound(int index) {
1403 const int path = index_to_path_[index];
1408 void EvaluatePath(int path) {
1410 int64_t source = sources_[path];
1411 const int64_t end = sinks_[path];
1413 if (source >= nexts_.size() || touched_[source]) {
1414 status_[path]->SetValue(0);
1417 touched_.Set(source);
1418 IntVar* const next = nexts_[source];
1422 sources_.SetValue(solver(), path, source);
1423 index_to_path_.SetValue(solver(), source, path);
1427 status_[path]->SetValue(1);
1430 RevArray<int64_t> sources_;
1431 RevArray<int> index_to_path_;
1432 const std::vector<int64_t> sinks_;
1433 const std::vector<IntVar*> nexts_;
1434 const std::vector<IntVar*> status_;
1435 SparseBitset<int64_t> touched_;
1440 std::vector<int64_t> sources,
1441 std::vector<int64_t> sinks,
1442 std::vector<IntVar*> status) {
1443 return RevAlloc(new PathConnectedConstraint(
1444 this, std::move(nexts), sources, std::move(sinks), std::move(status)));
1448class PathTransitPrecedenceConstraint : public Constraint {
1455 PathTransitPrecedenceConstraint(
1456 Solver* solver, std::vector<IntVar*> nexts, std::vector<IntVar*> transits,
1457 absl::Span<const std::pair<int, int>> precedences,
1458 absl::flat_hash_map<int, PrecedenceType> precedence_types)
1460 nexts_(std::move(nexts)),
1461 transits_(std::move(transits)),
1462 predecessors_(nexts_.size()),
1463 successors_(nexts_.size()),
1464 precedence_types_(std::move(precedence_types)),
1465 starts_(nexts_.size(), -1),
1466 ends_(nexts_.size(), -1),
1467 transit_cumuls_(nexts_.size(), 0) {
1468 for (int i = 0; i < nexts_.size(); ++i) {
1469 starts_.SetValue(solver, i, i);
1470 ends_.SetValue(solver, i, i);
1472 for (const auto& precedence : precedences) {
1473 if (precedence.second < nexts_.size()) {
1474 predecessors_[precedence.second].push_back(precedence.first);
1476 if (precedence.first < nexts_.size()) {
1477 successors_[precedence.first].push_back(precedence.second);
1481 ~PathTransitPrecedenceConstraint() override {}
1483 for (int i = 0; i < nexts_.size(); ++i) {
1485 solver(), this, &PathTransitPrecedenceConstraint::NextBound,
1488 for (int i = 0; i < transits_.size(); ++i) {
1490 solver(), this, &PathTransitPrecedenceConstraint::NextBound,
1494 void InitialPropagate() override {
1495 for (int i = 0; i < nexts_.size(); ++i) {
1496 if (nexts_[i]->Bound()) {
1501 std::string DebugString() const override {
1502 std::string output = "PathPrecedence(";
1503 std::vector<std::string> elements = {JoinDebugStringPtr(nexts_, ",")};
1504 if (!transits_.empty()) {
1507 for (int i = 0; i < predecessors_.size(); ++i) {
1508 for (const int predecessor : predecessors_[i]) {
1509 elements.push_back(absl::StrCat("(", predecessor, ", ", i, ")"));
1512 output += absl::StrJoin(elements, ",") + ")";
1515 void Accept(ModelVisitor* const visitor) const override {
1520 void NextBound(int index) {
1521 if (!nexts_[index]->Bound()) return;
1522 const int next = nexts_[index]->Min();
1523 const int start = starts_[index];
1524 const int end = (next < nexts_.size()) ? ends_[next] : next;
1525 if (end < nexts_.size()) starts_.SetValue(solver(), end, start);
1526 ends_.SetValue(solver(), start, end);
1528 PrecedenceType type = ANY;
1529 auto it = precedence_types_.find(start);
1530 if (it != precedence_types_.end()) {
1536 int64_t transit_cumul = 0;
1537 const bool has_transits = !transits_.empty();
1538 while (current < nexts_.size() && current != end) {
1539 transit_cumuls_[current] = transit_cumul;
1542 if (!predecessors_[current].empty() && !pushed_.empty()) {
1545 for (const int predecessor : predecessors_[current]) {
1546 if (pushed_.back() == predecessor) {
1551 if (!found) solver()->Fail();
1554 if (forbidden_.find(current) != forbidden_.end()) {
1555 for (const int successor : successors_[current]) {
1556 if (marked_.find(successor) != marked_.end()) {
1558 CapSub(transit_cumul, transit_cumuls_[successor]) > 0) {
1564 if (!successors_[current].empty()) {
1567 pushed_.push_back(current);
1570 pushed_.push_front(current);
1576 for (const int predecessor : predecessors_[current]) {
1577 forbidden_.insert(predecessor);
1580 transit_cumul = CapAdd(transit_cumul, transits_[current]->Min());
1582 current = nexts_[current]->Min();
1584 if (forbidden_.find(current) != forbidden_.end()) {
1585 for (const int successor : successors_[current]) {
1586 if (marked_.find(successor) != marked_.end()) {
1588 CapSub(transit_cumul, transit_cumuls_[successor]) > 0) {
1596 const std::vector<IntVar*> nexts_;
1597 const std::vector<IntVar*> transits_;
1598 std::vector<std::vector<int>> predecessors_;
1599 std::vector<std::vector<int>> successors_;
1600 const absl::flat_hash_map<int, PrecedenceType> precedence_types_;
1603 absl::flat_hash_set<int> forbidden_;
1604 absl::flat_hash_set<int> marked_;
1606 std::vector<int64_t> transit_cumuls_;
1609Constraint* MakePathTransitTypedPrecedenceConstraint(
1610 Solver* solver, std::vector<IntVar*> nexts, std::vector<IntVar*> transits,
1611 absl::Span<const std::pair<int, int>> precedences,
1612 absl::flat_hash_map<int, PathTransitPrecedenceConstraint::PrecedenceType>
1614 if (precedences.empty()) {
1615 return solver->MakeTrueConstraint();
1617 return solver->RevAlloc(new PathTransitPrecedenceConstraint(
1618 solver, std::move(nexts), std::move(transits), precedences,
1619 std::move(precedence_types)));
1631 std::vector<IntVar*> nexts,
1632 const std::vector<std::pair<int, int>>& precedences,
1633 absl::Span<const int> lifo_path_starts,
1634 absl::Span<const int> fifo_path_starts) {
1635 absl::flat_hash_map<int, PathTransitPrecedenceConstraint::PrecedenceType>
1637 for (int start : lifo_path_starts) {
1638 precedence_types[start] = PathTransitPrecedenceConstraint::LIFO;
1640 for (int start : fifo_path_starts) {
1641 precedence_types[start] = PathTransitPrecedenceConstraint::FIFO;
1643 return MakePathTransitTypedPrecedenceConstraint(
1644 this, std::move(nexts), {}, precedences, std::move(precedence_types));
1648 std::vector<IntVar*> nexts, std::vector<IntVar*> transits,
1649 const std::vector<std::pair<int, int>>& precedences) {
1650 return MakePathTransitTypedPrecedenceConstraint(
1651 this, std::move(nexts), std::move(transits), precedences, {{}});
1656class PathEnergyCostConstraint : public Constraint {
1658 PathEnergyCostConstraint(
1661 : Constraint(solver), specification_(std::move(specification)) {
1662 const int num_nexts = specification_.nexts.size();
1663 DCHECK_EQ(num_nexts, specification_.distances.size());
1666 DCHECK_EQ(num_paths, specification_.costs.size());
1668 DCHECK_EQ(num_paths, specification_.path_starts.size());
1669 DCHECK_EQ(num_paths, specification_.path_ends.size());
1671 const int num_nodes = specification_.forces.size();
1672 DCHECK_EQ(num_nodes, specification_.paths.size());
1676 void InitialPropagate() override;
1679 void NodeDispatcher(int node);
1680 void PropagatePath(int path);
1681 Solver::PathEnergyCostConstraintSpecification specification_;
1682 std::vector<Demon*> path_demons_;
1687void PathEnergyCostConstraint::Post() {
1690 path_demons_.resize(num_paths, nullptr);
1691 for (int path = 0; path < num_paths; ++path) {
1695 if (cost.IsNull()) continue;
1697 solver(), this, &PathEnergyCostConstraint::PropagatePath,
1701 const int num_nodes = specification_.forces.size();
1702 const int num_nexts = specification_.nexts.size();
1703 for (int node = 0; node < num_nodes; ++node) {
1705 solver(), this, &PathEnergyCostConstraint::NodeDispatcher,
1707 specification_.forces[node]->WhenRange(demon);
1708 specification_.paths[node]->WhenBound(demon);
1710 specification_.nexts[node]->WhenBound(demon);
1711 specification_.distances[node]->WhenRange(demon);
1716void PathEnergyCostConstraint::InitialPropagate() {
1718 for (int path = 0; path < num_paths; ++path) {
1721 specification_.costs[path]->SetValue(0);
1729void PathEnergyCostConstraint::NodeDispatcher(int node) {
1730 if (!specification_.paths[node]->Bound()) return;
1731 const int path = specification_.paths[node]->Min();
1733 if (path_demons_[path] == nullptr) return;
1734 EnqueueDelayedDemon(path_demons_[path]);
1738void PathEnergyCostConstraint::PropagatePath(int path) {
1740 const auto& [threshold, cost_per_unit_below, cost_per_unit_above] =
1742 int64_t energy_below_min = 0;
1743 int64_t energy_below_max = 0;
1744 int64_t energy_above_min = 0;
1745 int64_t energy_above_max = 0;
1746 int current = specification_.path_starts[path];
1747 const int num_nodes = specification_.nexts.size();
1748 int num_nonend_nodes = 0;
1749 while (current < num_nodes) {
1751 if (!specification_.nexts[current]->Bound()) break;
1752 const int next = specification_.nexts[current]->Min();
1754 const int64_t distance_min = specification_.distances[current]->Min();
1755 const int64_t distance_max = specification_.distances[current]->Max();
1756 DCHECK_GE(distance_min, 0);
1757 const IntVar* force = specification_.forces[next];
1758 CapAddTo(CapProd(std::min(threshold, force->Min()), distance_min),
1760 CapAddTo(CapProd(std::min(threshold, force->Max()), distance_max),
1770 if (current == specification_.path_ends[path]) {
1772 specification_.costs[path]->SetValue(0);
1774 specification_.costs[path]->SetRange(
1775 CapAdd(CapProd(energy_below_min, cost_per_unit_below),
1776 CapProd(energy_above_min, cost_per_unit_above)),
1777 CapAdd(CapProd(energy_below_max, cost_per_unit_below),
1778 CapProd(energy_above_max, cost_per_unit_above)));
1787 return RevAlloc(new PathEnergyCostConstraint(this, std::move(specification)));
static const char kNoCycle[]
static const char kActiveArgument[]
argument names:
static const char kNextsArgument[]
Constraint * MakePathConnected(std::vector< IntVar * > nexts, std::vector< int64_t > sources, std::vector< int64_t > sinks, std::vector< IntVar * > status)
Check whether more propagation is needed.
Definition graph_constraints.cc:1439
Constraint * MakeSubCircuit(const std::vector< IntVar * > &nexts)
Definition graph_constraints.cc:645
Constraint * MakePathCumul(const std::vector< IntVar * > &nexts, const std::vector< IntVar * > &active, const std::vector< IntVar * > &cumuls, const std::vector< IntVar * > &transits)
Definition graph_constraints.cc:1301
Constraint * MakeCircuit(const std::vector< IntVar * > &nexts)
Force the "nexts" variable to create a complete Hamiltonian path.
Definition graph_constraints.cc:641
Constraint * MakePathEnergyCostConstraint(PathEnergyCostConstraintSpecification specification)
Definition graph_constraints.cc:1785
Constraint * MakePathPrecedenceConstraint(std::vector< IntVar * > nexts, const std::vector< std::pair< int, int > > &precedences)
Definition graph_constraints.cc:1624
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
Constraint * MakeDelayedPathCumul(const std::vector< IntVar * > &nexts, const std::vector< IntVar * > &active, const std::vector< IntVar * > &cumuls, const std::vector< IntVar * > &transits)
Definition graph_constraints.cc:1329
std::function< bool(int64_t)> IndexFilter1
Constraint * MakeNoCycle(const std::vector< IntVar * > &nexts, const std::vector< IntVar * > &active, IndexFilter1 sink_handler=nullptr)
Definition graph_constraints.cc:634
Constraint * MakePathTransitPrecedenceConstraint(std::vector< IntVar * > nexts, std::vector< IntVar * > transits, const std::vector< std::pair< int, int > > &precedences)
Definition graph_constraints.cc:1647
void Set(IntegerType index)
std::vector< typename Map::mapped_type > Values(const Map &map, const Keys &keys)
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
int64_t CapAdd(int64_t x, int64_t y)
void CapAddTo(int64_t x, int64_t *y)
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)
int64_t CapSub(int64_t x, int64_t y)
Demon * MakeDelayedConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
ClosedInterval::Iterator end(ClosedInterval interval)
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)
std::string DebugString() const
std::vector< IntVar * > paths
std::vector< IntVar * > nexts
std::vector< EnergyCost > path_energy_costs
std::vector< bool > path_used_when_empty
std::vector< IntVar * > forces
std::vector< int64_t > path_starts
std::vector< IntVar * > costs
std::vector< int64_t > path_ends
std::vector< IntVar * > distances