OR-Tools  9.0
intervals.h
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 #ifndef OR_TOOLS_SAT_INTERVALS_H_
15 #define OR_TOOLS_SAT_INTERVALS_H_
16 
17 #include <cstdint>
18 #include <functional>
19 #include <vector>
20 
21 #include "absl/types/span.h"
22 #include "ortools/base/int_type.h"
24 #include "ortools/base/logging.h"
25 #include "ortools/base/macros.h"
28 #include "ortools/sat/integer.h"
30 #include "ortools/sat/model.h"
33 #include "ortools/sat/sat_base.h"
34 #include "ortools/sat/sat_solver.h"
35 
36 namespace operations_research {
37 namespace sat {
38 
39 DEFINE_INT_TYPE(IntervalVariable, int32_t);
40 const IntervalVariable kNoIntervalVariable(-1);
41 
42 // This class maintains a set of intervals which correspond to three integer
43 // variables (start, end and size). It automatically registers with the
44 // PrecedencesPropagator the relation between the bounds of each interval and
45 // provides many helper functions to add precedences relation between intervals.
47  public:
49  : model_(model),
50  assignment_(model->GetOrCreate<Trail>()->Assignment()),
51  integer_trail_(model->GetOrCreate<IntegerTrail>()) {}
52 
53  // Returns the current number of intervals in the repository.
54  // The interval will always be identified by an integer in [0, num_intervals).
55  int NumIntervals() const { return starts_.size(); }
56 
57  // Functions to add a new interval to the repository.
58  // If add_linear_relation is true, then we also link start, size and end.
59  //
60  // - If size == kNoIntegerVariable, then the size is fixed to fixed_size.
61  // - If is_present != kNoLiteralIndex, then this is an optional interval.
62  IntervalVariable CreateInterval(IntegerVariable start, IntegerVariable end,
63  IntegerVariable size, IntegerValue fixed_size,
64  LiteralIndex is_present);
65  IntervalVariable CreateInterval(AffineExpression start, AffineExpression end,
66  AffineExpression size,
67  LiteralIndex is_present,
68  bool add_linear_relation);
69 
70  // Returns whether or not a interval is optional and the associated literal.
71  bool IsOptional(IntervalVariable i) const {
72  return is_present_[i] != kNoLiteralIndex;
73  }
74  Literal PresenceLiteral(IntervalVariable i) const {
75  return Literal(is_present_[i]);
76  }
77  bool IsPresent(IntervalVariable i) const {
78  if (!IsOptional(i)) return true;
79  return assignment_.LiteralIsTrue(PresenceLiteral(i));
80  }
81  bool IsAbsent(IntervalVariable i) const {
82  if (!IsOptional(i)) return false;
83  return assignment_.LiteralIsFalse(PresenceLiteral(i));
84  }
85 
86  // The 3 integer variables associated to a interval.
87  // Fixed size intervals will have a kNoIntegerVariable as size.
88  //
89  // Note: For an optional interval, the start/end variables are propagated
90  // asssuming the interval is present. Because of that, these variables can
91  // cross each other or have an empty domain. If any of this happen, then the
92  // PresenceLiteral() of this interval will be propagated to false.
93  AffineExpression Size(IntervalVariable i) const { return sizes_[i]; }
94  AffineExpression Start(IntervalVariable i) const { return starts_[i]; }
95  AffineExpression End(IntervalVariable i) const { return ends_[i]; }
96 
97  // Deprecated.
98  IntegerVariable SizeVar(IntervalVariable i) const {
99  if (sizes_[i].var != kNoIntegerVariable) {
100  CHECK_EQ(sizes_[i].coeff, 1);
101  CHECK_EQ(sizes_[i].constant, 0);
102  }
103  return sizes_[i].var;
104  }
105  IntegerVariable StartVar(IntervalVariable i) const {
106  if (starts_[i].var != kNoIntegerVariable) {
107  CHECK_EQ(starts_[i].coeff, 1);
108  CHECK_EQ(starts_[i].constant, 0);
109  }
110  return starts_[i].var;
111  }
112  IntegerVariable EndVar(IntervalVariable i) const {
113  if (ends_[i].var != kNoIntegerVariable) {
114  CHECK_EQ(ends_[i].coeff, 1);
115  CHECK_EQ(ends_[i].constant, 0);
116  }
117  return ends_[i].var;
118  }
119 
120  // Return the minimum size of the given IntervalVariable.
121  IntegerValue MinSize(IntervalVariable i) const {
122  return integer_trail_->LowerBound(sizes_[i]);
123  }
124 
125  // Return the maximum size of the given IntervalVariable.
126  IntegerValue MaxSize(IntervalVariable i) const {
127  return integer_trail_->UpperBound(sizes_[i]);
128  }
129 
130  // Utility function that returns a vector will all intervals.
131  std::vector<IntervalVariable> AllIntervals() const {
132  std::vector<IntervalVariable> result;
133  for (IntervalVariable i(0); i < NumIntervals(); ++i) {
134  result.push_back(i);
135  }
136  return result;
137  }
138 
139  private:
140  // External classes needed.
141  Model* model_;
142  const VariablesAssignment& assignment_;
143  IntegerTrail* integer_trail_;
144 
145  // Literal indicating if the tasks is executed. Tasks that are always executed
146  // will have a kNoLiteralIndex entry in this vector.
148 
149  // The integer variables for each tasks.
153 
154  DISALLOW_COPY_AND_ASSIGN(IntervalsRepository);
155 };
156 
157 // An helper struct to sort task by time. This is used by the
158 // SchedulingConstraintHelper but also by many scheduling propagators to sort
159 // tasks.
160 struct TaskTime {
162  IntegerValue time;
163  bool operator<(TaskTime other) const { return time < other.time; }
164  bool operator>(TaskTime other) const { return time > other.time; }
165 };
166 
167 // Helper class shared by the propagators that manage a given list of tasks.
168 //
169 // One of the main advantage of this class is that it allows to share the
170 // vectors of tasks sorted by various criteria between propagator for a faster
171 // code.
174  public:
175  // All the functions below refer to a task by its index t in the tasks
176  // vector given at construction.
177  SchedulingConstraintHelper(const std::vector<IntervalVariable>& tasks,
178  Model* model);
179 
180  // Temporary constructor.
181  // The class will not be usable until ResetFromSubset() is called.
182  //
183  // TODO(user): Remove this. It is a hack because the disjunctive class needs
184  // to fetch the maximum possible number of task at construction.
185  SchedulingConstraintHelper(int num_tasks, Model* model);
186 
187  // This is a propagator so we can "cache" all the intervals relevant
188  // information. This gives good speedup. Note however that the info is stale
189  // except if a bound was pushed by this helper or if this was called. We run
190  // it at the highest priority, so that will mostly be the case at the
191  // beginning of each Propagate() call of the classes using this.
192  bool Propagate() final;
193  bool IncrementalPropagate(const std::vector<int>& watch_indices) final;
194  void RegisterWith(GenericLiteralWatcher* watcher);
195  void SetLevel(int level) final;
196 
197  // Resets the class to the same state as if it was constructed with
198  // the given subset of tasks from other.
200  absl::Span<const int> tasks);
201 
202  // Returns the number of task.
203  int NumTasks() const { return starts_.size(); }
204 
205  // Make sure the cached values are up to date. Also sets the time direction to
206  // either forward/backward. This will impact all the functions below. This
207  // MUST be called at the beginning of all Propagate() call that uses this
208  // helper.
209  void SynchronizeAndSetTimeDirection(bool is_forward);
210 
211  // Helpers for the current bounds on the current task time window.
212  // [ (size-min) ... (size-min) ]
213  // ^ ^ ^ ^
214  // start-min end-min start-max end-max
215  //
216  // Note that for tasks with variable durations, we don't necessarily have
217  // duration-min between the XXX-min and XXX-max value.
218  //
219  // Remark: We use cached values for most of these function as this is faster.
220  // In practice, the cache will almost always be up to date, but not in corner
221  // cases where pushing the start of one task will change values for many
222  // others. This is fine as the new values will be picked up as we reach the
223  // propagation fixed point.
224  IntegerValue SizeMin(int t) const { return cached_duration_min_[t]; }
225  IntegerValue SizeMax(int t) const {
226  // This one is "rare" so we don't cache it.
227  return integer_trail_->UpperBound(sizes_[t]);
228  }
229  IntegerValue StartMin(int t) const { return cached_start_min_[t]; }
230  IntegerValue EndMin(int t) const { return cached_end_min_[t]; }
231  IntegerValue StartMax(int t) const { return -cached_negated_start_max_[t]; }
232  IntegerValue EndMax(int t) const { return -cached_negated_end_max_[t]; }
233 
234  // In the presence of tasks with a variable size, we do not necessarily
235  // have start_min + size_min = end_min, we can instead have a situation
236  // like:
237  // | |<--- size-min --->|
238  // ^ ^ ^
239  // start-min | end-min
240  // |
241  // We define the "shifted start min" to be the right most time such that
242  // we known that we must have min-size "energy" to the right of it if the
243  // task is present. Using it in our scheduling propagators allows to propagate
244  // more in the presence of tasks with variable size (or optional task
245  // where we also do not necessarily have start_min + size_min = end_min.
246  //
247  // To explain this shifted start min, one must use the AddEnergyAfterReason().
248  IntegerValue ShiftedStartMin(int t) const {
249  return cached_shifted_start_min_[t];
250  }
251 
252  bool StartIsFixed(int t) const;
253  bool EndIsFixed(int t) const;
254  bool SizeIsFixed(int t) const;
255 
256  // Returns true if the corresponding fact is known for sure. A normal task is
257  // always present. For optional task for which the presence is still unknown,
258  // both of these function will return false.
259  bool IsOptional(int t) const;
260  bool IsPresent(int t) const;
261  bool IsAbsent(int t) const;
262 
263  // Returns a string with the current task bounds.
264  std::string TaskDebugString(int t) const;
265 
266  // Sorts and returns the tasks in corresponding order at the time of the call.
267  // Note that we do not mean strictly-increasing/strictly-decreasing, there
268  // will be duplicate time values in these vectors.
269  //
270  // TODO(user): we could merge the first loop of IncrementalSort() with the
271  // loop that fill TaskTime.time at each call.
272  const std::vector<TaskTime>& TaskByIncreasingStartMin();
273  const std::vector<TaskTime>& TaskByIncreasingEndMin();
274  const std::vector<TaskTime>& TaskByDecreasingStartMax();
275  const std::vector<TaskTime>& TaskByDecreasingEndMax();
276  const std::vector<TaskTime>& TaskByIncreasingShiftedStartMin();
277 
278  // Functions to clear and then set the current reason.
279  void ClearReason();
280  void AddPresenceReason(int t);
281  void AddAbsenceReason(int t);
282  void AddSizeMinReason(int t);
283  void AddSizeMinReason(int t, IntegerValue lower_bound);
284  void AddStartMinReason(int t, IntegerValue lower_bound);
285  void AddStartMaxReason(int t, IntegerValue upper_bound);
286  void AddEndMinReason(int t, IntegerValue lower_bound);
287  void AddEndMaxReason(int t, IntegerValue upper_bound);
288  void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time);
289 
290  // Adds the reason why task "before" must be before task "after".
291  // That is StartMax(before) < EndMin(after).
292  void AddReasonForBeingBefore(int before, int after);
293 
294  // It is also possible to directly manipulates the underlying reason vectors
295  // that will be used when pushing something.
296  std::vector<Literal>* MutableLiteralReason() { return &literal_reason_; }
297  std::vector<IntegerLiteral>* MutableIntegerReason() {
298  return &integer_reason_;
299  }
300 
301  // Push something using the current reason. Note that IncreaseStartMin() will
302  // also increase the end-min, and DecreaseEndMax() will also decrease the
303  // start-max.
304  //
305  // Important: IncreaseStartMin() and DecreaseEndMax() can be called on an
306  // optional interval whose presence is still unknown and push a bound
307  // conditionned on its presence. The functions will do the correct thing
308  // depending on whether or not the start_min/end_max are optional variables
309  // whose presence implies the interval presence.
310  ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min);
311  ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_start_max);
312  ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t);
313  ABSL_MUST_USE_RESULT bool PushTaskPresence(int t);
314  ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral lit);
315  ABSL_MUST_USE_RESULT bool ReportConflict();
316  ABSL_MUST_USE_RESULT bool PushIntegerLiteralIfTaskPresent(int t,
317  IntegerLiteral lit);
318 
319  // Returns the underlying affine expressions.
320  const std::vector<AffineExpression>& Starts() const { return starts_; }
321  const std::vector<AffineExpression>& Ends() const { return ends_; }
322  const std::vector<AffineExpression>& Sizes() const { return sizes_; }
325  return Literal(reason_for_presence_[index]);
326  }
327 
328  // Registers the given propagator id to be called if any of the tasks
329  // in this class change. Note that we do not watch size max though.
330  void WatchAllTasks(int id, GenericLiteralWatcher* watcher,
331  bool watch_start_max = true,
332  bool watch_end_max = true) const;
333 
334  // Manages the other helper (used by the diffn constraint).
335  //
336  // For each interval appearing in a reason on this helper, another reason
337  // will be added. This other reason specifies that on the other helper, the
338  // corresponding interval overlaps 'event'.
340  IntegerValue event) {
341  CHECK(other_helper != nullptr);
342  other_helper_ = other_helper;
343  event_for_other_helper_ = event;
344  }
345 
346  void ClearOtherHelper() { other_helper_ = nullptr; }
347 
348  // Adds to this helper reason all the explanation of the other helper.
349  // This checks that other_helper_ is null.
350  //
351  // This is used in the 2D energetic reasoning in the diffn constraint.
352  void ImportOtherReasons(const SchedulingConstraintHelper& other_helper);
353 
354  // TODO(user): Change the propagation loop code so that we don't stop
355  // pushing in the middle of the propagation as more advanced propagator do
356  // not handle this correctly.
357  bool InPropagationLoop() const { return integer_trail_->InPropagationLoop(); }
358 
359  private:
360  void InitSortedVectors();
361  void UpdateCachedValues(int t);
362 
363  // Internal function for IncreaseStartMin()/DecreaseEndMax().
364  bool PushIntervalBound(int t, IntegerLiteral lit);
365 
366  // This will be called on any interval that is part of a reason or
367  // a bound push. Since the last call to ClearReason(), for each unique
368  // t, we will add once to other_helper_ the reason for t containing
369  // the point event_for_other_helper_.
370  void AddOtherReason(int t);
371 
372  // Import the reasons on the other helper into this helper.
373  void ImportOtherReasons();
374 
375  Trail* trail_;
376  IntegerTrail* integer_trail_;
377  PrecedencesPropagator* precedences_;
378 
379  // The current direction of time, true for forward, false for backward.
380  bool current_time_direction_ = true;
381 
382  // All the underlying variables of the tasks.
383  // The vectors are indexed by the task index t.
384  std::vector<AffineExpression> starts_;
385  std::vector<AffineExpression> ends_;
386  std::vector<AffineExpression> sizes_;
387  std::vector<LiteralIndex> reason_for_presence_;
388 
389  // The negation of the start/end variable so that SetTimeDirection()
390  // can do its job in O(1) instead of calling NegationOf() on each entry.
391  std::vector<AffineExpression> minus_starts_;
392  std::vector<AffineExpression> minus_ends_;
393 
394  // This is used by SetLevel() to dected untrail.
395  int previous_level_ = 0;
396 
397  // The caches of all relevant interval values.
398  std::vector<IntegerValue> cached_duration_min_;
399  std::vector<IntegerValue> cached_start_min_;
400  std::vector<IntegerValue> cached_end_min_;
401  std::vector<IntegerValue> cached_negated_start_max_;
402  std::vector<IntegerValue> cached_negated_end_max_;
403  std::vector<IntegerValue> cached_shifted_start_min_;
404  std::vector<IntegerValue> cached_negated_shifted_end_max_;
405 
406  // Sorted vectors returned by the TasksBy*() functions.
407  std::vector<TaskTime> task_by_increasing_start_min_;
408  std::vector<TaskTime> task_by_increasing_end_min_;
409  std::vector<TaskTime> task_by_decreasing_start_max_;
410  std::vector<TaskTime> task_by_decreasing_end_max_;
411 
412  // This one is the most commonly used, so we optimized a bit more its
413  // computation by detecting when there is nothing to do.
414  std::vector<TaskTime> task_by_increasing_shifted_start_min_;
415  std::vector<TaskTime> task_by_negated_shifted_end_max_;
416  bool recompute_shifted_start_min_ = true;
417  bool recompute_negated_shifted_end_max_ = true;
418 
419  // If recompute_cache_[t] is true, then we need to update all the cached
420  // value for the task t in SynchronizeAndSetTimeDirection().
421  bool recompute_all_cache_ = true;
422  std::vector<bool> recompute_cache_;
423 
424  // Reason vectors.
425  std::vector<Literal> literal_reason_;
426  std::vector<IntegerLiteral> integer_reason_;
427 
428  // Optional 'proxy' helper used in the diffn constraint.
429  SchedulingConstraintHelper* other_helper_ = nullptr;
430  IntegerValue event_for_other_helper_;
431  std::vector<bool> already_added_to_other_reasons_;
432 };
433 
434 // =============================================================================
435 // SchedulingConstraintHelper inlined functions.
436 // =============================================================================
437 
439  return integer_trail_->IsFixed(starts_[t]);
440 }
441 
442 inline bool SchedulingConstraintHelper::EndIsFixed(int t) const {
443  return integer_trail_->IsFixed(ends_[t]);
444 }
445 
446 inline bool SchedulingConstraintHelper::SizeIsFixed(int t) const {
447  return integer_trail_->IsFixed(sizes_[t]);
448 }
449 
450 inline bool SchedulingConstraintHelper::IsOptional(int t) const {
451  return reason_for_presence_[t] != kNoLiteralIndex;
452 }
453 
454 inline bool SchedulingConstraintHelper::IsPresent(int t) const {
455  if (reason_for_presence_[t] == kNoLiteralIndex) return true;
456  return trail_->Assignment().LiteralIsTrue(Literal(reason_for_presence_[t]));
457 }
458 
459 inline bool SchedulingConstraintHelper::IsAbsent(int t) const {
460  if (reason_for_presence_[t] == kNoLiteralIndex) return false;
461  return trail_->Assignment().LiteralIsFalse(Literal(reason_for_presence_[t]));
462 }
463 
465  integer_reason_.clear();
466  literal_reason_.clear();
467  if (other_helper_) {
468  other_helper_->ClearReason();
469  already_added_to_other_reasons_.assign(NumTasks(), false);
470  }
471 }
472 
474  DCHECK(IsPresent(t));
475  AddOtherReason(t);
476  if (reason_for_presence_[t] != kNoLiteralIndex) {
477  literal_reason_.push_back(Literal(reason_for_presence_[t]).Negated());
478  }
479 }
480 
482  DCHECK(IsAbsent(t));
483  AddOtherReason(t);
484  if (reason_for_presence_[t] != kNoLiteralIndex) {
485  literal_reason_.push_back(Literal(reason_for_presence_[t]));
486  }
487 }
488 
490  AddOtherReason(t);
491  if (sizes_[t].var != kNoIntegerVariable) {
492  integer_reason_.push_back(
493  integer_trail_->LowerBoundAsLiteral(sizes_[t].var));
494  }
495 }
496 
498  int t, IntegerValue lower_bound) {
499  AddOtherReason(t);
500  if (sizes_[t].var != kNoIntegerVariable) {
501  integer_reason_.push_back(sizes_[t].GreaterOrEqual(lower_bound));
502  }
503 }
504 
506  int t, IntegerValue lower_bound) {
508  AddOtherReason(t);
509  if (starts_[t].var != kNoIntegerVariable) {
510  integer_reason_.push_back(starts_[t].GreaterOrEqual(lower_bound));
511  }
512 }
513 
515  int t, IntegerValue upper_bound) {
516  AddOtherReason(t);
517 
518  // Note that we cannot use the cache here!
519  if (integer_trail_->UpperBound(starts_[t]) <= upper_bound) {
520  if (starts_[t].var != kNoIntegerVariable) {
521  integer_reason_.push_back(starts_[t].LowerOrEqual(upper_bound));
522  }
523  } else {
524  // This might happen if we used StartMax() <= EndMax() - SizeMin().
525  if (sizes_[t].var != kNoIntegerVariable) {
526  integer_reason_.push_back(
527  integer_trail_->LowerBoundAsLiteral(sizes_[t].var));
528  }
529  if (ends_[t].var != kNoIntegerVariable) {
530  integer_reason_.push_back(
531  ends_[t].LowerOrEqual(upper_bound + SizeMin(t)));
532  }
533  }
534 }
535 
537  int t, IntegerValue lower_bound) {
538  AddOtherReason(t);
539 
540  // Note that we cannot use the cache here!
541  if (integer_trail_->LowerBound(ends_[t]) >= lower_bound) {
542  if (ends_[t].var != kNoIntegerVariable) {
543  integer_reason_.push_back(ends_[t].GreaterOrEqual(lower_bound));
544  }
545  } else {
546  // This might happen if we used EndMin() >= StartMin() + SizeMin().
547  if (sizes_[t].var != kNoIntegerVariable) {
548  integer_reason_.push_back(
549  integer_trail_->LowerBoundAsLiteral(sizes_[t].var));
550  }
551  if (starts_[t].var != kNoIntegerVariable) {
552  integer_reason_.push_back(
553  starts_[t].GreaterOrEqual(lower_bound - SizeMin(t)));
554  }
555  }
556 }
557 
559  int t, IntegerValue upper_bound) {
561  AddOtherReason(t);
562  if (ends_[t].var != kNoIntegerVariable) {
563  integer_reason_.push_back(ends_[t].LowerOrEqual(upper_bound));
564  }
565 }
566 
568  int t, IntegerValue energy_min, IntegerValue time) {
569  AddOtherReason(t);
570  if (StartMin(t) >= time) {
571  if (starts_[t].var != kNoIntegerVariable) {
572  integer_reason_.push_back(starts_[t].GreaterOrEqual(time));
573  }
574  } else {
575  if (ends_[t].var != kNoIntegerVariable) {
576  integer_reason_.push_back(ends_[t].GreaterOrEqual(time + energy_min));
577  }
578  }
579  if (sizes_[t].var != kNoIntegerVariable) {
580  integer_reason_.push_back(sizes_[t].GreaterOrEqual(energy_min));
581  }
582 }
583 
584 // =============================================================================
585 // Model based functions.
586 // =============================================================================
587 
588 inline std::function<IntegerVariable(const Model&)> StartVar(
589  IntervalVariable v) {
590  return [=](const Model& model) {
591  return model.Get<IntervalsRepository>()->StartVar(v);
592  };
593 }
594 
595 inline std::function<IntegerVariable(const Model&)> EndVar(IntervalVariable v) {
596  return [=](const Model& model) {
597  return model.Get<IntervalsRepository>()->EndVar(v);
598  };
599 }
600 
601 inline std::function<IntegerVariable(const Model&)> SizeVar(
602  IntervalVariable v) {
603  return [=](const Model& model) {
604  return model.Get<IntervalsRepository>()->SizeVar(v);
605  };
606 }
607 
608 inline std::function<int64_t(const Model&)> MinSize(IntervalVariable v) {
609  return [=](const Model& model) {
610  return model.Get<IntervalsRepository>()->MinSize(v).value();
611  };
612 }
613 
614 inline std::function<int64_t(const Model&)> MaxSize(IntervalVariable v) {
615  return [=](const Model& model) {
616  return model.Get<IntervalsRepository>()->MaxSize(v).value();
617  };
618 }
619 
620 inline std::function<bool(const Model&)> IsOptional(IntervalVariable v) {
621  return [=](const Model& model) {
622  return model.Get<IntervalsRepository>()->IsOptional(v);
623  };
624 }
625 
626 inline std::function<Literal(const Model&)> IsPresentLiteral(
627  IntervalVariable v) {
628  return [=](const Model& model) {
629  return model.Get<IntervalsRepository>()->PresenceLiteral(v);
630  };
631 }
632 
633 inline std::function<IntervalVariable(Model*)> NewInterval(int64_t min_start,
634  int64_t max_end,
635  int64_t size) {
636  return [=](Model* model) {
637  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
638  model->Add(NewIntegerVariable(min_start, max_end)),
639  model->Add(NewIntegerVariable(min_start, max_end)), kNoIntegerVariable,
640  IntegerValue(size), kNoLiteralIndex);
641  };
642 }
643 
644 inline std::function<IntervalVariable(Model*)> NewInterval(
645  IntegerVariable start, IntegerVariable end, IntegerVariable size) {
646  return [=](Model* model) {
647  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
648  start, end, size, IntegerValue(0), kNoLiteralIndex);
649  };
650 }
651 
652 inline std::function<IntervalVariable(Model*)> NewIntervalWithVariableSize(
653  int64_t min_start, int64_t max_end, int64_t min_size, int64_t max_size) {
654  return [=](Model* model) {
655  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
656  model->Add(NewIntegerVariable(min_start, max_end)),
657  model->Add(NewIntegerVariable(min_start, max_end)),
658  model->Add(NewIntegerVariable(min_size, max_size)), IntegerValue(0),
660  };
661 }
662 
663 inline std::function<IntervalVariable(Model*)> NewOptionalInterval(
664  int64_t min_start, int64_t max_end, int64_t size, Literal is_present) {
665  return [=](Model* model) {
666  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
667  model->Add(NewIntegerVariable(min_start, max_end)),
668  model->Add(NewIntegerVariable(min_start, max_end)), kNoIntegerVariable,
669  IntegerValue(size), is_present.Index());
670  };
671 }
672 
673 inline std::function<IntervalVariable(Model*)>
674 NewOptionalIntervalWithOptionalVariables(int64_t min_start, int64_t max_end,
675  int64_t size, Literal is_present) {
676  return [=](Model* model) {
677  // Note that we need to mark the optionality first.
678  const IntegerVariable start =
679  model->Add(NewIntegerVariable(min_start, max_end));
680  const IntegerVariable end =
681  model->Add(NewIntegerVariable(min_start, max_end));
682  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
683  integer_trail->MarkIntegerVariableAsOptional(start, is_present);
684  integer_trail->MarkIntegerVariableAsOptional(end, is_present);
685  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
686  start, end, kNoIntegerVariable, IntegerValue(size), is_present.Index());
687  };
688 }
689 
690 inline std::function<IntervalVariable(Model*)> NewOptionalInterval(
691  IntegerVariable start, IntegerVariable end, IntegerVariable size,
692  Literal is_present) {
693  return [=](Model* model) {
694  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
695  start, end, size, IntegerValue(0), is_present.Index());
696  };
697 }
698 
699 inline std::function<IntervalVariable(Model*)>
700 NewOptionalIntervalWithVariableSize(int64_t min_start, int64_t max_end,
701  int64_t min_size, int64_t max_size,
702  Literal is_present) {
703  return [=](Model* model) {
704  return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
705  model->Add(NewIntegerVariable(min_start, max_end)),
706  model->Add(NewIntegerVariable(min_start, max_end)),
707  model->Add(NewIntegerVariable(min_size, max_size)), IntegerValue(0),
708  is_present.Index());
709  };
710 }
711 
712 // This requires that all the alternatives are optional tasks.
713 inline std::function<void(Model*)> IntervalWithAlternatives(
714  IntervalVariable parent, const std::vector<IntervalVariable>& members) {
715  return [=](Model* model) {
716  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
717  auto* intervals = model->GetOrCreate<IntervalsRepository>();
718 
719  std::vector<Literal> presences;
720  std::vector<IntegerValue> sizes;
721 
722  // Create an "exactly one executed" constraint on the alternatives.
723  std::vector<LiteralWithCoeff> sat_ct;
724  for (const IntervalVariable member : members) {
725  CHECK(intervals->IsOptional(member));
726  const Literal is_present = intervals->PresenceLiteral(member);
727  sat_ct.push_back({is_present, Coefficient(1)});
728  model->Add(
729  Equality(model->Get(StartVar(parent)), model->Get(StartVar(member))));
730  model->Add(
731  Equality(model->Get(EndVar(parent)), model->Get(EndVar(member))));
732 
733  // TODO(user): IsOneOf() only work for members with fixed size.
734  // Generalize to an "int_var_element" constraint.
735  CHECK(integer_trail->IsFixed(intervals->Size(member)));
736  presences.push_back(is_present);
737  sizes.push_back(intervals->MinSize(member));
738  }
739  if (intervals->SizeVar(parent) != kNoIntegerVariable) {
740  model->Add(IsOneOf(intervals->SizeVar(parent), presences, sizes));
741  }
742  model->Add(BooleanLinearConstraint(1, 1, &sat_ct));
743 
744  // Propagate from the candidate bounds to the parent interval ones.
745  {
746  std::vector<IntegerVariable> starts;
747  starts.reserve(members.size());
748  for (const IntervalVariable member : members) {
749  starts.push_back(intervals->StartVar(member));
750  }
751  model->Add(
752  PartialIsOneOfVar(intervals->StartVar(parent), starts, presences));
753  }
754  {
755  std::vector<IntegerVariable> ends;
756  ends.reserve(members.size());
757  for (const IntervalVariable member : members) {
758  ends.push_back(intervals->EndVar(member));
759  }
760  model->Add(PartialIsOneOfVar(intervals->EndVar(parent), ends, presences));
761  }
762  };
763 }
764 
765 } // namespace sat
766 } // namespace operations_research
767 
768 #endif // OR_TOOLS_SAT_INTERVALS_H_
#define CHECK(condition)
Definition: base/logging.h:498
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:895
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK(condition)
Definition: base/logging.h:892
An Assignment is a variable -> domains mapping, used to report solutions to the user.
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1313
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1335
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1309
void MarkIntegerVariableAsOptional(IntegerVariable i, Literal is_considered)
Definition: integer.h:644
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1305
bool IsOptional(IntegerVariable i) const
Definition: integer.h:627
IntegerVariable SizeVar(IntervalVariable i) const
Definition: intervals.h:98
AffineExpression End(IntervalVariable i) const
Definition: intervals.h:95
IntegerValue MaxSize(IntervalVariable i) const
Definition: intervals.h:126
AffineExpression Start(IntervalVariable i) const
Definition: intervals.h:94
Literal PresenceLiteral(IntervalVariable i) const
Definition: intervals.h:74
IntegerVariable StartVar(IntervalVariable i) const
Definition: intervals.h:105
IntegerValue MinSize(IntervalVariable i) const
Definition: intervals.h:121
IntegerVariable EndVar(IntervalVariable i) const
Definition: intervals.h:112
bool IsPresent(IntervalVariable i) const
Definition: intervals.h:77
AffineExpression Size(IntervalVariable i) const
Definition: intervals.h:93
std::vector< IntervalVariable > AllIntervals() const
Definition: intervals.h:131
bool IsAbsent(IntervalVariable i) const
Definition: intervals.h:81
bool IsOptional(IntervalVariable i) const
Definition: intervals.h:71
IntervalVariable CreateInterval(IntegerVariable start, IntegerVariable end, IntegerVariable size, IntegerValue fixed_size, LiteralIndex is_present)
Definition: intervals.cc:24
LiteralIndex Index() const
Definition: sat_base.h:85
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral lit)
Definition: intervals.cc:378
const std::vector< TaskTime > & TaskByDecreasingEndMax()
Definition: intervals.cc:302
ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t)
Definition: intervals.cc:423
SchedulingConstraintHelper(const std::vector< IntervalVariable > &tasks, Model *model)
Definition: intervals.cc:65
const std::vector< TaskTime > & TaskByIncreasingStartMin()
Definition: intervals.cc:265
void AddStartMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:505
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:460
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition: intervals.cc:110
const std::vector< TaskTime > & TaskByIncreasingEndMin()
Definition: intervals.cc:277
void ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
Definition: intervals.cc:176
std::vector< IntegerLiteral > * MutableIntegerReason()
Definition: intervals.h:297
void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time)
Definition: intervals.h:567
ABSL_MUST_USE_RESULT bool PushIntegerLiteralIfTaskPresent(int t, IntegerLiteral lit)
Definition: intervals.cc:383
void RegisterWith(GenericLiteralWatcher *watcher)
Definition: intervals.cc:129
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:536
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
Definition: intervals.cc:405
const std::vector< AffineExpression > & Starts() const
Definition: intervals.h:320
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_start_max)
Definition: intervals.cc:414
const std::vector< TaskTime > & TaskByDecreasingStartMax()
Definition: intervals.cc:289
ABSL_MUST_USE_RESULT bool PushTaskPresence(int t)
Definition: intervals.cc:439
void AddEndMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:558
void SetOtherHelper(SchedulingConstraintHelper *other_helper, IntegerValue event)
Definition: intervals.h:339
const std::vector< TaskTime > & TaskByIncreasingShiftedStartMin()
Definition: intervals.cc:314
void AddReasonForBeingBefore(int before, int after)
Definition: intervals.cc:334
const std::vector< AffineExpression > & Sizes() const
Definition: intervals.h:322
void AddStartMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:514
const std::vector< AffineExpression > & Ends() const
Definition: intervals.h:321
const VariablesAssignment & Assignment() const
Definition: sat_base.h:381
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:151
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:148
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
GRBmodel * model
std::function< IntegerVariable(const Model &)> SizeVar(IntervalVariable v)
Definition: intervals.h:601
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
Definition: integer.h:1500
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
Definition: intervals.h:633
std::function< IntervalVariable(Model *)> NewOptionalIntervalWithVariableSize(int64_t min_start, int64_t max_end, int64_t min_size, int64_t max_size, Literal is_present)
Definition: intervals.h:700
std::function< Literal(const Model &)> IsPresentLiteral(IntervalVariable v)
Definition: intervals.h:626
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> PartialIsOneOfVar(IntegerVariable target_var, const std::vector< IntegerVariable > &vars, const std::vector< Literal > &selectors)
std::function< void(Model *)> IsOneOf(IntegerVariable var, const std::vector< Literal > &selectors, const std::vector< IntegerValue > &values)
const IntegerVariable kNoIntegerVariable(-1)
const IntervalVariable kNoIntervalVariable(-1)
std::function< IntegerVariable(const Model &)> EndVar(IntervalVariable v)
Definition: intervals.h:595
std::function< IntervalVariable(Model *)> NewIntervalWithVariableSize(int64_t min_start, int64_t max_end, int64_t min_size, int64_t max_size)
Definition: intervals.h:652
std::function< int64_t(const Model &)> MinSize(IntervalVariable v)
Definition: intervals.h:608
std::function< int64_t(const Model &)> MaxSize(IntervalVariable v)
Definition: intervals.h:614
DEFINE_INT_TYPE(ClauseIndex, int)
std::function< bool(const Model &)> IsOptional(IntervalVariable v)
Definition: intervals.h:620
std::function< void(Model *)> BooleanLinearConstraint(int64_t lower_bound, int64_t upper_bound, std::vector< LiteralWithCoeff > *cst)
Definition: sat_solver.h:853
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
Definition: integer.h:1515
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1431
std::function< IntegerVariable(const Model &)> StartVar(IntervalVariable v)
Definition: intervals.h:588
std::function< void(Model *)> Equality(IntegerVariable v, int64_t value)
Definition: integer.h:1530
std::function< void(Model *)> IntervalWithAlternatives(IntervalVariable parent, const std::vector< IntervalVariable > &members)
Definition: intervals.h:713
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:663
std::function< IntervalVariable(Model *)> NewOptionalIntervalWithOptionalVariables(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:674
Collection of objects used to extend the Constraint Solver library.
int index
Definition: pack.cc:509
int64_t time
Definition: resource.cc:1691
bool operator<(TaskTime other) const
Definition: intervals.h:163
bool operator>(TaskTime other) const
Definition: intervals.h:164