OR-Tools  9.0
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
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:894
#define DCHECK(condition)
Definition: base/logging.h:892
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
A constraint is the main modeling object.
virtual void SetRange(int64_t l, int64_t u)
This method sets both the min and the max of the expression.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual void SetValue(int64_t v)
This method sets the value of the expression.
virtual void WhenRange(Demon *d)=0
Attach a demon that will watch the min or the max of the expression.
The class IntVar is a subset of IntExpr.
virtual int64_t Value() const =0
This method returns the value of the variable.
Interval variables are often used in scheduling.
virtual void SetEndMax(int64_t m)=0
void WhenAnything(Demon *const d)
Attaches a demon awakened when anything about this interval changes.
Definition: interval.cc:2259
virtual void SetStartMax(int64_t m)=0
virtual void SetStartRange(int64_t mi, int64_t ma)=0
virtual bool MustBePerformed() const =0
These methods query, set, and watch the performed status of the interval var.
virtual void SetEndMin(int64_t m)=0
virtual int64_t EndMin() const =0
These methods query, set, and watch the end position of the interval var.
virtual int64_t StartMin() const =0
These methods query, set, and watch the start position of the interval var.
virtual int64_t EndMax() const =0
virtual void SetStartMin(int64_t m)=0
virtual bool MayBePerformed() const =0
virtual void SetEndRange(int64_t mi, int64_t ma)=0
virtual int64_t StartMax() const =0
static const char kIntervalUnaryRelation[]
static const char kIntervalBinaryRelation[]
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
UnaryIntervalRelation
This enum is used in Solver::MakeIntervalVarRelation to specify the temporal relation between an inte...
@ ENDS_BEFORE
t ends before d, i.e. End(t) <= d.
@ AVOID_DATE
STARTS_AFTER or ENDS_BEFORE, i.e.
@ ENDS_AFTER
t ends after d, i.e. End(t) >= d.
@ STARTS_BEFORE
t starts before d, i.e. Start(t) <= d.
@ STARTS_AT
t starts at d, i.e. Start(t) == d.
@ ENDS_AT
t ends at d, i.e. End(t) == d.
@ STARTS_AFTER
t starts after d, i.e. Start(t) >= d.
@ CROSS_DATE
STARTS_BEFORE and ENDS_AFTER at the same time, i.e.
BinaryIntervalRelation
This enum is used in Solver::MakeIntervalVarRelation to specify the temporal relation between the two...
@ ENDS_AFTER_END
t1 ends after t2 end, i.e. End(t1) >= End(t2) + delay.
@ ENDS_AFTER_START
t1 ends after t2 start, i.e. End(t1) >= Start(t2) + delay.
@ STAYS_IN_SYNC
STARTS_AT_START and ENDS_AT_END at the same time.
@ ENDS_AT_END
t1 ends at t2 end, i.e. End(t1) == End(t2) + delay.
@ STARTS_AT_END
t1 starts at t2 end, i.e. Start(t1) == End(t2) + delay.
@ ENDS_AT_START
t1 ends at t2 start, i.e. End(t1) == Start(t2) + delay.
@ STARTS_AFTER_END
t1 starts after t2 end, i.e. Start(t1) >= End(t2) + delay.
@ STARTS_AFTER_START
t1 starts after t2 start, i.e. Start(t1) >= Start(t2) + delay.
@ STARTS_AT_START
t1 starts at t2 start, i.e. Start(t1) == Start(t2) + delay.
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
T * RevAlloc(T *object)
Registers the given object as being reversible.
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
Collection of objects used to extend the Constraint Solver library.
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)