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"
26#include "ortools/base/macros.h"
29#include "ortools/sat/integer.h"
31#include "ortools/sat/model.h"
36
37namespace operations_research {
38namespace sat {
39
40DEFINE_INT_TYPE(IntervalVariable, int32_t);
41const 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,
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.
161struct 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
467 return integer_trail_->IsFixed(ends_[t]);
468}
469
471 return integer_trail_->IsFixed(sizes_[t]);
472}
473
475 return reason_for_presence_[t] != kNoLiteralIndex;
476}
477
479 if (reason_for_presence_[t] == kNoLiteralIndex) return true;
480 return trail_->Assignment().LiteralIsTrue(Literal(reason_for_presence_[t]));
481}
482
483inline 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
515}
516
517inline 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
621inline std::function<IntegerVariable(const Model&)> StartVar(
622 IntervalVariable v) {
623 return [=](const Model& model) {
624 return model.Get<IntervalsRepository>()->StartVar(v);
625 };
626}
627
628inline std::function<IntegerVariable(const Model&)> EndVar(IntervalVariable v) {
629 return [=](const Model& model) {
630 return model.Get<IntervalsRepository>()->EndVar(v);
631 };
632}
633
634inline std::function<IntegerVariable(const Model&)> SizeVar(
635 IntervalVariable v) {
636 return [=](const Model& model) {
637 return model.Get<IntervalsRepository>()->SizeVar(v);
638 };
639}
640
641inline 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
647inline 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
653inline std::function<bool(const Model&)> IsOptional(IntervalVariable v) {
654 return [=](const Model& model) {
655 return model.Get<IntervalsRepository>()->IsOptional(v);
656 };
657}
658
659inline std::function<Literal(const Model&)> IsPresentLiteral(
660 IntervalVariable v) {
661 return [=](const Model& model) {
662 return model.Get<IntervalsRepository>()->PresenceLiteral(v);
663 };
664}
665
666inline 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
677inline 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
685inline 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
696inline 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
706inline std::function<IntervalVariable(Model*)>
707NewOptionalIntervalWithOptionalVariables(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
723inline 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
732inline std::function<IntervalVariable(Model*)>
733NewOptionalIntervalWithVariableSize(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.
746inline 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_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:491
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
#define DCHECK(condition)
Definition: base/logging.h:885
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
An Assignment is a variable -> domains mapping, used to report solutions to the user.
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1353
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1349
void MarkIntegerVariableAsOptional(IntegerVariable i, Literal is_considered)
Definition: integer.h:673
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:846
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1345
bool IsOptional(IntegerVariable i) const
Definition: integer.h:656
IntegerVariable SizeVar(IntervalVariable i) const
Definition: intervals.h:99
AffineExpression End(IntervalVariable i) const
Definition: intervals.h:96
IntegerValue MaxSize(IntervalVariable i) const
Definition: intervals.h:127
AffineExpression Start(IntervalVariable i) const
Definition: intervals.h:95
Literal PresenceLiteral(IntervalVariable i) const
Definition: intervals.h:75
std::vector< IntervalVariable > AllIntervals() const
Definition: intervals.h:132
IntegerVariable StartVar(IntervalVariable i) const
Definition: intervals.h:106
IntegerValue MinSize(IntervalVariable i) const
Definition: intervals.h:122
IntegerVariable EndVar(IntervalVariable i) const
Definition: intervals.h:113
bool IsPresent(IntervalVariable i) const
Definition: intervals.h:78
AffineExpression Size(IntervalVariable i) const
Definition: intervals.h:94
bool IsAbsent(IntervalVariable i) const
Definition: intervals.h:82
bool IsOptional(IntervalVariable i) const
Definition: intervals.h:72
IntervalVariable CreateInterval(IntegerVariable start, IntegerVariable end, IntegerVariable size, IntegerValue fixed_size, LiteralIndex is_present)
Definition: intervals.cc:26
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:426
const std::vector< AffineExpression > & Starts() const
Definition: intervals.h:336
const std::vector< TaskTime > & TaskByDecreasingEndMax()
Definition: intervals.cc:350
std::vector< IntegerLiteral > * MutableIntegerReason()
Definition: intervals.h:313
ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t)
Definition: intervals.cc:471
SchedulingConstraintHelper(const std::vector< IntervalVariable > &tasks, Model *model)
Definition: intervals.cc:67
const std::vector< TaskTime > & TaskByIncreasingStartMin()
Definition: intervals.cc:313
void AddStartMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:561
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:508
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition: intervals.cc:114
const std::vector< TaskTime > & TaskByIncreasingEndMin()
Definition: intervals.cc:325
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
Definition: intervals.cc:218
void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time)
Definition: intervals.h:590
ABSL_MUST_USE_RESULT bool PushIntegerLiteralIfTaskPresent(int t, IntegerLiteral lit)
Definition: intervals.cc:431
void RegisterWith(GenericLiteralWatcher *watcher)
Definition: intervals.cc:133
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:575
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
Definition: intervals.cc:453
const std::vector< AffineExpression > & Sizes() const
Definition: intervals.h:338
IntegerValue GetMinOverlap(int t, IntegerValue start, IntegerValue end) const
Definition: intervals.cc:559
void AddSizeMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:554
void SetOtherHelper(SchedulingConstraintHelper *other_helper, absl::Span< const int > map_to_other_helper, IntegerValue event)
Definition: intervals.h:355
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
Definition: intervals.cc:295
const std::vector< TaskTime > & TaskByDecreasingStartMax()
Definition: intervals.cc:337
ABSL_MUST_USE_RESULT bool PushTaskPresence(int t)
Definition: intervals.cc:487
void AddEndMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:583
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
Definition: intervals.h:600
const std::vector< TaskTime > & TaskByIncreasingShiftedStartMin()
Definition: intervals.cc:362
void AddReasonForBeingBefore(int before, int after)
Definition: intervals.cc:382
void AddStartMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:568
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_end_max)
Definition: intervals.cc:462
const std::vector< AffineExpression > & Ends() const
Definition: intervals.h:337
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
int64_t b
int64_t a
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
GRBmodel * model
std::tuple< int64_t, int64_t, const double > Coefficient
std::function< int64_t(const Model &)> MaxSize(IntervalVariable v)
Definition: intervals.h:647
std::function< IntegerVariable(const Model &)> SizeVar(IntervalVariable v)
Definition: intervals.h:634
std::function< void(Model *)> Equality(IntegerVariable v, int64_t value)
Definition: integer.h:1582
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< Literal(const Model &)> IsPresentLiteral(IntervalVariable v)
Definition: intervals.h:659
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:685
std::function< IntegerVariable(const Model &)> EndVar(IntervalVariable v)
Definition: intervals.h:628
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:666
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:696
DEFINE_INT_TYPE(ClauseIndex, int)
std::function< IntervalVariable(Model *)> NewOptionalIntervalWithOptionalVariables(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:707
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:29
std::function< IntegerVariable(const Model &)> StartVar(IntervalVariable v)
Definition: intervals.h:621
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1483
std::function< int64_t(const Model &)> MinSize(IntervalVariable v)
Definition: intervals.h:641
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 *)> IntervalWithAlternatives(IntervalVariable parent, const std::vector< IntervalVariable > &members)
Definition: intervals.h:746
std::function< bool(const Model &)> IsOptional(IntervalVariable v)
Definition: intervals.h:653
Collection of objects used to extend the Constraint Solver library.
int index
Definition: pack.cc:509
int64_t time
Definition: resource.cc:1691
IntegerLiteral LowerOrEqual(IntegerValue bound) const
Definition: integer.h:1339
bool operator<(TaskTime other) const
Definition: intervals.h:164
bool operator>(TaskTime other) const
Definition: intervals.h:165