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