Google OR-Tools: ortools/constraint_solver/sched_search.cc Source File
22#include "absl/container/flat_hash_set.h"
24#include "absl/strings/str_format.h"
32int64_t ValueToIndex(int64_t value) { return value - 1; }
34int64_t IndexToValue(int64_t index) { return index + 1; }
43 const std::vector<IntervalVar*>& intervals,
44 const std::vector<IntVar*>& nexts,
45 const std::string& name)
49 previous_(nexts.size() + 1, -1) {
62 int64_t hmin, hmax, dmin, dmax;
70 "%s(horizon = %d..%d, duration = %d..%d, not ranked = %d, ranked = %d, "
72 name(), hmin, hmax, dmin, dmax, not_ranked, ranked,
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) {
104 hor_min = std::min(hor_min, t->StartMin());
105 hor_max = std::max(hor_max, t->EndMax());
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()) {
121 while (nexts_[first]->Bound()) {
122 first = nexts_[first]->Min();
123 if (first < nexts_.size()) {
124 decided.insert(ValueToIndex(first));
129 if (first != nexts_.size()) {
132 while (previous_[last] != -1) {
134 decided.insert(ValueToIndex(last));
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)) {
142 hor_min = std::min(hor_min, t->StartMin());
143 hor_max = std::max(hor_max, t->EndMax());
151 int* const unperformed) const {
153 for (int i = 0; i < intervals_.size(); ++i) {
154 if (intervals_[i]->CannotBePerformed()) {
160 while (first < nexts_.size() && nexts_[first]->Bound()) {
161 first = nexts_[first]->Min();
164 if (first != nexts_.size()) {
167 while (previous_[last] != -1) {
174 *not_ranked = intervals_.size() - *ranked - *unperformed;
177int SequenceVar::ComputeForwardFrontier() {
179 while (first != nexts_.size() && nexts_[first]->Bound()) {
180 first = nexts_[first]->Min();
185int SequenceVar::ComputeBackwardFrontier() {
188 while (previous_[last] != -1) {
195 std::vector<int>* const possible_firsts,
196 std::vector<int>* const possible_lasts) {
199 absl::flat_hash_set<int> to_check;
200 for (int i = 0; i < intervals_.size(); ++i) {
201 if (intervals_[i]->MayBePerformed()) {
206 while (nexts_[first]->Bound()) {
207 first = nexts_[first]->Min();
208 if (first == nexts_.size()) {
211 to_check.erase(ValueToIndex(first));
214 IntVar* const forward_var = nexts_[first];
215 std::vector<int> candidates;
216 int64_t smallest_start_max = std::numeric_limits<int64_t>::max();
218 for (int64_t i = forward_var->Min(); i <= forward_var->Max(); ++i) {
220 if (i != 0 && i < IndexToValue(intervals_.size()) &&
221 intervals_[ValueToIndex(i)]->MayBePerformed() &&
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();
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);
243 while (previous_[last] != -1) {
245 to_check.erase(ValueToIndex(last));
249 int64_t biggest_end_min = std::numeric_limits<int64_t>::min();
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();
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) {
273 const std::vector<int>& rank_last,
274 const std::vector<int>& unperformed) {
278 for (const int value : unperformed) {
279 intervals_[value]->SetPerformed(false);
283 for (int i = 0; i < rank_first.size(); ++i) {
284 const int next = 1 + rank_first[i];
285 nexts_[forward]->SetValue(next);
289 int backward = IndexToValue(intervals_.size());
290 for (int i = 0; i < rank_last.size(); ++i) {
291 const int next = 1 + rank_last[i];
299 intervals_[index]->SetPerformed(true);
301 while (forward_frontier != nexts_.size() &&
302 nexts_[forward_frontier]->Bound()) {
303 forward_frontier = nexts_[forward_frontier]->Min();
304 if (forward_frontier == IndexToValue(index)) {
308 DCHECK_LT(forward_frontier, nexts_.size());
309 nexts_[forward_frontier]->SetValue(IndexToValue(index));
314 const int forward_frontier = ComputeForwardFrontier();
315 if (forward_frontier < nexts_.size()) {
316 nexts_[forward_frontier]->RemoveValue(IndexToValue(index));
322 intervals_[index]->SetPerformed(true);
324 int backward_frontier = nexts_.size();
325 while (previous_[backward_frontier] != -1) {
326 backward_frontier = previous_[backward_frontier];
327 if (backward_frontier == IndexToValue(index)) {
331 DCHECK_NE(backward_frontier, 0);
332 nexts_[IndexToValue(index)]->SetValue(backward_frontier);
337 const int backward_frontier = ComputeBackwardFrontier();
338 nexts_[IndexToValue(index)]->RemoveValue(backward_frontier);
341void SequenceVar::UpdatePrevious() const {
342 for (int i = 0; i < intervals_.size() + 2; ++i) {
345 for (int i = 0; i < nexts_.size(); ++i) {
347 previous_[nexts_[i]->Min()] = i;
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);
361 for (int i = 0; i < intervals_.size(); ++i) {
362 if (intervals_[i]->CannotBePerformed()) {
363 unperformed->push_back(i);
367 while (nexts_[first]->Bound()) {
368 first = nexts_[first]->Min();
369 if (first < nexts_.size()) {
370 rank_first->push_back(ValueToIndex(first));
375 if (first != nexts_.size()) {
378 while (previous_[last] != -1) {
393class ScheduleOrPostpone : public Decision {
395 ScheduleOrPostpone(IntervalVar* const var, int64_t est, int64_t* const marker)
396 : var_(var), est_(est), marker_(marker) {}
397 ~ScheduleOrPostpone() override {}
399 void Apply(Solver* const s) override {
401 if (est_.Value() < var_->StartMin()) {
402 est_.SetValue(s, var_->StartMin());
404 var_->SetStartRange(est_.Value(), est_.Value());
407 void Refute(Solver* const s) override {
408 s->SaveAndSetValue(marker_, est_.Value());
411 void Accept(DecisionVisitor* const visitor) const override {
412 CHECK(visitor != nullptr);
413 visitor->VisitScheduleOrPostpone(var_, est_.Value());
416 std::string DebugString() const override {
417 return absl::StrFormat("ScheduleOrPostpone(%s at %d)", var_->DebugString(),
423 NumericalRev<int64_t> est_;
429 explicit SetTimesForward(const std::vector<IntervalVar*>& vars)
431 markers_(vars.size(), std::numeric_limits<int64_t>::min()) {}
433 ~SetTimesForward() override {}
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();
442 for (int i = 0; i < vars_.size(); ++i) {
443 IntervalVar* const v = vars_[i];
444 if (v->MayBePerformed() && v->StartMax() != v->StartMin() &&
446 (v->StartMin() < best_est ||
447 (v->StartMin() == best_est && v->EndMax() < best_lct))) {
450 support = i;
456 UnperformPostponedTaskBefore(std::numeric_limits<int64_t>::max());
459 UnperformPostponedTaskBefore(best_est);
461 new ScheduleOrPostpone(vars_[support], best_est, &markers_[support]));
464 std::string DebugString() const override { return "SetTimesForward()"; }
466 void Accept(ModelVisitor* const visitor) const override {
474 bool IsPostponed(int index) {
475 DCHECK(vars_[index]->MayBePerformed());
476 return vars_[index]->StartMin() <= markers_[index];
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() &&
491 (v->EndMin() <= date || v->StartMax() <= date)) {
497 const std::vector<IntervalVar*> vars_;
498 std::vector<int64_t> markers_;
504class ScheduleOrExpedite : public Decision {
506 ScheduleOrExpedite(IntervalVar* const var, int64_t est, int64_t* const marker)
507 : var_(var), est_(est), marker_(marker) {}
508 ~ScheduleOrExpedite() override {}
510 void Apply(Solver* const s) override {
512 if (est_.Value() > var_->EndMax()) {
513 est_.SetValue(s, var_->EndMax());
515 var_->SetEndRange(est_.Value(), est_.Value());
518 void Refute(Solver* const s) override {
519 s->SaveAndSetValue(marker_, est_.Value() - 1);
522 void Accept(DecisionVisitor* const visitor) const override {
523 CHECK(visitor != nullptr);
524 visitor->VisitScheduleOrExpedite(var_, est_.Value());
527 std::string DebugString() const override {
528 return absl::StrFormat("ScheduleOrExpedite(%s at %d)", var_->DebugString(),
534 NumericalRev<int64_t> est_;
540 explicit SetTimesBackward(const std::vector<IntervalVar*>& vars)
542 markers_(vars.size(), std::numeric_limits<int64_t>::max()) {}
544 ~SetTimesBackward() override {}
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();
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))) {
558 best_start = v->StartMin();
559 support = i;
574 return s->RevAlloc(new ScheduleOrExpedite(
575 vars_[support], vars_[support]->EndMax(), &markers_[support]));
578 std::string DebugString() const override { return "SetTimesBackward()"; }
580 void Accept(ModelVisitor* const visitor) const override {
588 const std::vector<IntervalVar*> vars_;
589 std::vector<int64_t> markers_;
594class RankFirst : public Decision {
596 RankFirst(SequenceVar* const seq, int index)
597 : sequence_(seq), index_(index) {}
600 void Apply(Solver* const s) override { sequence_->RankFirst(index_); }
602 void Refute(Solver* const s) override { sequence_->RankNotFirst(index_); }
604 void Accept(DecisionVisitor* const visitor) const override {
605 CHECK(visitor != nullptr);
606 visitor->VisitRankFirstInterval(sequence_, index_);
609 std::string DebugString() const override {
610 return absl::StrFormat("RankFirst(%s, %d)", sequence_->DebugString(),
615 SequenceVar* const sequence_;
619class RankLast : public Decision {
621 RankLast(SequenceVar* const seq, int index) : sequence_(seq), index_(index) {}
624 void Apply(Solver* const s) override { sequence_->RankLast(index_); }
626 void Refute(Solver* const s) override { sequence_->RankNotLast(index_); }
628 void Accept(DecisionVisitor* const visitor) const override {
629 CHECK(visitor != nullptr);
630 visitor->VisitRankLastInterval(sequence_, index_);
633 std::string DebugString() const override {
634 return absl::StrFormat("RankLast(%s, %d)", sequence_->DebugString(),
639 SequenceVar* const sequence_;
645 RankFirstIntervalVars(const std::vector<SequenceVar*>& sequences,
647 : sequences_(sequences), strategy_(str) {}
649 ~RankFirstIntervalVars() override {}
651 Decision* Next(Solver* const s) override {
652 SequenceVar* best_sequence = nullptr;
653 best_possible_firsts_.clear();
655 if (FindSequenceVar(s, &best_sequence)) {
657 DCHECK(best_sequence != nullptr);
658 if (best_possible_firsts_.size() == 1 &&
659 best_sequence->Interval(best_possible_firsts_.back())
661 best_sequence->RankFirst(best_possible_firsts_.back());
665 if (!FindIntervalVar(s, best_sequence, &best_interval)) {
668 CHECK_NE(-1, best_interval);
669 return s->RevAlloc(new RankFirst(best_sequence, best_interval));
676 void Accept(ModelVisitor* const visitor) const override {
685 bool FindIntervalVarOnStartMin(Solver* const s,
686 SequenceVar* const best_sequence,
687 int* const best_interval_index) {
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();
698 if (best_interval == -1) {
701 *best_interval_index = best_interval;
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];
715 bool FindIntervalVar(Solver* const s, SequenceVar* const best_sequence,
716 int* const best_interval_index) {
721 return FindIntervalVarOnStartMin(s, best_sequence, best_interval_index);
723 return FindIntervalVarRandomly(s, best_sequence, best_interval_index);
725 LOG(FATAL) << "Unknown strategy " << strategy_;
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();
736 best_possible_firsts_.clear();
737 for (int i = 0; i < sequences_.size(); ++i) {
738 SequenceVar* const candidate_sequence = sequences_[i];
742 candidate_sequence->ComputeStatistics(&ranked, ¬_ranked, &unperformed);
744 candidate_possible_firsts_.clear();
745 candidate_possible_lasts_.clear();
746 candidate_sequence->ComputePossibleFirstsAndLasts(
747 &candidate_possible_firsts_, &candidate_possible_lasts_);
749 if (candidate_possible_firsts_.empty()) {
753 if (candidate_possible_firsts_.size() == 1 &&
754 candidate_sequence->Interval(candidate_possible_firsts_.back())
756 *best_sequence = candidate_sequence;
757 best_possible_firsts_ = candidate_possible_firsts_;
762 int64_t hmin, hmax, dmin, dmax;
763 candidate_sequence->HorizonRange(&hmin, &hmax);
764 candidate_sequence->DurationRange(&dmin, &dmax);
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_;
777 return *best_sequence != nullptr;
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];
789 candidate_sequence->ComputeStatistics(&ranked, ¬_ranked, &unperformed);
791 candidate_possible_firsts_.clear();
792 candidate_possible_lasts_.clear();
793 candidate_sequence->ComputePossibleFirstsAndLasts(
794 &candidate_possible_firsts_, &candidate_possible_lasts_);
796 if (candidate_possible_firsts_.empty()) {
800 if (candidate_possible_firsts_.size() == 1 &&
801 candidate_sequence->Interval(candidate_possible_firsts_.back())
803 *best_sequence = candidate_sequence;
804 best_possible_firsts_ = candidate_possible_firsts_;
808 all_candidates.push_back(candidate_sequence);
809 all_possible_firsts.push_back(candidate_possible_firsts_);
812 if (all_candidates.empty()) {
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]);
821 bool FindSequenceVar(Solver* const s, SequenceVar** const best_sequence) {
826 return FindSequenceVarOnSlack(s, best_sequence);
828 return FindSequenceVarRandomly(s, best_sequence);
830 LOG(FATAL) << "Unknown strategy " << strategy_;
834 const std::vector<SequenceVar*> sequences_;
836 std::vector<int> best_possible_firsts_;
837 std::vector<int> candidate_possible_firsts_;
838 std::vector<int> candidate_possible_lasts_;
846 return RevAlloc(new ScheduleOrPostpone(var, est, marker));
853 return RevAlloc(new ScheduleOrExpedite(var, est, marker));
862 return RevAlloc(new SetTimesForward(intervals));
864 return RevAlloc(new SetTimesBackward(intervals));
872 CHECK(sequence != nullptr);
873 return RevAlloc(new RankFirst(sequence, index));
883 return RevAlloc(new RankFirstIntervalVars(sequences, str));
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)
static const char kSequencesArgument[]
static const char kVariableGroupExtension[]
static const char kIntervalsArgument[]
virtual std::string name() const
Object naming.
void set_name(absl::string_view name)
PropagationBaseObject(Solver *const s)
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 RankNotFirst(int index)
Definition sched_search.cc:312
void HorizonRange(int64_t *hmin, int64_t *hmax) const
Definition sched_search.cc:97
~SequenceVar() override
Definition sched_search.cc:53
virtual void Accept(ModelVisitor *visitor) const
Accepts the given visitor.
Definition sched_search.cc:76
void RankNotLast(int index)
Definition sched_search.cc:335
void RankSequence(const std::vector< int > &rank_first, const std::vector< int > &rank_last, const std::vector< int > &unperformed)
Definition sched_search.cc:272
IntervalVar * Interval(int index) const
Returns the index_th interval of the sequence.
Definition sched_search.cc:55
void ComputePossibleFirstsAndLasts(std::vector< int > *possible_firsts, std::vector< int > *possible_lasts)
Definition sched_search.cc:194
void RankLast(int index)
Definition sched_search.cc:320
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.
Definition sched_search.cc:59
SequenceVar(Solver *s, const std::vector< IntervalVar * > &intervals, const std::vector< IntVar * > &nexts, const std::string &name)
Definition sched_search.cc:42
void RankFirst(int index)
Definition sched_search.cc:297
void ComputeStatistics(int *ranked, int *not_ranked, int *unperformed) const
Compute statistics on the sequence.
Definition sched_search.cc:150
void FillSequence(std::vector< int > *rank_first, std::vector< int > *rank_last, std::vector< int > *unperformed) const
Definition sched_search.cc:352
std::string DebugString() const override
Definition sched_search.cc:61
void ActiveHorizonRange(int64_t *hmin, int64_t *hmax) const
Definition sched_search.cc:112
void DurationRange(int64_t *dmin, int64_t *dmax) const
Definition sched_search.cc:80
Decision * MakeRankLastInterval(SequenceVar *sequence, int index)
Definition sched_search.cc:876
DecisionBuilder * MakePhase(const std::vector< IntVar * > &vars, IntVarStrategy var_str, IntValueStrategy val_str)
Phases on IntVar arrays.
Decision * MakeScheduleOrExpedite(IntervalVar *var, int64_t est, int64_t *marker)
Definition sched_search.cc:849
SequenceStrategy
Used for scheduling. Not yet implemented.
@ CHOOSE_RANDOM_RANK_FORWARD
@ CHOOSE_MIN_SLACK_RANK_FORWARD
Decision * MakeRankFirstInterval(SequenceVar *sequence, int index)
Definition sched_search.cc:870
Decision * MakeScheduleOrPostpone(IntervalVar *var, int64_t est, int64_t *marker)
Definition sched_search.cc:842
@ INTERVAL_SET_TIMES_FORWARD
@ INTERVAL_DEFAULT
The default is INTERVAL_SET_TIMES_FORWARD.
@ INTERVAL_SET_TIMES_BACKWARD
@ INTERVAL_SIMPLE
The simple is INTERVAL_SET_TIMES_FORWARD.
PropagationMonitor * GetPropagationMonitor() const
Returns the propagation monitor.
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)