OR-Tools  9.0
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"
22 #include "ortools/base/logging.h"
26 
27 namespace operations_research {
28 namespace {
29 int64_t ValueToIndex(int64_t value) { return value - 1; }
30 
31 int64_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) {
47  set_name(name);
48 }
49 
51 
53  return intervals_[index];
54 }
55 
56 IntVar* SequenceVar::Next(int index) const { return nexts_[index]; }
57 
58 std::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 
73 void SequenceVar::Accept(ModelVisitor* const visitor) const {
74  visitor->VisitSequenceVariable(this);
75 }
76 
77 void 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 
94 void 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 
109 void 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 (!gtl::ContainsKey(decided, 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 
147 void 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 
174 int 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 
182 int 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 
269 void 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 
338 void 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 
349 void 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
386 namespace {
387 //
388 // Forward scheduling.
389 //
390 class 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 
424 class 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 //
501 class 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 
535 class 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 
591 class 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 
616 class 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 
640 class 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 
853 DecisionBuilder* 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 
878 DecisionBuilder* 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:498
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:894
#define CHECK_NE(val1, val2)
Definition: base/logging.h:706
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:896
#define LOG(severity)
Definition: base/logging.h:423
#define DCHECK(condition)
Definition: base/logging.h:892
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.
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:2017
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
const int FATAL
Definition: log_severity.h:32
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
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
int index
Definition: pack.cc:509
IntervalVar * interval
Definition: resource.cc:100