OR-Tools  9.3
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"
20#include "ortools/base/macros.h"
23
24namespace operations_research {
25
26// ----- interval <unary relation> date -----
27
28namespace {
29const char* kUnaryNames[] = {
30 "ENDS_AFTER", "ENDS_AT", "ENDS_BEFORE", "STARTS_AFTER",
31 "STARTS_AT", "STARTS_BEFORE", "CROSS_DATE", "AVOID_DATE",
32};
33
34const 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
39class 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
69void IntervalUnaryRelation::Post() {
70 if (t_->MayBePerformed()) {
71 Demon* d = solver()->MakeConstraintInitialPropagateCallback(this);
72 t_->WhenAnything(d);
73 }
74}
75
76void IntervalUnaryRelation::InitialPropagate() {
77 if (t_->MayBePerformed()) {
78 switch (rel_) {
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;
92 t_->SetStartRange(d_, d_);
93 break;
94
96 t_->SetStartMax(d_);
97 break;
99 t_->SetStartMax(d_);
100 t_->SetEndMin(d_);
101 break;
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
122namespace {
123class 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
155void 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?
164void 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;
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;
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
247namespace {
248class 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
283void 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
298void 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
310std::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
321void 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
333void 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
353void 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
375void 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
384void 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:892
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
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
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
T * RevAlloc(T *object)
Registers the given object as being reversible.
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)