OR-Tools  9.2
timetabling.cc
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 #include <cstdint>
15 #include <string>
16 
17 #include "absl/strings/str_format.h"
19 #include "ortools/base/logging.h"
20 #include "ortools/base/macros.h"
23 
24 namespace operations_research {
25 
26 // ----- interval <unary relation> date -----
27 
28 namespace {
29 const char* kUnaryNames[] = {
30  "ENDS_AFTER", "ENDS_AT", "ENDS_BEFORE", "STARTS_AFTER",
31  "STARTS_AT", "STARTS_BEFORE", "CROSS_DATE", "AVOID_DATE",
32 };
33 
34 const char* kBinaryNames[] = {
35  "ENDS_AFTER_END", "ENDS_AFTER_START", "ENDS_AT_END",
36  "ENDS_AT_START", "STARTS_AFTER_END", "STARTS_AFTER_START",
37  "STARTS_AT_END", "STARTS_AT_START", "STAYS_IN_SYNC"};
38 
39 class IntervalUnaryRelation : public Constraint {
40  public:
41  IntervalUnaryRelation(Solver* const s, IntervalVar* const t, int64_t d,
43  : Constraint(s), t_(t), d_(d), rel_(rel) {}
44  ~IntervalUnaryRelation() override {}
45 
46  void Post() override;
47 
48  void InitialPropagate() override;
49 
50  std::string DebugString() const override {
51  return absl::StrFormat("(%s %s %d)", t_->DebugString(), kUnaryNames[rel_],
52  d_);
53  }
54 
55  void Accept(ModelVisitor* const visitor) const override {
56  visitor->BeginVisitConstraint(ModelVisitor::kIntervalUnaryRelation, this);
57  visitor->VisitIntervalArgument(ModelVisitor::kIntervalArgument, t_);
58  visitor->VisitIntegerArgument(ModelVisitor::kRelationArgument, rel_);
59  visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, d_);
60  visitor->EndVisitConstraint(ModelVisitor::kIntervalUnaryRelation, this);
61  }
62 
63  private:
64  IntervalVar* const t_;
65  const int64_t d_;
67 };
68 
69 void IntervalUnaryRelation::Post() {
70  if (t_->MayBePerformed()) {
71  Demon* d = solver()->MakeConstraintInitialPropagateCallback(this);
72  t_->WhenAnything(d);
73  }
74 }
75 
76 void IntervalUnaryRelation::InitialPropagate() {
77  if (t_->MayBePerformed()) {
78  switch (rel_) {
79  case Solver::ENDS_AFTER:
80  t_->SetEndMin(d_);
81  break;
82  case Solver::ENDS_AT:
83  t_->SetEndRange(d_, d_);
84  break;
86  t_->SetEndMax(d_);
87  break;
89  t_->SetStartMin(d_);
90  break;
91  case Solver::STARTS_AT:
92  t_->SetStartRange(d_, d_);
93  break;
94 
96  t_->SetStartMax(d_);
97  break;
98  case Solver::CROSS_DATE:
99  t_->SetStartMax(d_);
100  t_->SetEndMin(d_);
101  break;
102  case Solver::AVOID_DATE:
103  if (t_->EndMin() > d_) {
104  t_->SetStartMin(d_);
105  } else if (t_->StartMax() < d_) {
106  t_->SetEndMax(d_);
107  }
108  break;
109  }
110  }
111 }
112 } // namespace
113 
116  int64_t d) {
117  return RevAlloc(new IntervalUnaryRelation(this, t, d, r));
118 }
119 
120 // ----- interval <binary relation> interval -----
121 
122 namespace {
123 class IntervalBinaryRelation : public Constraint {
124  public:
125  IntervalBinaryRelation(Solver* const s, IntervalVar* const t1,
126  IntervalVar* const t2,
127  Solver::BinaryIntervalRelation rel, int64_t delay)
128  : Constraint(s), t1_(t1), t2_(t2), rel_(rel), delay_(delay) {}
129  ~IntervalBinaryRelation() override {}
130 
131  void Post() override;
132 
133  void InitialPropagate() override;
134 
135  std::string DebugString() const override {
136  return absl::StrFormat("(%s %s %s)", t1_->DebugString(), kBinaryNames[rel_],
137  t2_->DebugString());
138  }
139 
140  void Accept(ModelVisitor* const visitor) const override {
141  visitor->BeginVisitConstraint(ModelVisitor::kIntervalBinaryRelation, this);
142  visitor->VisitIntervalArgument(ModelVisitor::kLeftArgument, t1_);
143  visitor->VisitIntegerArgument(ModelVisitor::kRelationArgument, rel_);
144  visitor->VisitIntervalArgument(ModelVisitor::kRightArgument, t2_);
145  visitor->EndVisitConstraint(ModelVisitor::kIntervalBinaryRelation, this);
146  }
147 
148  private:
149  IntervalVar* const t1_;
150  IntervalVar* const t2_;
152  const int64_t delay_;
153 };
154 
155 void IntervalBinaryRelation::Post() {
156  if (t1_->MayBePerformed() && t2_->MayBePerformed()) {
157  Demon* d = solver()->MakeConstraintInitialPropagateCallback(this);
158  t1_->WhenAnything(d);
159  t2_->WhenAnything(d);
160  }
161 }
162 
163 // TODO(user) : make code more compact, use function pointers?
164 void IntervalBinaryRelation::InitialPropagate() {
165  if (t2_->MustBePerformed() && t1_->MayBePerformed()) {
166  switch (rel_) {
168  t1_->SetEndMin(t2_->EndMin() + delay_);
169  break;
171  t1_->SetEndMin(t2_->StartMin() + delay_);
172  break;
173  case Solver::ENDS_AT_END:
174  t1_->SetEndRange(t2_->EndMin() + delay_, t2_->EndMax() + delay_);
175  break;
177  t1_->SetEndRange(t2_->StartMin() + delay_, t2_->StartMax() + delay_);
178  break;
180  t1_->SetStartMin(t2_->EndMin() + delay_);
181  break;
183  t1_->SetStartMin(t2_->StartMin() + delay_);
184  break;
186  t1_->SetStartRange(t2_->EndMin() + delay_, t2_->EndMax() + delay_);
187  break;
189  t1_->SetStartRange(t2_->StartMin() + delay_, t2_->StartMax() + delay_);
190  break;
192  t1_->SetStartRange(t2_->StartMin() + delay_, t2_->StartMax() + delay_);
193  t1_->SetEndRange(t2_->EndMin() + delay_, t2_->EndMax() + delay_);
194  break;
195  }
196  }
197 
198  if (t1_->MustBePerformed() && t2_->MayBePerformed()) {
199  switch (rel_) {
201  t2_->SetEndMax(t1_->EndMax() - delay_);
202  break;
204  t2_->SetStartMax(t1_->EndMax() - delay_);
205  break;
206  case Solver::ENDS_AT_END:
207  t2_->SetEndRange(t1_->EndMin() - delay_, t1_->EndMax() - delay_);
208  break;
210  t2_->SetStartRange(t1_->EndMin() - delay_, t1_->EndMax() - delay_);
211  break;
213  t2_->SetEndMax(t1_->StartMax() - delay_);
214  break;
216  t2_->SetStartMax(t1_->StartMax() - delay_);
217  break;
219  t2_->SetEndRange(t1_->StartMin() - delay_, t1_->StartMax() - delay_);
220  break;
222  t2_->SetStartRange(t1_->StartMin() - delay_, t1_->StartMax() - delay_);
223  break;
225  t2_->SetStartRange(t1_->StartMin() - delay_, t1_->StartMax() - delay_);
226  t2_->SetEndRange(t1_->EndMin() - delay_, t1_->EndMax() - delay_);
227  break;
228  }
229  }
230 }
231 } // namespace
232 
235  IntervalVar* const t2) {
236  return RevAlloc(new IntervalBinaryRelation(this, t1, t2, r, 0));
237 }
238 
241  IntervalVar* const t2, int64_t delay) {
242  return RevAlloc(new IntervalBinaryRelation(this, t1, t2, r, delay));
243 }
244 
245 // ----- activity a before activity b or activity b before activity a -----
246 
247 namespace {
248 class TemporalDisjunction : public Constraint {
249  public:
250  enum State { ONE_BEFORE_TWO, TWO_BEFORE_ONE, UNDECIDED };
251 
252  TemporalDisjunction(Solver* const s, IntervalVar* const t1,
253  IntervalVar* const t2, IntVar* const alt)
254  : Constraint(s), t1_(t1), t2_(t2), alt_(alt), state_(UNDECIDED) {}
255  ~TemporalDisjunction() override {}
256 
257  void Post() override;
258  void InitialPropagate() override;
259  std::string DebugString() const override;
260 
261  void RangeDemon1();
262  void RangeDemon2();
263  void RangeAlt();
264  void Decide(State s);
265  void TryToDecide();
266 
267  void Accept(ModelVisitor* const visitor) const override {
268  visitor->BeginVisitConstraint(ModelVisitor::kIntervalDisjunction, this);
269  visitor->VisitIntervalArgument(ModelVisitor::kLeftArgument, t1_);
270  visitor->VisitIntervalArgument(ModelVisitor::kRightArgument, t2_);
271  visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
272  alt_);
273  visitor->EndVisitConstraint(ModelVisitor::kIntervalDisjunction, this);
274  }
275 
276  private:
277  IntervalVar* const t1_;
278  IntervalVar* const t2_;
279  IntVar* const alt_;
280  State state_;
281 };
282 
283 void TemporalDisjunction::Post() {
284  Solver* const s = solver();
285  Demon* d = MakeConstraintDemon0(s, this, &TemporalDisjunction::RangeDemon1,
286  "RangeDemon1");
287  t1_->WhenAnything(d);
288  d = MakeConstraintDemon0(s, this, &TemporalDisjunction::RangeDemon2,
289  "RangeDemon2");
290  t2_->WhenAnything(d);
291  if (alt_ != nullptr) {
292  d = MakeConstraintDemon0(s, this, &TemporalDisjunction::RangeAlt,
293  "RangeAlt");
294  alt_->WhenRange(d);
295  }
296 }
297 
298 void TemporalDisjunction::InitialPropagate() {
299  if (alt_ != nullptr) {
300  alt_->SetRange(0, 1);
301  }
302  if (alt_ != nullptr && alt_->Bound()) {
303  RangeAlt();
304  } else {
305  RangeDemon1();
306  RangeDemon2();
307  }
308 }
309 
310 std::string TemporalDisjunction::DebugString() const {
311  std::string out;
312  (out = absl::StrFormat("TemporalDisjunction(%s, %s", t1_->DebugString(),
313  t2_->DebugString()));
314  if (alt_ != nullptr) {
315  absl::StrAppendFormat(&out, " => %s", alt_->DebugString());
316  }
317  out += ") ";
318  return out;
319 }
320 
321 void TemporalDisjunction::TryToDecide() {
322  DCHECK_EQ(UNDECIDED, state_);
323  if (t1_->MayBePerformed() && t2_->MayBePerformed() &&
324  (t1_->MustBePerformed() || t2_->MustBePerformed())) {
325  if (t1_->EndMin() > t2_->StartMax()) {
326  Decide(TWO_BEFORE_ONE);
327  } else if (t2_->EndMin() > t1_->StartMax()) {
328  Decide(ONE_BEFORE_TWO);
329  }
330  }
331 }
332 
333 void TemporalDisjunction::RangeDemon1() {
334  switch (state_) {
335  case ONE_BEFORE_TWO: {
336  if (t1_->MustBePerformed() && t2_->MayBePerformed()) {
337  t2_->SetStartMin(t1_->EndMin());
338  }
339  break;
340  }
341  case TWO_BEFORE_ONE: {
342  if (t1_->MustBePerformed() && t2_->MayBePerformed()) {
343  t2_->SetEndMax(t1_->StartMax());
344  }
345  break;
346  }
347  case UNDECIDED: {
348  TryToDecide();
349  }
350  }
351 }
352 
353 void TemporalDisjunction::RangeDemon2() {
354  if (t1_->MayBePerformed() || t2_->MayBePerformed()) {
355  switch (state_) {
356  case ONE_BEFORE_TWO: {
357  if (t2_->MustBePerformed() && t1_->MayBePerformed()) {
358  t1_->SetEndMax(t2_->StartMax());
359  }
360  break;
361  }
362  case TWO_BEFORE_ONE: {
363  if (t2_->MustBePerformed() && t1_->MayBePerformed()) {
364  t1_->SetStartMin(t2_->EndMin());
365  }
366  break;
367  }
368  case UNDECIDED: {
369  TryToDecide();
370  }
371  }
372  }
373 }
374 
375 void TemporalDisjunction::RangeAlt() {
376  DCHECK(alt_ != nullptr);
377  if (alt_->Value() == 0) {
378  Decide(ONE_BEFORE_TWO);
379  } else {
380  Decide(TWO_BEFORE_ONE);
381  }
382 }
383 
384 void TemporalDisjunction::Decide(State s) {
385  // Should Decide on a fixed state?
386  DCHECK_NE(s, UNDECIDED);
387  if (state_ != UNDECIDED && state_ != s) {
388  solver()->Fail();
389  }
390  solver()->SaveValue(reinterpret_cast<int*>(&state_));
391  state_ = s;
392  if (alt_ != nullptr) {
393  if (s == ONE_BEFORE_TWO) {
394  alt_->SetValue(0);
395  } else {
396  alt_->SetValue(1);
397  }
398  }
399  RangeDemon1();
400  RangeDemon2();
401 }
402 } // namespace
403 
405  IntervalVar* const t2,
406  IntVar* const alt) {
407  return RevAlloc(new TemporalDisjunction(this, t1, t2, alt));
408 }
409 
411  IntervalVar* const t2) {
412  return RevAlloc(new TemporalDisjunction(this, t1, t2, nullptr));
413 }
414 
415 } // namespace operations_research
t ends after d, i.e. End(t) >= d.
virtual int64_t Value() const =0
This method returns the value of the variable.
static const char kIntervalUnaryRelation[]
t1 ends at t2 end, i.e. End(t1) == End(t2) + delay.
BinaryIntervalRelation
This enum is used in Solver::MakeIntervalVarRelation to specify the temporal relation between the two...
t ends before d, i.e. End(t) <= d.
Constraint * MakeIntervalVarRelationWithDelay(IntervalVar *const t1, BinaryIntervalRelation r, IntervalVar *const t2, int64_t delay)
This method creates a relation between two interval vars.
Definition: timetabling.cc:239
A constraint is the main modeling object.
virtual void SetEndRange(int64_t mi, int64_t ma)=0
t1 ends at t2 start, i.e. End(t1) == Start(t2) + delay.
void WhenAnything(Demon *const d)
Attaches a demon awakened when anything about this interval changes.
Definition: interval.cc:2259
t ends at d, i.e. End(t) == d.
virtual void SetStartMin(int64_t m)=0
t1 starts at t2 start, i.e. Start(t1) == Start(t2) + delay.
t starts at d, i.e. Start(t) == d.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
Interval variables are often used in scheduling.
t1 ends after t2 end, i.e. End(t1) >= End(t2) + delay.
virtual int64_t StartMax() const =0
Constraint * MakeTemporalDisjunction(IntervalVar *const t1, IntervalVar *const t2, IntVar *const alt)
This constraint implements a temporal disjunction between two interval vars t1 and t2.
Definition: timetabling.cc:404
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
virtual bool MayBePerformed() const =0
virtual int64_t EndMax() const =0
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:891
UnaryIntervalRelation
This enum is used in Solver::MakeIntervalVarRelation to specify the temporal relation between an inte...
t1 starts at t2 end, i.e. Start(t1) == End(t2) + delay.
t1 starts after t2 end, i.e. Start(t1) >= End(t2) + delay.
Constraint * MakeIntervalVarRelation(IntervalVar *const t, UnaryIntervalRelation r, int64_t d)
This method creates a relation between an interval var and a date.
Definition: timetabling.cc:114
The class IntVar is a subset of IntExpr.
t1 ends after t2 start, i.e. End(t1) >= Start(t2) + delay.
static const char kIntervalBinaryRelation[]
STARTS_AT_START and ENDS_AT_END at the same time.
STARTS_BEFORE and ENDS_AFTER at the same time, i.e.
virtual void SetValue(int64_t v)
This method sets the value of the expression.
T * RevAlloc(T *object)
Registers the given object as being reversible.
virtual int64_t EndMin() const =0
These methods query, set, and watch the end position of the interval var.
#define DCHECK(condition)
Definition: base/logging.h:889
STARTS_AFTER or ENDS_BEFORE, i.e.
virtual void SetEndMin(int64_t m)=0
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:890
virtual void SetStartMax(int64_t m)=0
Collection of objects used to extend the Constraint Solver library.
virtual void SetStartRange(int64_t mi, int64_t ma)=0
virtual void WhenRange(Demon *d)=0
Attach a demon that will watch the min or the max of the expression.
t starts before d, i.e. Start(t) <= d.
virtual bool MustBePerformed() const =0
These methods query, set, and watch the performed status of the interval var.
virtual void SetEndMax(int64_t m)=0
t1 starts after t2 start, i.e. Start(t1) >= Start(t2) + delay.
virtual void SetRange(int64_t l, int64_t u)
This method sets both the min and the max of the expression.
t starts after d, i.e. Start(t) >= d.
virtual int64_t StartMin() const =0
These methods query, set, and watch the start position of the interval var.