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