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