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