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