OR-Tools  9.6
disjunctive.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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_DISJUNCTIVE_H_
15 #define OR_TOOLS_SAT_DISJUNCTIVE_H_
16 
17 #include <algorithm>
18 #include <functional>
19 #include <vector>
20 
21 #include "ortools/base/macros.h"
22 #include "ortools/sat/integer.h"
23 #include "ortools/sat/intervals.h"
24 #include "ortools/sat/model.h"
26 #include "ortools/sat/sat_base.h"
27 #include "ortools/sat/theta_tree.h"
29 
30 namespace operations_research {
31 namespace sat {
32 
33 // Enforces a disjunctive (or no overlap) constraint on the given interval
34 // variables. The intervals are interpreted as [start, end) and the constraint
35 // enforces that no time point belongs to two intervals.
36 //
37 // TODO(user): This is not completely true for empty intervals (start == end).
38 // Make sure such intervals are ignored by the constraint.
39 std::function<void(Model*)> Disjunctive(
40  const std::vector<IntervalVariable>& intervals);
41 
42 // Creates Boolean variables for all the possible precedences of the form (task
43 // i is before task j) and forces that, for each couple of task (i,j), either i
44 // is before j or j is before i. Do not create any other propagators.
46  const std::vector<IntervalVariable>& intervals, Model* model);
47 
48 // Same as Disjunctive() + DisjunctiveWithBooleanPrecedencesOnly().
50  const std::vector<IntervalVariable>& intervals, Model* model);
51 
52 // Helper class to compute the end-min of a set of tasks given their start-min
53 // and size-min. In Petr Vilim's PhD "Global Constraints in Scheduling",
54 // this corresponds to his Theta-tree except that we use a O(n) implementation
55 // for most of the function here, not a O(log(n)) one.
56 class TaskSet {
57  public:
58  explicit TaskSet(int num_tasks) { sorted_tasks_.reserve(num_tasks); }
59 
60  struct Entry {
61  int task;
62  IntegerValue start_min;
63  IntegerValue size_min;
64 
65  // Note that the tie-breaking is not important here.
66  bool operator<(Entry other) const { return start_min < other.start_min; }
67  };
68 
69  // Insertion and modification. These leave sorted_tasks_ sorted.
70  void Clear() {
71  sorted_tasks_.clear();
72  optimized_restart_ = 0;
73  }
74  void AddEntry(const Entry& e);
75 
76  // Same as AddEntry({t, helper->ShiftedStartMin(t), helper->SizeMin(t)}).
77  // This is a minor optimization to not call SizeMin(t) twice.
78  void AddShiftedStartMinEntry(const SchedulingConstraintHelper& helper, int t);
79 
80  // Advanced usage, if the entry is present, this assumes that its start_min is
81  // >= the end min without it, and update the datastructure accordingly.
82  void NotifyEntryIsNowLastIfPresent(const Entry& e);
83 
84  // Advanced usage. Instead of calling many AddEntry(), it is more efficient to
85  // call AddUnsortedEntry() instead, but then Sort() MUST be called just after
86  // the insertions. Nothing is checked here, so it is up to the client to do
87  // that properly.
88  void AddUnsortedEntry(const Entry& e) { sorted_tasks_.push_back(e); }
89  void Sort() { std::sort(sorted_tasks_.begin(), sorted_tasks_.end()); }
90 
91  // Returns the end-min for the task in the set. The time profile of the tasks
92  // packed to the left will always be a set of contiguous tasks separated by
93  // empty space:
94  //
95  // [Bunch of tasks] ... [Bunch of tasks] ... [critical tasks].
96  //
97  // We call "critical tasks" the last group. These tasks will be solely
98  // responsible for the end-min of the whole set. The returned
99  // critical_index will be the index of the first critical task in
100  // SortedTasks().
101  //
102  // A reason for the min end is:
103  // - The size-min of all the critical tasks.
104  // - The fact that all critical tasks have a start-min greater or equal to the
105  // first of them, that is SortedTasks()[critical_index].start_min.
106  //
107  // It is possible to behave like if one task was not in the set by setting
108  // task_to_ignore to the id of this task. This returns 0 if the set is empty
109  // in which case critical_index will be left unchanged.
110  IntegerValue ComputeEndMin(int task_to_ignore, int* critical_index) const;
111  IntegerValue ComputeEndMin() const;
112 
113  // Warning, this is only valid if ComputeEndMin() was just called. It is the
114  // same index as if one called ComputeEndMin(-1, &critical_index), but saves
115  // another unneeded loop.
116  int GetCriticalIndex() const { return optimized_restart_; }
117 
118  const std::vector<Entry>& SortedTasks() const { return sorted_tasks_; }
119 
120  private:
121  std::vector<Entry> sorted_tasks_;
122  mutable int optimized_restart_ = 0;
123 };
124 
125 // ============================================================================
126 // Below are many of the known propagation techniques for the disjunctive, each
127 // implemented in only one time direction and in its own propagator class. The
128 // Disjunctive() model function above will instantiate the used ones (according
129 // to the solver parameters) in both time directions.
130 //
131 // See Petr Vilim PhD "Global Constraints in Scheduling" for a description of
132 // some of the algorithm.
133 // ============================================================================
134 
136  public:
138  : helper_(helper) {
139  // Resize this once and for all.
140  task_to_event_.resize(helper_->NumTasks());
141  }
142  bool Propagate() final;
143  int RegisterWith(GenericLiteralWatcher* watcher);
144 
145  private:
146  bool PropagateSubwindow(IntegerValue global_window_end);
147 
149 
150  std::vector<TaskTime> window_;
151  std::vector<TaskTime> task_by_increasing_end_max_;
152 
153  ThetaLambdaTree<IntegerValue> theta_tree_;
154  std::vector<int> task_to_event_;
155 };
156 
158  public:
159  DisjunctiveDetectablePrecedences(bool time_direction,
161  : time_direction_(time_direction),
162  helper_(helper),
163  task_set_(helper->NumTasks()) {}
164  bool Propagate() final;
165  int RegisterWith(GenericLiteralWatcher* watcher);
166 
167  private:
168  bool PropagateSubwindow();
169 
170  std::vector<TaskTime> task_by_increasing_end_min_;
171  std::vector<TaskTime> task_by_increasing_start_max_;
172 
173  std::vector<bool> processed_;
174  std::vector<int> to_propagate_;
175 
176  const bool time_direction_;
178  TaskSet task_set_;
179 };
180 
181 // Singleton model class which is just a SchedulingConstraintHelper will all
182 // the intervals.
184  public:
187  model->GetOrCreate<IntervalsRepository>()->AllIntervals(), model) {}
188 };
189 
190 // This propagates the same things as DisjunctiveDetectablePrecedences, except
191 // that it only sort the full set of intervals once and then work on a combined
192 // set of disjunctives.
193 template <bool time_direction>
195  public:
196  explicit CombinedDisjunctive(Model* model);
197 
198  // After creation, this must be called for all the disjunctive constraints
199  // in the model.
200  void AddNoOverlap(const std::vector<IntervalVariable>& var);
201 
202  bool Propagate() final;
203 
204  private:
205  AllIntervalsHelper* helper_;
206  std::vector<std::vector<int>> task_to_disjunctives_;
207  std::vector<bool> task_is_added_;
208  std::vector<TaskSet> task_sets_;
209  std::vector<IntegerValue> end_mins_;
210 };
211 
213  public:
214  DisjunctiveNotLast(bool time_direction, SchedulingConstraintHelper* helper)
215  : time_direction_(time_direction),
216  helper_(helper),
217  task_set_(helper->NumTasks()) {}
218  bool Propagate() final;
219  int RegisterWith(GenericLiteralWatcher* watcher);
220 
221  private:
222  bool PropagateSubwindow();
223 
224  std::vector<TaskTime> start_min_window_;
225  std::vector<TaskTime> start_max_window_;
226 
227  const bool time_direction_;
229  TaskSet task_set_;
230 };
231 
233  public:
234  DisjunctiveEdgeFinding(bool time_direction,
236  : time_direction_(time_direction), helper_(helper) {}
237  bool Propagate() final;
238  int RegisterWith(GenericLiteralWatcher* watcher);
239 
240  private:
241  bool PropagateSubwindow(IntegerValue window_end_min);
242 
243  const bool time_direction_;
245 
246  // This only contains non-gray tasks.
247  std::vector<TaskTime> task_by_increasing_end_max_;
248 
249  // All these member are indexed in the same way.
250  std::vector<TaskTime> window_;
251  ThetaLambdaTree<IntegerValue> theta_tree_;
252  std::vector<IntegerValue> event_size_;
253 
254  // Task indexed.
255  std::vector<int> non_gray_task_to_event_;
256  std::vector<bool> is_gray_;
257 };
258 
259 // Exploits the precedences relations of the form "this set of disjoint
260 // IntervalVariables must be performed before a given IntegerVariable". The
261 // relations are computed with PrecedencesPropagator::ComputePrecedences().
263  public:
264  DisjunctivePrecedences(bool time_direction,
266  IntegerTrail* integer_trail,
267  PrecedencesPropagator* precedences)
268  : time_direction_(time_direction),
269  helper_(helper),
270  integer_trail_(integer_trail),
271  precedences_(precedences),
272  task_set_(helper->NumTasks()),
273  task_to_arc_index_(helper->NumTasks()) {}
274  bool Propagate() final;
275  int RegisterWith(GenericLiteralWatcher* watcher);
276 
277  private:
278  bool PropagateSubwindow();
279 
280  const bool time_direction_;
282  IntegerTrail* integer_trail_;
283  PrecedencesPropagator* precedences_;
284 
285  std::vector<TaskTime> window_;
286  std::vector<IntegerVariable> index_to_end_vars_;
287 
288  TaskSet task_set_;
289  std::vector<int> task_to_arc_index_;
290  std::vector<PrecedencesPropagator::IntegerPrecedences> before_;
291 };
292 
293 // This is an optimization for the case when we have a big number of such
294 // pairwise constraints. This should be roughtly equivalent to what the general
295 // disjunctive case is doing, but it dealt with variable size better and has a
296 // lot less overhead.
298  public:
300  : helper_(helper) {}
301  bool Propagate() final;
302  int RegisterWith(GenericLiteralWatcher* watcher);
303 
304  private:
306 };
307 
308 } // namespace sat
309 } // namespace operations_research
310 
311 #endif // OR_TOOLS_SAT_DISJUNCTIVE_H_
DisjunctiveDetectablePrecedences(bool time_direction, SchedulingConstraintHelper *helper)
Definition: disjunctive.h:159
DisjunctiveEdgeFinding(bool time_direction, SchedulingConstraintHelper *helper)
Definition: disjunctive.h:234
DisjunctiveNotLast(bool time_direction, SchedulingConstraintHelper *helper)
Definition: disjunctive.h:214
DisjunctiveOverloadChecker(SchedulingConstraintHelper *helper)
Definition: disjunctive.h:137
int RegisterWith(GenericLiteralWatcher *watcher)
Definition: disjunctive.cc:684
DisjunctivePrecedences(bool time_direction, SchedulingConstraintHelper *helper, IntegerTrail *integer_trail, PrecedencesPropagator *precedences)
Definition: disjunctive.h:264
DisjunctiveWithTwoItems(SchedulingConstraintHelper *helper)
Definition: disjunctive.h:299
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
void AddUnsortedEntry(const Entry &e)
Definition: disjunctive.h:88
void NotifyEntryIsNowLastIfPresent(const Entry &e)
Definition: disjunctive.cc:236
void AddShiftedStartMinEntry(const SchedulingConstraintHelper &helper, int t)
Definition: disjunctive.cc:230
IntegerValue ComputeEndMin() const
Definition: disjunctive.cc:251
void AddEntry(const Entry &e)
Definition: disjunctive.cc:215
const std::vector< Entry > & SortedTasks() const
Definition: disjunctive.h:118
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
void AddDisjunctiveWithBooleanPrecedencesOnly(const std::vector< IntervalVariable > &intervals, Model *model)
Definition: disjunctive.cc:142
void AddDisjunctiveWithBooleanPrecedences(const std::vector< IntervalVariable > &intervals, Model *model)
Definition: disjunctive.cc:209
std::function< void(Model *)> Disjunctive(const std::vector< IntervalVariable > &intervals)
Definition: disjunctive.cc:39
Collection of objects used to extend the Constraint Solver library.
Definition: disjunctive.h:60
int task
Definition: disjunctive.h:61
IntegerValue size_min
Definition: disjunctive.h:63
bool operator<(Entry other) const
Definition: disjunctive.h:66
IntegerValue start_min
Definition: disjunctive.h:62