Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sched_search.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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 <cstring>
17#include <limits>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_set.h"
23#include "absl/log/check.h"
24#include "absl/strings/str_format.h"
29
30namespace operations_research {
31namespace {
32int64_t ValueToIndex(int64_t value) { return value - 1; }
33
34int64_t IndexToValue(int64_t index) { return index + 1; }
35} // namespace
36
37// ----- SequenceVar -----
38
39// TODO(user): Add better class invariants, in particular checks
40// that ranked_first, ranked_last, and unperformed are truly disjoint.
41
43 const std::vector<IntervalVar*>& intervals,
44 const std::vector<IntVar*>& nexts,
45 const std::string& name)
47 intervals_(intervals),
48 nexts_(nexts),
49 previous_(nexts.size() + 1, -1) {
51}
52
54
56 return intervals_[index];
57}
58
59IntVar* SequenceVar::Next(int index) const { return nexts_[index]; }
60
61std::string SequenceVar::DebugString() const {
62 int64_t hmin, hmax, dmin, dmax;
63 HorizonRange(&hmin, &hmax);
64 DurationRange(&dmin, &dmax);
65 int unperformed = 0;
66 int ranked = 0;
67 int not_ranked = 0;
68 ComputeStatistics(&ranked, &not_ranked, &unperformed);
69 return absl::StrFormat(
70 "%s(horizon = %d..%d, duration = %d..%d, not ranked = %d, ranked = %d, "
71 "nexts = [%s])",
72 name(), hmin, hmax, dmin, dmax, not_ranked, ranked,
73 JoinDebugStringPtr(nexts_, ", "));
74}
75
76void SequenceVar::Accept(ModelVisitor* const visitor) const {
77 visitor->VisitSequenceVariable(this);
78}
79
80void SequenceVar::DurationRange(int64_t* const dmin,
81 int64_t* const dmax) const {
82 int64_t dur_min = 0;
83 int64_t dur_max = 0;
84 for (int i = 0; i < intervals_.size(); ++i) {
85 IntervalVar* const t = intervals_[i];
86 if (t->MayBePerformed()) {
87 if (t->MustBePerformed()) {
88 dur_min += t->DurationMin();
89 }
90 dur_max += t->DurationMax();
91 }
92 }
93 *dmin = dur_min;
94 *dmax = dur_max;
95}
96
97void SequenceVar::HorizonRange(int64_t* const hmin, int64_t* const hmax) const {
98 int64_t hor_min = std::numeric_limits<int64_t>::max();
99 int64_t hor_max = std::numeric_limits<int64_t>::min();
100 for (int i = 0; i < intervals_.size(); ++i) {
101 IntervalVar* const t = intervals_[i];
102 if (t->MayBePerformed()) {
103 IntervalVar* const t = intervals_[i];
104 hor_min = std::min(hor_min, t->StartMin());
105 hor_max = std::max(hor_max, t->EndMax());
106 }
107 }
108 *hmin = hor_min;
109 *hmax = hor_max;
110}
111
112void SequenceVar::ActiveHorizonRange(int64_t* const hmin,
113 int64_t* const hmax) const {
114 absl::flat_hash_set<int> decided;
115 for (int i = 0; i < intervals_.size(); ++i) {
116 if (intervals_[i]->CannotBePerformed()) {
117 decided.insert(i);
118 }
119 }
120 int first = 0;
121 while (nexts_[first]->Bound()) {
122 first = nexts_[first]->Min();
123 if (first < nexts_.size()) {
124 decided.insert(ValueToIndex(first));
125 } else {
126 break;
127 }
128 }
129 if (first != nexts_.size()) {
130 UpdatePrevious();
131 int last = nexts_.size();
132 while (previous_[last] != -1) {
133 last = previous_[last];
134 decided.insert(ValueToIndex(last));
135 }
136 }
137 int64_t hor_min = std::numeric_limits<int64_t>::max();
138 int64_t hor_max = std::numeric_limits<int64_t>::min();
139 for (int i = 0; i < intervals_.size(); ++i) {
140 if (!decided.contains(i)) {
141 IntervalVar* const t = intervals_[i];
142 hor_min = std::min(hor_min, t->StartMin());
143 hor_max = std::max(hor_max, t->EndMax());
144 }
145 }
146 *hmin = hor_min;
147 *hmax = hor_max;
148}
149
150void SequenceVar::ComputeStatistics(int* const ranked, int* const not_ranked,
151 int* const unperformed) const {
152 *unperformed = 0;
153 for (int i = 0; i < intervals_.size(); ++i) {
154 if (intervals_[i]->CannotBePerformed()) {
155 (*unperformed)++;
156 }
157 }
158 *ranked = 0;
159 int first = 0;
160 while (first < nexts_.size() && nexts_[first]->Bound()) {
161 first = nexts_[first]->Min();
162 (*ranked)++;
163 }
164 if (first != nexts_.size()) {
165 UpdatePrevious();
166 int last = nexts_.size();
167 while (previous_[last] != -1) {
168 last = previous_[last];
169 (*ranked)++;
170 }
171 } else { // We counted the sentinel.
172 (*ranked)--;
173 }
174 *not_ranked = intervals_.size() - *ranked - *unperformed;
175}
176
177int SequenceVar::ComputeForwardFrontier() {
178 int first = 0;
179 while (first != nexts_.size() && nexts_[first]->Bound()) {
180 first = nexts_[first]->Min();
181 }
182 return first;
183}
184
185int SequenceVar::ComputeBackwardFrontier() {
186 UpdatePrevious();
187 int last = nexts_.size();
188 while (previous_[last] != -1) {
189 last = previous_[last];
190 }
191 return last;
192}
193
195 std::vector<int>* const possible_firsts,
196 std::vector<int>* const possible_lasts) {
197 possible_firsts->clear();
198 possible_lasts->clear();
199 absl::flat_hash_set<int> to_check;
200 for (int i = 0; i < intervals_.size(); ++i) {
201 if (intervals_[i]->MayBePerformed()) {
202 to_check.insert(i);
203 }
204 }
205 int first = 0;
206 while (nexts_[first]->Bound()) {
207 first = nexts_[first]->Min();
208 if (first == nexts_.size()) {
209 return;
210 }
211 to_check.erase(ValueToIndex(first));
212 }
213
214 IntVar* const forward_var = nexts_[first];
215 std::vector<int> candidates;
216 int64_t smallest_start_max = std::numeric_limits<int64_t>::max();
217 int ssm_support = -1;
218 for (int64_t i = forward_var->Min(); i <= forward_var->Max(); ++i) {
219 // TODO(user): use domain iterator.
220 if (i != 0 && i < IndexToValue(intervals_.size()) &&
221 intervals_[ValueToIndex(i)]->MayBePerformed() &&
222 forward_var->Contains(i)) {
223 const int candidate = ValueToIndex(i);
224 candidates.push_back(candidate);
225 if (intervals_[candidate]->MustBePerformed()) {
226 if (smallest_start_max > intervals_[candidate]->StartMax()) {
227 smallest_start_max = intervals_[candidate]->StartMax();
228 ssm_support = candidate;
229 }
230 }
231 }
232 }
233 for (int i = 0; i < candidates.size(); ++i) {
234 const int candidate = candidates[i];
235 if (candidate == ssm_support ||
236 intervals_[candidate]->EndMin() <= smallest_start_max) {
237 possible_firsts->push_back(candidate);
238 }
239 }
240
241 UpdatePrevious();
242 int last = nexts_.size();
243 while (previous_[last] != -1) {
244 last = previous_[last];
245 to_check.erase(ValueToIndex(last));
246 }
247
248 candidates.clear();
249 int64_t biggest_end_min = std::numeric_limits<int64_t>::min();
250 int bem_support = -1;
251 for (const int candidate : to_check) {
252 if (nexts_[IndexToValue(candidate)]->Contains(last)) {
253 candidates.push_back(candidate);
254 if (intervals_[candidate]->MustBePerformed()) {
255 if (biggest_end_min < intervals_[candidate]->EndMin()) {
256 biggest_end_min = intervals_[candidate]->EndMin();
257 bem_support = candidate;
258 }
259 }
260 }
261 }
262
263 for (int i = 0; i < candidates.size(); ++i) {
264 const int candidate = candidates[i];
265 if (candidate == bem_support ||
266 intervals_[candidate]->StartMax() >= biggest_end_min) {
267 possible_lasts->push_back(candidate);
268 }
269 }
270}
271
272void SequenceVar::RankSequence(const std::vector<int>& rank_first,
273 const std::vector<int>& rank_last,
274 const std::vector<int>& unperformed) {
275 solver()->GetPropagationMonitor()->RankSequence(this, rank_first, rank_last,
276 unperformed);
277 // Mark unperformed.
278 for (const int value : unperformed) {
279 intervals_[value]->SetPerformed(false);
280 }
281 // Forward.
282 int forward = 0;
283 for (int i = 0; i < rank_first.size(); ++i) {
284 const int next = 1 + rank_first[i];
285 nexts_[forward]->SetValue(next);
286 forward = next;
287 }
288 // Backward.
289 int backward = IndexToValue(intervals_.size());
290 for (int i = 0; i < rank_last.size(); ++i) {
291 const int next = 1 + rank_last[i];
292 nexts_[next]->SetValue(backward);
293 backward = next;
294 }
295}
296
297void SequenceVar::RankFirst(int index) {
298 solver()->GetPropagationMonitor()->RankFirst(this, index);
299 intervals_[index]->SetPerformed(true);
300 int forward_frontier = 0;
301 while (forward_frontier != nexts_.size() &&
302 nexts_[forward_frontier]->Bound()) {
303 forward_frontier = nexts_[forward_frontier]->Min();
304 if (forward_frontier == IndexToValue(index)) {
305 return;
306 }
307 }
308 DCHECK_LT(forward_frontier, nexts_.size());
309 nexts_[forward_frontier]->SetValue(IndexToValue(index));
310}
311
313 solver()->GetPropagationMonitor()->RankNotFirst(this, index);
314 const int forward_frontier = ComputeForwardFrontier();
315 if (forward_frontier < nexts_.size()) {
316 nexts_[forward_frontier]->RemoveValue(IndexToValue(index));
317 }
318}
319
320void SequenceVar::RankLast(int index) {
321 solver()->GetPropagationMonitor()->RankLast(this, index);
322 intervals_[index]->SetPerformed(true);
323 UpdatePrevious();
324 int backward_frontier = nexts_.size();
325 while (previous_[backward_frontier] != -1) {
326 backward_frontier = previous_[backward_frontier];
327 if (backward_frontier == IndexToValue(index)) {
328 return;
329 }
330 }
331 DCHECK_NE(backward_frontier, 0);
332 nexts_[IndexToValue(index)]->SetValue(backward_frontier);
333}
334
336 solver()->GetPropagationMonitor()->RankNotLast(this, index);
337 const int backward_frontier = ComputeBackwardFrontier();
338 nexts_[IndexToValue(index)]->RemoveValue(backward_frontier);
339}
340
341void SequenceVar::UpdatePrevious() const {
342 for (int i = 0; i < intervals_.size() + 2; ++i) {
343 previous_[i] = -1;
344 }
345 for (int i = 0; i < nexts_.size(); ++i) {
346 if (nexts_[i]->Bound()) {
347 previous_[nexts_[i]->Min()] = i;
348 }
349 }
350}
351
352void SequenceVar::FillSequence(std::vector<int>* const rank_first,
353 std::vector<int>* const rank_last,
354 std::vector<int>* const unperformed) const {
355 CHECK(rank_first != nullptr);
356 CHECK(rank_last != nullptr);
357 CHECK(unperformed != nullptr);
358 rank_first->clear();
359 rank_last->clear();
360 unperformed->clear();
361 for (int i = 0; i < intervals_.size(); ++i) {
362 if (intervals_[i]->CannotBePerformed()) {
363 unperformed->push_back(i);
364 }
365 }
366 int first = 0;
367 while (nexts_[first]->Bound()) {
368 first = nexts_[first]->Min();
369 if (first < nexts_.size()) {
370 rank_first->push_back(ValueToIndex(first));
371 } else {
372 break;
373 }
374 }
375 if (first != nexts_.size()) {
376 UpdatePrevious();
377 int last = nexts_.size();
378 while (previous_[last] != -1) {
379 last = previous_[last];
380 rank_last->push_back(ValueToIndex(last));
381 }
382 }
383}
384
385// ----- Decisions and DecisionBuilders on interval vars -----
386
387// TODO(user) : treat optional intervals
388// TODO(user) : Call DecisionVisitor and pass name of variable
389namespace {
390//
391// Forward scheduling.
392//
393class ScheduleOrPostpone : public Decision {
394 public:
395 ScheduleOrPostpone(IntervalVar* const var, int64_t est, int64_t* const marker)
396 : var_(var), est_(est), marker_(marker) {}
397 ~ScheduleOrPostpone() override {}
398
399 void Apply(Solver* const s) override {
400 var_->SetPerformed(true);
401 if (est_.Value() < var_->StartMin()) {
402 est_.SetValue(s, var_->StartMin());
403 }
404 var_->SetStartRange(est_.Value(), est_.Value());
405 }
406
407 void Refute(Solver* const s) override {
408 s->SaveAndSetValue(marker_, est_.Value());
409 }
410
411 void Accept(DecisionVisitor* const visitor) const override {
412 CHECK(visitor != nullptr);
413 visitor->VisitScheduleOrPostpone(var_, est_.Value());
414 }
415
416 std::string DebugString() const override {
417 return absl::StrFormat("ScheduleOrPostpone(%s at %d)", var_->DebugString(),
418 est_.Value());
419 }
420
421 private:
422 IntervalVar* const var_;
423 NumericalRev<int64_t> est_;
424 int64_t* const marker_;
425};
426
427class SetTimesForward : public DecisionBuilder {
428 public:
429 explicit SetTimesForward(const std::vector<IntervalVar*>& vars)
430 : vars_(vars),
431 markers_(vars.size(), std::numeric_limits<int64_t>::min()) {}
432
433 ~SetTimesForward() override {}
434
435 Decision* Next(Solver* const s) override {
436 int64_t best_est = std::numeric_limits<int64_t>::max();
437 int64_t best_lct = std::numeric_limits<int64_t>::max();
438 int support = -1;
439 // We are looking for the interval that has the smallest start min
440 // (tie break with smallest end max) and is not postponed. And
441 // you're going to schedule that interval at its start min.
442 for (int i = 0; i < vars_.size(); ++i) {
443 IntervalVar* const v = vars_[i];
444 if (v->MayBePerformed() && v->StartMax() != v->StartMin() &&
445 !IsPostponed(i) &&
446 (v->StartMin() < best_est ||
447 (v->StartMin() == best_est && v->EndMax() < best_lct))) {
448 best_est = v->StartMin();
449 best_lct = v->EndMax();
450 support = i;
451 }
452 }
453 // TODO(user) : remove this crude quadratic loop with
454 // reversibles range reduction.
455 if (support == -1) { // All intervals are either fixed or postponed.
456 UnperformPostponedTaskBefore(std::numeric_limits<int64_t>::max());
457 return nullptr;
458 }
459 UnperformPostponedTaskBefore(best_est);
460 return s->RevAlloc(
461 new ScheduleOrPostpone(vars_[support], best_est, &markers_[support]));
462 }
463
464 std::string DebugString() const override { return "SetTimesForward()"; }
465
466 void Accept(ModelVisitor* const visitor) const override {
467 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
468 visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
469 vars_);
470 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
471 }
472
473 private:
474 bool IsPostponed(int index) {
475 DCHECK(vars_[index]->MayBePerformed());
476 return vars_[index]->StartMin() <= markers_[index];
477 }
478
479 void UnperformPostponedTaskBefore(int64_t date) {
480 for (int i = 0; i < vars_.size(); ++i) {
481 IntervalVar* const v = vars_[i];
482 if (v->MayBePerformed() && v->StartMin() != v->StartMax() &&
483 IsPostponed(i) &&
484 // There are two rules here:
485 // - v->StartMax() <= date: the interval should have been scheduled
486 // as it cannot be scheduled later (assignment is chronological).
487 // - v->EndMin() <= date: The interval can fit before the current
488 // start date. In that case, it 'should' always fit, and as it has
489 // not be scheduled, then we are missing it. So, as a dominance
490 // rule, it should be marked as unperformed.
491 (v->EndMin() <= date || v->StartMax() <= date)) {
492 v->SetPerformed(false);
493 }
494 }
495 }
496
497 const std::vector<IntervalVar*> vars_;
498 std::vector<int64_t> markers_;
499};
500
501//
502// Backward scheduling.
503//
504class ScheduleOrExpedite : public Decision {
505 public:
506 ScheduleOrExpedite(IntervalVar* const var, int64_t est, int64_t* const marker)
507 : var_(var), est_(est), marker_(marker) {}
508 ~ScheduleOrExpedite() override {}
509
510 void Apply(Solver* const s) override {
511 var_->SetPerformed(true);
512 if (est_.Value() > var_->EndMax()) {
513 est_.SetValue(s, var_->EndMax());
514 }
515 var_->SetEndRange(est_.Value(), est_.Value());
516 }
517
518 void Refute(Solver* const s) override {
519 s->SaveAndSetValue(marker_, est_.Value() - 1);
520 }
521
522 void Accept(DecisionVisitor* const visitor) const override {
523 CHECK(visitor != nullptr);
524 visitor->VisitScheduleOrExpedite(var_, est_.Value());
525 }
526
527 std::string DebugString() const override {
528 return absl::StrFormat("ScheduleOrExpedite(%s at %d)", var_->DebugString(),
529 est_.Value());
530 }
531
532 private:
533 IntervalVar* const var_;
534 NumericalRev<int64_t> est_;
535 int64_t* const marker_;
536};
537
538class SetTimesBackward : public DecisionBuilder {
539 public:
540 explicit SetTimesBackward(const std::vector<IntervalVar*>& vars)
541 : vars_(vars),
542 markers_(vars.size(), std::numeric_limits<int64_t>::max()) {}
543
544 ~SetTimesBackward() override {}
545
546 Decision* Next(Solver* const s) override {
547 int64_t best_end = std::numeric_limits<int64_t>::min();
548 int64_t best_start = std::numeric_limits<int64_t>::min();
549 int support = -1;
550 int refuted = 0;
551 for (int i = 0; i < vars_.size(); ++i) {
552 IntervalVar* const v = vars_[i];
553 if (v->MayBePerformed() && v->EndMax() > v->EndMin()) {
554 if (v->EndMax() <= markers_[i] &&
555 (v->EndMax() > best_end ||
556 (v->EndMax() == best_end && v->StartMin() > best_start))) {
557 best_end = v->EndMax();
558 best_start = v->StartMin();
559 support = i;
560 } else {
561 refuted++;
562 }
563 }
564 }
565 // TODO(user) : remove this crude quadratic loop with
566 // reversibles range reduction.
567 if (support == -1) {
568 if (refuted == 0) {
569 return nullptr;
570 } else {
571 s->Fail();
572 }
573 }
574 return s->RevAlloc(new ScheduleOrExpedite(
575 vars_[support], vars_[support]->EndMax(), &markers_[support]));
576 }
577
578 std::string DebugString() const override { return "SetTimesBackward()"; }
579
580 void Accept(ModelVisitor* const visitor) const override {
581 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
582 visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
583 vars_);
584 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
585 }
586
587 private:
588 const std::vector<IntervalVar*> vars_;
589 std::vector<int64_t> markers_;
590};
591
592// ----- Decisions and DecisionBuilders on sequences -----
593
594class RankFirst : public Decision {
595 public:
596 RankFirst(SequenceVar* const seq, int index)
597 : sequence_(seq), index_(index) {}
598 ~RankFirst() override {}
599
600 void Apply(Solver* const s) override { sequence_->RankFirst(index_); }
601
602 void Refute(Solver* const s) override { sequence_->RankNotFirst(index_); }
603
604 void Accept(DecisionVisitor* const visitor) const override {
605 CHECK(visitor != nullptr);
606 visitor->VisitRankFirstInterval(sequence_, index_);
607 }
608
609 std::string DebugString() const override {
610 return absl::StrFormat("RankFirst(%s, %d)", sequence_->DebugString(),
611 index_);
612 }
613
614 private:
615 SequenceVar* const sequence_;
616 const int index_;
617};
618
619class RankLast : public Decision {
620 public:
621 RankLast(SequenceVar* const seq, int index) : sequence_(seq), index_(index) {}
622 ~RankLast() override {}
623
624 void Apply(Solver* const s) override { sequence_->RankLast(index_); }
625
626 void Refute(Solver* const s) override { sequence_->RankNotLast(index_); }
627
628 void Accept(DecisionVisitor* const visitor) const override {
629 CHECK(visitor != nullptr);
630 visitor->VisitRankLastInterval(sequence_, index_);
631 }
632
633 std::string DebugString() const override {
634 return absl::StrFormat("RankLast(%s, %d)", sequence_->DebugString(),
635 index_);
636 }
637
638 private:
639 SequenceVar* const sequence_;
640 const int index_;
641};
642
643class RankFirstIntervalVars : public DecisionBuilder {
644 public:
645 RankFirstIntervalVars(const std::vector<SequenceVar*>& sequences,
647 : sequences_(sequences), strategy_(str) {}
648
649 ~RankFirstIntervalVars() override {}
650
651 Decision* Next(Solver* const s) override {
652 SequenceVar* best_sequence = nullptr;
653 best_possible_firsts_.clear();
654 while (true) {
655 if (FindSequenceVar(s, &best_sequence)) {
656 // No not create a choice point if it is not needed.
657 DCHECK(best_sequence != nullptr);
658 if (best_possible_firsts_.size() == 1 &&
659 best_sequence->Interval(best_possible_firsts_.back())
660 ->MustBePerformed()) {
661 best_sequence->RankFirst(best_possible_firsts_.back());
662 continue;
663 }
664 int best_interval = -1;
665 if (!FindIntervalVar(s, best_sequence, &best_interval)) {
666 s->Fail();
667 }
668 CHECK_NE(-1, best_interval);
669 return s->RevAlloc(new RankFirst(best_sequence, best_interval));
670 } else {
671 return nullptr;
672 }
673 }
674 }
675
676 void Accept(ModelVisitor* const visitor) const override {
677 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
678 visitor->VisitSequenceArrayArgument(ModelVisitor::kSequencesArgument,
679 sequences_);
680 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
681 }
682
683 private:
684 // Selects the interval var to rank.
685 bool FindIntervalVarOnStartMin(Solver* const s,
686 SequenceVar* const best_sequence,
687 int* const best_interval_index) {
688 int best_interval = -1;
689 int64_t best_start_min = std::numeric_limits<int64_t>::max();
690 for (int index = 0; index < best_possible_firsts_.size(); ++index) {
691 const int candidate = best_possible_firsts_[index];
692 IntervalVar* const interval = best_sequence->Interval(candidate);
693 if (interval->StartMin() < best_start_min) {
694 best_interval = candidate;
695 best_start_min = interval->StartMin();
696 }
697 }
698 if (best_interval == -1) {
699 return false;
700 } else {
701 *best_interval_index = best_interval;
702 return true;
703 }
704 }
705
706 bool FindIntervalVarRandomly(Solver* const s,
707 SequenceVar* const best_sequence,
708 int* const best_interval_index) {
709 DCHECK(!best_possible_firsts_.empty());
710 const int index = s->Rand32(best_possible_firsts_.size());
711 *best_interval_index = best_possible_firsts_[index];
712 return true;
713 }
714
715 bool FindIntervalVar(Solver* const s, SequenceVar* const best_sequence,
716 int* const best_interval_index) {
717 switch (strategy_) {
721 return FindIntervalVarOnStartMin(s, best_sequence, best_interval_index);
723 return FindIntervalVarRandomly(s, best_sequence, best_interval_index);
724 default:
725 LOG(FATAL) << "Unknown strategy " << strategy_;
726 return false;
727 }
728 }
729
730 // Selects the sequence var to start ranking.
731 bool FindSequenceVarOnSlack(Solver* const s,
732 SequenceVar** const best_sequence) {
733 int64_t best_slack = std::numeric_limits<int64_t>::max();
734 int64_t best_ahmin = std::numeric_limits<int64_t>::max();
735 *best_sequence = nullptr;
736 best_possible_firsts_.clear();
737 for (int i = 0; i < sequences_.size(); ++i) {
738 SequenceVar* const candidate_sequence = sequences_[i];
739 int ranked = 0;
740 int not_ranked = 0;
741 int unperformed = 0;
742 candidate_sequence->ComputeStatistics(&ranked, &not_ranked, &unperformed);
743 if (not_ranked > 0) {
744 candidate_possible_firsts_.clear();
745 candidate_possible_lasts_.clear();
746 candidate_sequence->ComputePossibleFirstsAndLasts(
747 &candidate_possible_firsts_, &candidate_possible_lasts_);
748 // No possible first, failing.
749 if (candidate_possible_firsts_.empty()) {
750 s->Fail();
751 }
752 // Only 1 candidate, and non optional: ranking without branching.
753 if (candidate_possible_firsts_.size() == 1 &&
754 candidate_sequence->Interval(candidate_possible_firsts_.back())
755 ->MustBePerformed()) {
756 *best_sequence = candidate_sequence;
757 best_possible_firsts_ = candidate_possible_firsts_;
758 return true;
759 }
760
761 // Evaluating the sequence.
762 int64_t hmin, hmax, dmin, dmax;
763 candidate_sequence->HorizonRange(&hmin, &hmax);
764 candidate_sequence->DurationRange(&dmin, &dmax);
765 int64_t ahmin, ahmax;
766 candidate_sequence->ActiveHorizonRange(&ahmin, &ahmax);
767 const int64_t current_slack = (hmax - hmin - dmax);
768 if (current_slack < best_slack ||
769 (current_slack == best_slack && ahmin < best_ahmin)) {
770 best_slack = current_slack;
771 *best_sequence = candidate_sequence;
772 best_possible_firsts_ = candidate_possible_firsts_;
773 best_ahmin = ahmin;
774 }
775 }
776 }
777 return *best_sequence != nullptr;
778 }
779
780 bool FindSequenceVarRandomly(Solver* const s,
781 SequenceVar** const best_sequence) {
782 std::vector<SequenceVar*> all_candidates;
783 std::vector<std::vector<int>> all_possible_firsts;
784 for (int i = 0; i < sequences_.size(); ++i) {
785 SequenceVar* const candidate_sequence = sequences_[i];
786 int ranked = 0;
787 int not_ranked = 0;
788 int unperformed = 0;
789 candidate_sequence->ComputeStatistics(&ranked, &not_ranked, &unperformed);
790 if (not_ranked > 0) {
791 candidate_possible_firsts_.clear();
792 candidate_possible_lasts_.clear();
793 candidate_sequence->ComputePossibleFirstsAndLasts(
794 &candidate_possible_firsts_, &candidate_possible_lasts_);
795 // No possible first, failing.
796 if (candidate_possible_firsts_.empty()) {
797 s->Fail();
798 }
799 // Only 1 candidate, and non optional: ranking without branching.
800 if (candidate_possible_firsts_.size() == 1 &&
801 candidate_sequence->Interval(candidate_possible_firsts_.back())
802 ->MustBePerformed()) {
803 *best_sequence = candidate_sequence;
804 best_possible_firsts_ = candidate_possible_firsts_;
805 return true;
806 }
807
808 all_candidates.push_back(candidate_sequence);
809 all_possible_firsts.push_back(candidate_possible_firsts_);
810 }
811 }
812 if (all_candidates.empty()) {
813 return false;
814 }
815 const int chosen = s->Rand32(all_candidates.size());
816 *best_sequence = all_candidates[chosen];
817 best_possible_firsts_ = std::move(all_possible_firsts[chosen]);
818 return true;
819 }
820
821 bool FindSequenceVar(Solver* const s, SequenceVar** const best_sequence) {
822 switch (strategy_) {
826 return FindSequenceVarOnSlack(s, best_sequence);
828 return FindSequenceVarRandomly(s, best_sequence);
829 default:
830 LOG(FATAL) << "Unknown strategy " << strategy_;
831 }
832 }
833
834 const std::vector<SequenceVar*> sequences_;
835 const Solver::SequenceStrategy strategy_;
836 std::vector<int> best_possible_firsts_;
837 std::vector<int> candidate_possible_firsts_;
838 std::vector<int> candidate_possible_lasts_;
839};
840} // namespace
841
843 int64_t* const marker) {
844 CHECK(var != nullptr);
845 CHECK(marker != nullptr);
846 return RevAlloc(new ScheduleOrPostpone(var, est, marker));
847}
848
850 int64_t* const marker) {
851 CHECK(var != nullptr);
852 CHECK(marker != nullptr);
853 return RevAlloc(new ScheduleOrExpedite(var, est, marker));
854}
855
856DecisionBuilder* Solver::MakePhase(const std::vector<IntervalVar*>& intervals,
857 IntervalStrategy str) {
858 switch (str) {
862 return RevAlloc(new SetTimesForward(intervals));
864 return RevAlloc(new SetTimesBackward(intervals));
865 default:
866 LOG(FATAL) << "Unknown strategy " << str;
867 }
868}
869
871 int index) {
872 CHECK(sequence != nullptr);
873 return RevAlloc(new RankFirst(sequence, index));
874}
875
877 CHECK(sequence != nullptr);
878 return RevAlloc(new RankLast(sequence, index));
879}
880
881DecisionBuilder* Solver::MakePhase(const std::vector<SequenceVar*>& sequences,
882 SequenceStrategy str) {
883 return RevAlloc(new RankFirstIntervalVars(sequences, str));
884}
885
886} // namespace operations_research
virtual int64_t Min() const =0
virtual bool Contains(int64_t v) const =0
virtual int64_t DurationMax() const =0
virtual int64_t EndMax() const =0
virtual bool MayBePerformed() const =0
virtual int64_t DurationMin() const =0
These methods query, set, and watch the duration of the interval var.
virtual int64_t StartMin() const =0
virtual bool MustBePerformed() const =0
virtual void VisitSequenceVariable(const SequenceVar *variable)
virtual std::string name() const
Object naming.
virtual void RankSequence(SequenceVar *var, const std::vector< int > &rank_first, const std::vector< int > &rank_last, const std::vector< int > &unperformed)=0
virtual void RankNotFirst(SequenceVar *var, int index)=0
virtual void RankNotLast(SequenceVar *var, int index)=0
virtual void RankFirst(SequenceVar *var, int index)=0
SequenceVar modifiers.
virtual void RankLast(SequenceVar *var, int index)=0
void HorizonRange(int64_t *hmin, int64_t *hmax) const
virtual void Accept(ModelVisitor *visitor) const
Accepts the given visitor.
void RankSequence(const std::vector< int > &rank_first, const std::vector< int > &rank_last, const std::vector< int > &unperformed)
IntervalVar * Interval(int index) const
Returns the index_th interval of the sequence.
void ComputePossibleFirstsAndLasts(std::vector< int > *possible_firsts, std::vector< int > *possible_lasts)
int64_t size() const
Returns the number of interval vars in the sequence.
IntVar * Next(int index) const
Returns the next of the index_th interval of the sequence.
SequenceVar(Solver *s, const std::vector< IntervalVar * > &intervals, const std::vector< IntVar * > &nexts, const std::string &name)
--— SequenceVar --—
void ComputeStatistics(int *ranked, int *not_ranked, int *unperformed) const
Compute statistics on the sequence.
void FillSequence(std::vector< int > *rank_first, std::vector< int > *rank_last, std::vector< int > *unperformed) const
std::string DebugString() const override
void ActiveHorizonRange(int64_t *hmin, int64_t *hmax) const
void DurationRange(int64_t *dmin, int64_t *dmax) const
For the time being, Solver is neither MT_SAFE nor MT_HOT.
Decision * MakeRankLastInterval(SequenceVar *sequence, int index)
DecisionBuilder * MakePhase(const std::vector< IntVar * > &vars, IntVarStrategy var_str, IntValueStrategy val_str)
Definition search.cc:2140
Decision * MakeScheduleOrExpedite(IntervalVar *var, int64_t est, int64_t *marker)
SequenceStrategy
Used for scheduling. Not yet implemented.
Decision * MakeRankFirstInterval(SequenceVar *sequence, int index)
Decision * MakeScheduleOrPostpone(IntervalVar *var, int64_t est, int64_t *marker)
@ INTERVAL_DEFAULT
The default is INTERVAL_SET_TIMES_FORWARD.
@ INTERVAL_SIMPLE
The simple is INTERVAL_SET_TIMES_FORWARD.
PropagationMonitor * GetPropagationMonitor() const
Returns the propagation monitor.
In SWIG mode, we don't want anything besides these top-level includes.
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)
Join v[i]->DebugString().
bool Next()