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