OR-Tools  9.3
routing_breaks.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 <algorithm>
15#include <cstdint>
16#include <iterator>
17#include <limits>
18#include <map>
19#include <numeric>
20#include <string>
21#include <utility>
22#include <vector>
23
33
34namespace operations_research {
35
37 DCHECK_LE(tasks->num_chain_tasks, tasks->start_min.size());
38 DCHECK_EQ(tasks->start_min.size(), tasks->start_max.size());
39 DCHECK_EQ(tasks->start_min.size(), tasks->duration_min.size());
40 DCHECK_EQ(tasks->start_min.size(), tasks->duration_max.size());
41 DCHECK_EQ(tasks->start_min.size(), tasks->end_min.size());
42 DCHECK_EQ(tasks->start_min.size(), tasks->end_max.size());
43 DCHECK_EQ(tasks->start_min.size(), tasks->is_preemptible.size());
44 // Do forward deductions, then backward deductions.
45 // All propagators are followed by Precedences(),
46 // except MirrorTasks() after which Precedences() would make no deductions,
47 // and DetectablePrecedencesWithChain() which is stronger than Precedences().
48 // Precedences() is a propagator that does obvious deductions quickly (O(n)),
49 // so interleaving Precedences() speeds up the propagation fixed point.
50 if (!Precedences(tasks) || !EdgeFinding(tasks) || !Precedences(tasks) ||
52 return false;
53 }
54 if (!tasks->forbidden_intervals.empty()) {
55 if (!ForbiddenIntervals(tasks) || !Precedences(tasks)) return false;
56 }
57 if (!tasks->distance_duration.empty()) {
58 if (!DistanceDuration(tasks) || !Precedences(tasks)) return false;
59 }
60 if (!MirrorTasks(tasks) || !EdgeFinding(tasks) || !Precedences(tasks) ||
62 return false;
63 }
64 return true;
65}
66
68 const int num_chain_tasks = tasks->num_chain_tasks;
69 if (num_chain_tasks > 0) {
70 // Propagate forwards.
71 int64_t time = tasks->start_min[0];
72 for (int task = 0; task < num_chain_tasks; ++task) {
73 time = std::max(tasks->start_min[task], time);
74 tasks->start_min[task] = time;
75 time = CapAdd(time, tasks->duration_min[task]);
76 if (tasks->end_max[task] < time) return false;
77 time = std::max(time, tasks->end_min[task]);
78 tasks->end_min[task] = time;
79 }
80 // Propagate backwards.
81 time = tasks->end_max[num_chain_tasks - 1];
82 for (int task = num_chain_tasks - 1; task >= 0; --task) {
83 time = std::min(tasks->end_max[task], time);
84 tasks->end_max[task] = time;
85 time = CapSub(time, tasks->duration_min[task]);
86 if (time < tasks->start_min[task]) return false;
87 time = std::min(time, tasks->start_max[task]);
88 tasks->start_max[task] = time;
89 }
90 }
91 const int num_tasks = tasks->start_min.size();
92 for (int task = 0; task < num_tasks; ++task) {
93 // Enforce start + duration <= end.
94 tasks->end_min[task] =
95 std::max(tasks->end_min[task],
96 CapAdd(tasks->start_min[task], tasks->duration_min[task]));
97 tasks->start_max[task] =
98 std::min(tasks->start_max[task],
99 CapSub(tasks->end_max[task], tasks->duration_min[task]));
100 tasks->duration_max[task] =
101 std::min(tasks->duration_max[task],
102 CapSub(tasks->end_max[task], tasks->start_min[task]));
103 if (!tasks->is_preemptible[task]) {
104 // Enforce start + duration == end for nonpreemptibles.
105 tasks->end_max[task] =
106 std::min(tasks->end_max[task],
107 CapAdd(tasks->start_max[task], tasks->duration_max[task]));
108 tasks->start_min[task] =
109 std::max(tasks->start_min[task],
110 CapSub(tasks->end_min[task], tasks->duration_max[task]));
111 tasks->duration_min[task] =
112 std::max(tasks->duration_min[task],
113 CapSub(tasks->end_min[task], tasks->start_max[task]));
114 }
115 if (tasks->duration_min[task] > tasks->duration_max[task]) return false;
116 if (tasks->end_min[task] > tasks->end_max[task]) return false;
117 if (tasks->start_min[task] > tasks->start_max[task]) return false;
118 }
119 return true;
120}
121
123 const int num_tasks = tasks->start_min.size();
124 // For all tasks, start_min := -end_max and end_max := -start_min.
125 for (int task = 0; task < num_tasks; ++task) {
126 const int64_t t = -tasks->start_min[task];
127 tasks->start_min[task] = -tasks->end_max[task];
128 tasks->end_max[task] = t;
129 }
130 // For all tasks, start_max := -end_min and end_min := -start_max.
131 for (int task = 0; task < num_tasks; ++task) {
132 const int64_t t = -tasks->start_max[task];
133 tasks->start_max[task] = -tasks->end_min[task];
134 tasks->end_min[task] = t;
135 }
136 // In the mirror problem, tasks linked by precedences are in reversed order.
137 const int num_chain_tasks = tasks->num_chain_tasks;
138 for (const auto it :
139 {tasks->start_min.begin(), tasks->start_max.begin(),
140 tasks->duration_min.begin(), tasks->duration_max.begin(),
141 tasks->end_min.begin(), tasks->end_max.begin()}) {
142 std::reverse(it, it + num_chain_tasks);
143 std::reverse(it + num_chain_tasks, it + num_tasks);
144 }
145 std::reverse(tasks->is_preemptible.begin(),
146 tasks->is_preemptible.begin() + num_chain_tasks);
147 std::reverse(tasks->is_preemptible.begin() + num_chain_tasks,
148 tasks->is_preemptible.begin() + num_tasks);
149 return true;
150}
151
153 const int num_tasks = tasks->start_min.size();
154 // Prepare start_min events for tree.
155 tasks_by_start_min_.resize(num_tasks);
156 std::iota(tasks_by_start_min_.begin(), tasks_by_start_min_.end(), 0);
157 std::sort(
158 tasks_by_start_min_.begin(), tasks_by_start_min_.end(),
159 [&](int i, int j) { return tasks->start_min[i] < tasks->start_min[j]; });
160 event_of_task_.resize(num_tasks);
161 for (int event = 0; event < num_tasks; ++event) {
162 event_of_task_[tasks_by_start_min_[event]] = event;
163 }
164 // Tasks will be browsed according to end_max order.
165 tasks_by_end_max_.resize(num_tasks);
166 std::iota(tasks_by_end_max_.begin(), tasks_by_end_max_.end(), 0);
167 std::sort(
168 tasks_by_end_max_.begin(), tasks_by_end_max_.end(),
169 [&](int i, int j) { return tasks->end_max[i] < tasks->end_max[j]; });
170
171 // Generic overload checking: insert tasks by end_max,
172 // fail if envelope > end_max.
173 theta_lambda_tree_.Reset(num_tasks);
174 for (const int task : tasks_by_end_max_) {
175 theta_lambda_tree_.AddOrUpdateEvent(
176 event_of_task_[task], tasks->start_min[task], tasks->duration_min[task],
177 tasks->duration_min[task]);
178 if (theta_lambda_tree_.GetEnvelope() > tasks->end_max[task]) {
179 return false;
180 }
181 }
182
183 // Generic edge finding: from full set of tasks, at each end_max event in
184 // decreasing order, check lambda feasibility, then move end_max task from
185 // theta to lambda.
186 for (int i = num_tasks - 1; i >= 0; --i) {
187 const int task = tasks_by_end_max_[i];
188 const int64_t envelope = theta_lambda_tree_.GetEnvelope();
189 // If a nonpreemptible optional would overload end_max, push to envelope.
190 while (theta_lambda_tree_.GetOptionalEnvelope() > tasks->end_max[task]) {
191 int critical_event; // Dummy value.
192 int optional_event;
193 int64_t available_energy; // Dummy value.
195 tasks->end_max[task], &critical_event, &optional_event,
196 &available_energy);
197 const int optional_task = tasks_by_start_min_[optional_event];
198 tasks->start_min[optional_task] =
199 std::max(tasks->start_min[optional_task], envelope);
200 theta_lambda_tree_.RemoveEvent(optional_event);
201 }
202 if (!tasks->is_preemptible[task]) {
203 theta_lambda_tree_.AddOrUpdateOptionalEvent(event_of_task_[task],
204 tasks->start_min[task],
205 tasks->duration_min[task]);
206 } else {
207 theta_lambda_tree_.RemoveEvent(event_of_task_[task]);
208 }
209 }
210 return true;
211}
212
214 const int num_tasks = tasks->start_min.size();
215 // Prepare start_min events for tree.
216 tasks_by_start_min_.resize(num_tasks);
217 std::iota(tasks_by_start_min_.begin(), tasks_by_start_min_.end(), 0);
218 std::sort(
219 tasks_by_start_min_.begin(), tasks_by_start_min_.end(),
220 [&](int i, int j) { return tasks->start_min[i] < tasks->start_min[j]; });
221 event_of_task_.resize(num_tasks);
222 for (int event = 0; event < num_tasks; ++event) {
223 event_of_task_[tasks_by_start_min_[event]] = event;
224 }
225 theta_lambda_tree_.Reset(num_tasks);
226
227 // Sort nonchain tasks by start max = end_max - duration_min.
228 const int num_chain_tasks = tasks->num_chain_tasks;
229 nonchain_tasks_by_start_max_.resize(num_tasks - num_chain_tasks);
230 std::iota(nonchain_tasks_by_start_max_.begin(),
231 nonchain_tasks_by_start_max_.end(), num_chain_tasks);
232 std::sort(nonchain_tasks_by_start_max_.begin(),
233 nonchain_tasks_by_start_max_.end(), [&tasks](int i, int j) {
234 return tasks->end_max[i] - tasks->duration_min[i] <
235 tasks->end_max[j] - tasks->duration_min[j];
236 });
237
238 // Detectable precedences, specialized for routes: for every task on route,
239 // put all tasks before it in the tree, then push with envelope.
240 int index_nonchain = 0;
241 for (int i = 0; i < num_chain_tasks; ++i) {
242 if (!tasks->is_preemptible[i]) {
243 // Add all nonchain tasks detected before i.
244 while (index_nonchain < nonchain_tasks_by_start_max_.size()) {
245 const int task = nonchain_tasks_by_start_max_[index_nonchain];
246 if (tasks->end_max[task] - tasks->duration_min[task] >=
247 tasks->start_min[i] + tasks->duration_min[i])
248 break;
249 theta_lambda_tree_.AddOrUpdateEvent(
250 event_of_task_[task], tasks->start_min[task],
251 tasks->duration_min[task], tasks->duration_min[task]);
252 index_nonchain++;
253 }
254 }
255 // All chain and nonchain tasks before i are now in the tree, push i.
256 const int64_t new_start_min = theta_lambda_tree_.GetEnvelope();
257 // Add i to the tree before updating it.
258 theta_lambda_tree_.AddOrUpdateEvent(event_of_task_[i], tasks->start_min[i],
259 tasks->duration_min[i],
260 tasks->duration_min[i]);
261 tasks->start_min[i] = std::max(tasks->start_min[i], new_start_min);
262 }
263 return true;
264}
265
267 if (tasks->forbidden_intervals.empty()) return true;
268 const int num_tasks = tasks->start_min.size();
269 for (int task = 0; task < num_tasks; ++task) {
270 if (tasks->duration_min[task] == 0) continue;
271 if (tasks->forbidden_intervals[task] == nullptr) continue;
272 // If start_min forbidden, push to next feasible value.
273 {
274 const auto& interval =
275 tasks->forbidden_intervals[task]->FirstIntervalGreaterOrEqual(
276 tasks->start_min[task]);
277 if (interval == tasks->forbidden_intervals[task]->end()) continue;
278 if (interval->start <= tasks->start_min[task]) {
279 tasks->start_min[task] = CapAdd(interval->end, 1);
280 }
281 }
282 // If end_max forbidden, push to next feasible value.
283 {
284 const int64_t start_max =
285 CapSub(tasks->end_max[task], tasks->duration_min[task]);
286 const auto& interval =
287 tasks->forbidden_intervals[task]->LastIntervalLessOrEqual(start_max);
288 if (interval == tasks->forbidden_intervals[task]->end()) continue;
289 if (interval->end >= start_max) {
290 tasks->end_max[task] =
291 CapAdd(interval->start, tasks->duration_min[task] - 1);
292 }
293 }
294 if (CapAdd(tasks->start_min[task], tasks->duration_min[task]) >
295 tasks->end_max[task]) {
296 return false;
297 }
298 }
299 return true;
300}
301
303 if (tasks->distance_duration.empty()) return true;
304 if (tasks->num_chain_tasks == 0) return true;
305 const int route_start = 0;
306 const int route_end = tasks->num_chain_tasks - 1;
307 const int num_tasks = tasks->start_min.size();
308 for (int i = 0; i < tasks->distance_duration.size(); ++i) {
309 const int64_t max_distance = tasks->distance_duration[i].first;
310 const int64_t minimum_break_duration = tasks->distance_duration[i].second;
311
312 // This is a sweeping algorithm that looks whether the union of intervals
313 // defined by breaks and route start/end is (-infty, +infty).
314 // Those intervals are:
315 // - route start: (-infty, start_max + distance]
316 // - route end: [end_min, +infty)
317 // - breaks: [start_min, end_max + distance) if their duration_max
318 // is >= min_duration, empty set otherwise.
319 // If sweeping finds that a time point can be covered by only one interval,
320 // it will force the corresponding break or route start/end to cover this
321 // point, which can force a break to be above minimum_break_duration.
322
323 // We suppose break tasks are ordered, so the algorithm supposes that
324 // start_min(task_n) <= start_min(task_{n+1}) and
325 // end_max(task_n) <= end_max(task_{n+1}).
326 for (int task = tasks->num_chain_tasks + 1; task < num_tasks; ++task) {
327 tasks->start_min[task] =
328 std::max(tasks->start_min[task], tasks->start_min[task - 1]);
329 }
330 for (int task = num_tasks - 2; task >= tasks->num_chain_tasks; --task) {
331 tasks->end_max[task] =
332 std::min(tasks->end_max[task], tasks->end_max[task + 1]);
333 }
334 // Skip breaks that cannot be performed after start.
335 int index_break_by_emax = tasks->num_chain_tasks;
336 while (index_break_by_emax < num_tasks &&
337 tasks->end_max[index_break_by_emax] <= tasks->end_min[route_start]) {
338 ++index_break_by_emax;
339 }
340 // Special case: no breaks after start.
341 if (index_break_by_emax == num_tasks) {
342 tasks->end_min[route_start] =
343 std::max(tasks->end_min[route_start],
344 CapSub(tasks->start_min[route_end], max_distance));
345 tasks->start_max[route_end] =
346 std::min(tasks->start_max[route_end],
347 CapAdd(tasks->end_max[route_start], max_distance));
348 continue;
349 }
350 // There will be a break after start, so route_start coverage is tested.
351 // Initial state: start at -inf with route_start in task_set.
352 // Sweep over profile, looking for time points where the number of
353 // covering breaks is <= 1. If it is 0, fail, otherwise force the
354 // unique break to cover it.
355 // Route start and end get a special treatment, not sure generalizing
356 // would be better.
357 int64_t xor_active_tasks = route_start;
358 int num_active_tasks = 1;
359 int64_t previous_time = std::numeric_limits<int64_t>::min();
360 const int64_t route_start_time =
361 CapAdd(tasks->end_max[route_start], max_distance);
362 const int64_t route_end_time = tasks->start_min[route_end];
363 // NOTE: all smin events must be closed by a corresponding emax event,
364 // otherwise num_active_tasks is wrong (too high) and the reasoning misses
365 // some filtering.
366 int index_break_by_smin = index_break_by_emax;
367 while (index_break_by_emax < num_tasks) {
368 // Find next time point among start/end of covering intervals.
369 int64_t current_time =
370 CapAdd(tasks->end_max[index_break_by_emax], max_distance);
371 if (index_break_by_smin < num_tasks) {
372 current_time =
373 std::min(current_time, tasks->start_min[index_break_by_smin]);
374 }
375 if (previous_time < route_start_time && route_start_time < current_time) {
376 current_time = route_start_time;
377 }
378 if (previous_time < route_end_time && route_end_time < current_time) {
379 current_time = route_end_time;
380 }
381 // If num_active_tasks was 1, the unique active task must cover from
382 // previous_time to current_time.
383 if (num_active_tasks == 1) {
384 // xor_active_tasks is the unique task that can cover [previous_time,
385 // current_time).
386 if (xor_active_tasks != route_end) {
387 tasks->end_min[xor_active_tasks] =
388 std::max(tasks->end_min[xor_active_tasks],
389 CapSub(current_time, max_distance));
390 if (xor_active_tasks != route_start) {
391 tasks->duration_min[xor_active_tasks] = std::max(
392 tasks->duration_min[xor_active_tasks],
393 std::max(
394 minimum_break_duration,
395 CapSub(CapSub(current_time, max_distance), previous_time)));
396 }
397 }
398 }
399 // Process covering intervals that start or end at current_time.
400 while (index_break_by_smin < num_tasks &&
401 current_time == tasks->start_min[index_break_by_smin]) {
402 if (tasks->duration_max[index_break_by_smin] >=
403 minimum_break_duration) {
404 xor_active_tasks ^= index_break_by_smin;
405 ++num_active_tasks;
406 }
407 ++index_break_by_smin;
408 }
409 while (index_break_by_emax < num_tasks &&
410 current_time ==
411 CapAdd(tasks->end_max[index_break_by_emax], max_distance)) {
412 if (tasks->duration_max[index_break_by_emax] >=
413 minimum_break_duration) {
414 xor_active_tasks ^= index_break_by_emax;
415 --num_active_tasks;
416 }
417 ++index_break_by_emax;
418 }
419 if (current_time == route_start_time) {
420 xor_active_tasks ^= route_start;
421 --num_active_tasks;
422 }
423 if (current_time == route_end_time) {
424 xor_active_tasks ^= route_end;
425 ++num_active_tasks;
426 }
427 // If num_active_tasks becomes 1, the unique active task must cover from
428 // current_time.
429 if (num_active_tasks <= 0) return false;
430 if (num_active_tasks == 1) {
431 if (xor_active_tasks != route_start) {
432 // xor_active_tasks is the unique task that can cover from
433 // current_time to the next time point.
434 tasks->start_max[xor_active_tasks] =
435 std::min(tasks->start_max[xor_active_tasks], current_time);
436 if (xor_active_tasks != route_end) {
437 tasks->duration_min[xor_active_tasks] = std::max(
438 tasks->duration_min[xor_active_tasks], minimum_break_duration);
439 }
440 }
441 }
442 previous_time = current_time;
443 }
444 }
445 return true;
446}
447
449 const int num_chain_tasks = tasks->num_chain_tasks;
450 if (num_chain_tasks < 1) return true;
451 // TODO(user): add stronger bounds.
452 // The duration of the chain plus that of nonchain tasks that must be
453 // performed during the chain is a lower bound of the chain span.
454 {
455 int64_t sum_chain_durations = 0;
456 const auto duration_start = tasks->duration_min.begin();
457 const auto duration_end = tasks->duration_min.begin() + num_chain_tasks;
458 for (auto it = duration_start; it != duration_end; ++it) {
459 sum_chain_durations = CapAdd(sum_chain_durations, *it);
460 }
461 int64_t sum_forced_nonchain_durations = 0;
462 for (int i = num_chain_tasks; i < tasks->start_min.size(); ++i) {
463 // Tasks that can be executed before or after are skipped.
464 if (tasks->end_min[i] <= tasks->start_max[0] ||
465 tasks->end_min[num_chain_tasks - 1] <= tasks->start_max[i]) {
466 continue;
467 }
468 sum_forced_nonchain_durations =
469 CapAdd(sum_forced_nonchain_durations, tasks->duration_min[i]);
470 }
471 tasks->span_min =
472 std::max(tasks->span_min,
473 CapAdd(sum_chain_durations, sum_forced_nonchain_durations));
474 }
475 // The difference end of the chain - start of the chain is a lower bound.
476 {
477 const int64_t end_minus_start =
478 CapSub(tasks->end_min[num_chain_tasks - 1], tasks->start_max[0]);
479 tasks->span_min = std::max(tasks->span_min, end_minus_start);
480 }
481
482 return tasks->span_min <= tasks->span_max;
483}
484
485// Computes a lower bound of the span of the chain, taking into account only
486// the first nonchain task.
487// TODO(user): extend to arbitrary number of nonchain tasks.
489 // Do nothing if there are no chain tasks or no nonchain tasks.
490 const int num_chain_tasks = tasks->num_chain_tasks;
491 if (num_chain_tasks < 1) return true;
492 if (num_chain_tasks == tasks->start_min.size()) return true;
493 const int task_index = num_chain_tasks;
494 if (!Precedences(tasks)) return false;
495 const int64_t min_possible_chain_end = tasks->end_min[num_chain_tasks - 1];
496 const int64_t max_possible_chain_start = tasks->start_max[0];
497 // For each chain task i, compute cumulated duration of chain tasks before it.
498 int64_t total_duration = 0;
499 {
500 total_duration_before_.resize(num_chain_tasks);
501 for (int i = 0; i < num_chain_tasks; ++i) {
502 total_duration_before_[i] = total_duration;
503 total_duration = CapAdd(total_duration, tasks->duration_min[i]);
504 }
505 }
506 // Estimate span min of chain tasks. Use the schedule that ends at
507 // min_possible_chain_end and starts at smallest of start_max[0] or the
508 // threshold where pushing start[0] later does not make a difference to the
509 // chain span because of chain precedence constraints,
510 // i.e. min_possible_chain_end - total_duration.
511 {
512 const int64_t chain_span_min =
513 min_possible_chain_end -
514 std::min(tasks->start_max[0], min_possible_chain_end - total_duration);
515 if (chain_span_min > tasks->span_max) {
516 return false;
517 } else {
518 tasks->span_min = std::max(tasks->span_min, chain_span_min);
519 }
520 // If task can be performed before or after the chain,
521 // span_min is chain_span_min.
522 if (tasks->end_min[task_index] <= tasks->start_max[0] ||
523 tasks->end_min[num_chain_tasks - 1] <= tasks->start_max[task_index]) {
524 return true;
525 }
526 }
527 // Scan all possible preemption positions of the nontask chain,
528 // keep the one that yields the minimum span.
529 int64_t span_min = std::numeric_limits<int64_t>::max();
530 bool schedule_is_feasible = false;
531 for (int i = 0; i < num_chain_tasks; ++i) {
532 if (!tasks->is_preemptible[i]) continue;
533 // Estimate span min if tasks is performed during i.
534 // For all possible minimal-span schedules, there is a schedule where task i
535 // and nonchain task form a single block. Thus, we only consider those.
536 const int64_t block_start_min =
537 std::max(tasks->start_min[i],
538 tasks->start_min[task_index] - tasks->duration_min[i]);
539 const int64_t block_start_max =
540 std::min(tasks->start_max[task_index],
541 tasks->start_max[i] - tasks->duration_min[task_index]);
542 if (block_start_min > block_start_max) continue;
543
544 // Compute the block start that yields the minimal span.
545 // Given a feasible block start, a chain of minimum span constrained to
546 // this particular block start can be obtained by scheduling all tasks after
547 // the block at their earliest, and all tasks before it at their latest.
548 // The span can be decomposed into two parts: the head, which are the
549 // tasks that are before the block, and the tail, which are the block and
550 // the tasks after it.
551 // When the block start varies, the head length of the optimal schedule
552 // described above decreases as much as the block start decreases, until
553 // an inflection point at which it stays constant. That inflection value
554 // is the one where the precedence constraints force the chain start to
555 // decrease because of durations.
556 const int64_t head_inflection =
557 max_possible_chain_start + total_duration_before_[i];
558 // The map from block start to minimal tail length also has an inflection
559 // point, that additionally depends on the nonchain task's duration.
560 const int64_t tail_inflection =
561 min_possible_chain_end - (total_duration - total_duration_before_[i]) -
562 tasks->duration_min[task_index];
563 // All block start values between these two yield the same minimal span.
564 // Indeed, first, mind that the inflection points might be in any order.
565 // - if head_inflection < tail_inflection, then inside the interval
566 // [head_inflection, tail_inflection], increasing the block start by delta
567 // decreases the tail length by delta and increases the head length by
568 // delta too.
569 // - if tail_inflection < head_inflection, then inside the interval
570 // [tail_inflection, head_inflection], head length is constantly at
571 // total_duration_before_[i], and tail length is also constant.
572 // In both cases, outside of the interval, one part is constant and the
573 // other increases as much as the distance to the interval.
574 // We can abstract inflection point to the interval they form.
575 const int64_t optimal_interval_min_start =
576 std::min(head_inflection, tail_inflection);
577 const int64_t optimal_interval_max_start =
578 std::max(head_inflection, tail_inflection);
579 // If the optimal interval for block start intersects the feasible interval,
580 // we can select any point within it, for instance the earliest one.
581 int64_t block_start = std::max(optimal_interval_min_start, block_start_min);
582 // If the intervals do not intersect, the feasible value closest to the
583 // optimal interval has the minimal span, because the span increases as
584 // much as the distance to the optimal interval.
585 if (optimal_interval_max_start < block_start_min) {
586 // Optimal interval is before feasible interval, closest is feasible min.
587 block_start = block_start_min;
588 } else if (block_start_max < optimal_interval_min_start) {
589 // Optimal interval is after feasible interval, closest is feasible max.
590 block_start = block_start_max;
591 }
592 // Compute span for the chosen block start.
593 const int64_t head_duration =
594 std::max(block_start, head_inflection) - max_possible_chain_start;
595 const int64_t tail_duration =
596 min_possible_chain_end - std::min(block_start, tail_inflection);
597 const int64_t optimal_span_at_i = head_duration + tail_duration;
598 span_min = std::min(span_min, optimal_span_at_i);
599 schedule_is_feasible = true;
600 }
601 if (!schedule_is_feasible || span_min > tasks->span_max) {
602 return false;
603 } else {
604 tasks->span_min = std::max(tasks->span_min, span_min);
605 return true;
606 }
607}
608
609void AppendTasksFromPath(const std::vector<int64_t>& path,
610 const TravelBounds& travel_bounds,
611 const RoutingDimension& dimension,
613 const int num_nodes = path.size();
614 DCHECK_EQ(travel_bounds.pre_travels.size(), num_nodes - 1);
615 DCHECK_EQ(travel_bounds.post_travels.size(), num_nodes - 1);
616 for (int i = 0; i < num_nodes; ++i) {
617 const int64_t cumul_min = dimension.CumulVar(path[i])->Min();
618 const int64_t cumul_max = dimension.CumulVar(path[i])->Max();
619 // Add task associated to visit i.
620 // Visits start at Cumul(path[i]) - before_visit
621 // and end at Cumul(path[i]) + after_visit
622 {
623 const int64_t before_visit =
624 (i == 0) ? 0 : travel_bounds.post_travels[i - 1];
625 const int64_t after_visit =
626 (i == num_nodes - 1) ? 0 : travel_bounds.pre_travels[i];
627
628 tasks->start_min.push_back(CapSub(cumul_min, before_visit));
629 tasks->start_max.push_back(CapSub(cumul_max, before_visit));
630 tasks->duration_min.push_back(CapAdd(before_visit, after_visit));
631 tasks->duration_max.push_back(CapAdd(before_visit, after_visit));
632 tasks->end_min.push_back(CapAdd(cumul_min, after_visit));
633 tasks->end_max.push_back(CapAdd(cumul_max, after_visit));
634 tasks->is_preemptible.push_back(false);
635 }
636 if (i == num_nodes - 1) break;
637
638 // Tasks from travels.
639 // A travel task starts at Cumul(path[i]) + pre_travel,
640 // last for FixedTransitVar(path[i]) - pre_travel - post_travel,
641 // and must end at the latest at Cumul(path[i+1]) - post_travel.
642 {
643 const int64_t pre_travel = travel_bounds.pre_travels[i];
644 const int64_t post_travel = travel_bounds.post_travels[i];
645 tasks->start_min.push_back(CapAdd(cumul_min, pre_travel));
646 tasks->start_max.push_back(CapAdd(cumul_max, pre_travel));
647 tasks->duration_min.push_back(
648 std::max<int64_t>(0, CapSub(travel_bounds.min_travels[i],
649 CapAdd(pre_travel, post_travel))));
650 tasks->duration_max.push_back(
653 : std::max<int64_t>(0, CapSub(travel_bounds.max_travels[i],
654 CapAdd(pre_travel, post_travel))));
655 tasks->end_min.push_back(
656 CapSub(dimension.CumulVar(path[i + 1])->Min(), post_travel));
657 tasks->end_max.push_back(
658 CapSub(dimension.CumulVar(path[i + 1])->Max(), post_travel));
659 tasks->is_preemptible.push_back(true);
660 }
661 }
662}
663
664void FillTravelBoundsOfVehicle(int vehicle, const std::vector<int64_t>& path,
665 const RoutingDimension& dimension,
666 TravelBounds* travel_bounds) {
667 // Fill path and min/max/pre/post travel bounds.
668 FillPathEvaluation(path, dimension.transit_evaluator(vehicle),
669 &travel_bounds->min_travels);
670 const int num_travels = travel_bounds->min_travels.size();
671 travel_bounds->max_travels.assign(num_travels,
673 {
674 const int index = dimension.GetPreTravelEvaluatorOfVehicle(vehicle);
675 if (index == -1) {
676 travel_bounds->pre_travels.assign(num_travels, 0);
677 } else {
678 FillPathEvaluation(path, dimension.model()->TransitCallback(index),
679 &travel_bounds->pre_travels);
680 }
681 }
682 {
683 const int index = dimension.GetPostTravelEvaluatorOfVehicle(vehicle);
684 if (index == -1) {
685 travel_bounds->post_travels.assign(num_travels, 0);
686 } else {
687 FillPathEvaluation(path, dimension.model()->TransitCallback(index),
688 &travel_bounds->post_travels);
689 }
690 }
691}
692
693void AppendTasksFromIntervals(const std::vector<IntervalVar*>& intervals,
695 for (IntervalVar* interval : intervals) {
696 if (!interval->MustBePerformed()) continue;
697 tasks->start_min.push_back(interval->StartMin());
698 tasks->start_max.push_back(interval->StartMax());
699 tasks->duration_min.push_back(interval->DurationMin());
700 tasks->duration_max.push_back(interval->DurationMax());
701 tasks->end_min.push_back(interval->EndMin());
702 tasks->end_max.push_back(interval->EndMax());
703 tasks->is_preemptible.push_back(false);
704 }
705}
706
708 const RoutingDimension* dimension)
709 : Constraint(dimension->model()->solver()),
710 model_(dimension->model()),
711 dimension_(dimension) {
712 vehicle_demons_.resize(model_->vehicles());
713}
714
716 for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
717 if (dimension_->GetBreakIntervalsOfVehicle(vehicle).empty() &&
718 dimension_->GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
719 continue;
720 }
721 vehicle_demons_[vehicle] = MakeDelayedConstraintDemon1(
722 solver(), this, &GlobalVehicleBreaksConstraint::PropagateVehicle,
723 "PropagateVehicle", vehicle);
724 for (IntervalVar* interval :
725 dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
726 interval->WhenAnything(vehicle_demons_[vehicle]);
727 }
728 }
729 const int num_cumuls = dimension_->cumuls().size();
730 const int num_nexts = model_->Nexts().size();
731 for (int node = 0; node < num_cumuls; node++) {
732 Demon* dimension_demon = MakeConstraintDemon1(
733 solver(), this, &GlobalVehicleBreaksConstraint::PropagateNode,
734 "PropagateNode", node);
735 if (node < num_nexts) {
736 model_->NextVar(node)->WhenBound(dimension_demon);
737 dimension_->SlackVar(node)->WhenRange(dimension_demon);
738 }
739 model_->VehicleVar(node)->WhenBound(dimension_demon);
740 dimension_->CumulVar(node)->WhenRange(dimension_demon);
741 }
742}
743
745 for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
746 if (!dimension_->GetBreakIntervalsOfVehicle(vehicle).empty() ||
747 !dimension_->GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
748 PropagateVehicle(vehicle);
749 }
750 }
751}
752
753// This dispatches node events to the right vehicle propagator.
754// It also filters out a part of uninteresting events, on which the vehicle
755// propagator will not find anything new.
756void GlobalVehicleBreaksConstraint::PropagateNode(int node) {
757 if (!model_->VehicleVar(node)->Bound()) return;
758 const int vehicle = model_->VehicleVar(node)->Min();
759 if (vehicle < 0 || vehicle_demons_[vehicle] == nullptr) return;
760 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
761}
762
763void GlobalVehicleBreaksConstraint::FillPartialPathOfVehicle(int vehicle) {
764 path_.clear();
765 int current = model_->Start(vehicle);
766 while (!model_->IsEnd(current)) {
767 path_.push_back(current);
768 current = model_->NextVar(current)->Bound()
769 ? model_->NextVar(current)->Min()
770 : model_->End(vehicle);
771 }
772 path_.push_back(current);
773}
774
775void GlobalVehicleBreaksConstraint::FillPathTravels(
776 const std::vector<int64_t>& path) {
777 const int num_travels = path.size() - 1;
778 travel_bounds_.min_travels.resize(num_travels);
779 travel_bounds_.max_travels.resize(num_travels);
780 for (int i = 0; i < num_travels; ++i) {
781 travel_bounds_.min_travels[i] = dimension_->FixedTransitVar(path[i])->Min();
782 travel_bounds_.max_travels[i] = dimension_->FixedTransitVar(path[i])->Max();
783 }
784}
785
786// First, perform energy-based reasoning on intervals and cumul variables.
787// Then, perform reasoning on slack variables.
788void GlobalVehicleBreaksConstraint::PropagateVehicle(int vehicle) {
789 // Fill path and pre/post travel information.
790 FillPartialPathOfVehicle(vehicle);
791 const int num_nodes = path_.size();
792 FillPathTravels(path_);
793 {
794 const int index = dimension_->GetPreTravelEvaluatorOfVehicle(vehicle);
795 if (index == -1) {
796 travel_bounds_.pre_travels.assign(num_nodes - 1, 0);
797 } else {
799 &travel_bounds_.pre_travels);
800 }
801 }
802 {
803 const int index = dimension_->GetPostTravelEvaluatorOfVehicle(vehicle);
804 if (index == -1) {
805 travel_bounds_.post_travels.assign(num_nodes - 1, 0);
806 } else {
808 &travel_bounds_.post_travels);
809 }
810 }
811 // The last travel might not be fixed: in that case, relax its information.
812 if (!model_->NextVar(path_[num_nodes - 2])->Bound()) {
813 travel_bounds_.min_travels.back() = 0;
814 travel_bounds_.max_travels.back() = std::numeric_limits<int64_t>::max();
815 travel_bounds_.pre_travels.back() = 0;
816 travel_bounds_.post_travels.back() = 0;
817 }
818
819 // Fill tasks from path, break intervals, and break constraints.
820 tasks_.Clear();
821 AppendTasksFromPath(path_, travel_bounds_, *dimension_, &tasks_);
822 tasks_.num_chain_tasks = tasks_.start_min.size();
824 &tasks_);
825 tasks_.distance_duration =
826 dimension_->GetBreakDistanceDurationOfVehicle(vehicle);
827
828 // Do the actual reasoning, no need to continue if infeasible.
829 if (!disjunctive_propagator_.Propagate(&tasks_)) solver()->Fail();
830
831 // Make task translators to help set new bounds of CP variables.
832 task_translators_.clear();
833 for (int i = 0; i < num_nodes; ++i) {
834 const int64_t before_visit =
835 (i == 0) ? 0 : travel_bounds_.post_travels[i - 1];
836 const int64_t after_visit =
837 (i == num_nodes - 1) ? 0 : travel_bounds_.pre_travels[i];
838 task_translators_.emplace_back(dimension_->CumulVar(path_[i]), before_visit,
839 after_visit);
840 if (i == num_nodes - 1) break;
841 task_translators_.emplace_back(); // Dummy translator for travel tasks.
842 }
843 for (IntervalVar* interval :
844 dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
845 if (!interval->MustBePerformed()) continue;
846 task_translators_.emplace_back(interval);
847 }
848
849 // Push new bounds to CP variables.
850 const int num_tasks = tasks_.start_min.size();
851 for (int task = 0; task < num_tasks; ++task) {
852 task_translators_[task].SetStartMin(tasks_.start_min[task]);
853 task_translators_[task].SetStartMax(tasks_.start_max[task]);
854 task_translators_[task].SetDurationMin(tasks_.duration_min[task]);
855 task_translators_[task].SetEndMin(tasks_.end_min[task]);
856 task_translators_[task].SetEndMax(tasks_.end_max[task]);
857 }
858
859 // Reasoning on slack variables: when intervals must be inside an arc,
860 // that arc's slack must be large enough to accommodate for those.
861 // TODO(user): Make a version more efficient than O(n^2).
862 if (dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) return;
863 // If the last arc of the path was not bound, do not change slack.
864 const int64_t last_bound_arc =
865 num_nodes - 2 - (model_->NextVar(path_[num_nodes - 2])->Bound() ? 0 : 1);
866 for (int i = 0; i <= last_bound_arc; ++i) {
867 const int64_t arc_start_max =
868 CapSub(dimension_->CumulVar(path_[i])->Max(),
869 i > 0 ? travel_bounds_.post_travels[i - 1] : 0);
870 const int64_t arc_end_min =
871 CapAdd(dimension_->CumulVar(path_[i + 1])->Min(),
872 i < num_nodes - 2 ? travel_bounds_.pre_travels[i + 1] : 0);
873 int64_t total_break_inside_arc = 0;
874 for (IntervalVar* interval :
875 dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
876 if (!interval->MustBePerformed()) continue;
877 const int64_t interval_start_max = interval->StartMax();
878 const int64_t interval_end_min = interval->EndMin();
879 const int64_t interval_duration_min = interval->DurationMin();
880 // If interval cannot end before the arc's from node and
881 // cannot start after the 'to' node, then it must be inside the arc.
882 if (arc_start_max < interval_end_min &&
883 interval_start_max < arc_end_min) {
884 total_break_inside_arc += interval_duration_min;
885 }
886 }
887 dimension_->SlackVar(path_[i])->SetMin(total_break_inside_arc);
888 }
889 // Reasoning on optional intervals.
890 // TODO(user): merge this with energy-based reasoning.
891 // If there is no optional interval, skip the rest of this function.
892 {
893 bool has_optional = false;
894 for (const IntervalVar* interval :
895 dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
896 if (interval->MayBePerformed() && !interval->MustBePerformed()) {
897 has_optional = true;
898 break;
899 }
900 }
901 if (!has_optional) return;
902 }
903 const std::vector<IntervalVar*>& break_intervals =
904 dimension_->GetBreakIntervalsOfVehicle(vehicle);
905 for (int pos = 0; pos < num_nodes - 1; ++pos) {
906 const int64_t current_slack_max = dimension_->SlackVar(path_[pos])->Max();
907 const int64_t visit_start_offset =
908 pos > 0 ? travel_bounds_.post_travels[pos - 1] : 0;
909 const int64_t visit_start_max =
910 CapSub(dimension_->CumulVar(path_[pos])->Max(), visit_start_offset);
911 const int64_t visit_end_offset =
912 (pos < num_nodes - 1) ? travel_bounds_.pre_travels[pos] : 0;
913 const int64_t visit_end_min =
914 CapAdd(dimension_->CumulVar(path_[pos])->Min(), visit_end_offset);
915
916 for (IntervalVar* interval : break_intervals) {
917 if (!interval->MayBePerformed()) continue;
918 const bool interval_is_performed = interval->MustBePerformed();
919 const int64_t interval_start_max = interval->StartMax();
920 const int64_t interval_end_min = interval->EndMin();
921 const int64_t interval_duration_min = interval->DurationMin();
922 // When interval cannot fit inside current arc,
923 // do disjunctive reasoning on full arc.
924 if (pos < num_nodes - 1 && interval_duration_min > current_slack_max) {
925 // The arc lasts from CumulVar(path_[pos]) - post_travel_[pos] to
926 // CumulVar(path_[pos+1]) + pre_travel_[pos+1].
927 const int64_t arc_start_offset =
928 pos > 0 ? travel_bounds_.post_travels[pos - 1] : 0;
929 const int64_t arc_start_max = visit_start_max;
930 const int64_t arc_end_offset =
931 (pos < num_nodes - 2) ? travel_bounds_.pre_travels[pos + 1] : 0;
932 const int64_t arc_end_min =
933 CapAdd(dimension_->CumulVar(path_[pos + 1])->Min(), arc_end_offset);
934 // Interval not before.
935 if (arc_start_max < interval_end_min) {
936 interval->SetStartMin(arc_end_min);
937 if (interval_is_performed) {
938 dimension_->CumulVar(path_[pos + 1])
939 ->SetMax(CapSub(interval_start_max, arc_end_offset));
940 }
941 }
942 // Interval not after.
943 if (interval_start_max < arc_end_min) {
944 interval->SetEndMax(arc_start_max);
945 if (interval_is_performed) {
946 dimension_->CumulVar(path_[pos])
947 ->SetMin(CapSub(interval_end_min, arc_start_offset));
948 }
949 }
950 continue;
951 }
952 // Interval could fit inside arc: do disjunctive reasoning between
953 // interval and visit.
954 // Interval not before.
955 if (visit_start_max < interval_end_min) {
956 interval->SetStartMin(visit_end_min);
957 if (interval_is_performed) {
958 dimension_->CumulVar(path_[pos])
959 ->SetMax(CapSub(interval_start_max, visit_end_offset));
960 }
961 }
962 // Interval not after.
963 if (interval_start_max < visit_end_min) {
964 interval->SetEndMax(visit_start_max);
965 if (interval_is_performed) {
966 dimension_->CumulVar(path_[pos])
967 ->SetMin(CapAdd(interval_end_min, visit_start_offset));
968 }
969 }
970 }
971 }
972}
973
974namespace {
975class VehicleBreaksFilter : public BasePathFilter {
976 public:
977 VehicleBreaksFilter(const RoutingModel& routing_model,
978 const RoutingDimension& dimension);
979 std::string DebugString() const override { return "VehicleBreaksFilter"; }
980 bool AcceptPath(int64_t path_start, int64_t chain_start,
981 int64_t chain_end) override;
982
983 private:
984 // Fills path_ with the path of vehicle, start to end.
985 void FillPathOfVehicle(int64_t vehicle);
986 std::vector<int64_t> path_;
987 // Handles to model.
988 const RoutingModel& model_;
989 const RoutingDimension& dimension_;
990 // Strong energy-based filtering algorithm.
991 DisjunctivePropagator disjunctive_propagator_;
992 DisjunctivePropagator::Tasks tasks_;
993 // Used to check whether propagation changed a vector.
994 std::vector<int64_t> old_start_min_;
995 std::vector<int64_t> old_start_max_;
996 std::vector<int64_t> old_end_min_;
997 std::vector<int64_t> old_end_max_;
998
999 std::vector<int> start_to_vehicle_;
1000 TravelBounds travel_bounds_;
1001};
1002
1003VehicleBreaksFilter::VehicleBreaksFilter(const RoutingModel& routing_model,
1004 const RoutingDimension& dimension)
1005 : BasePathFilter(routing_model.Nexts(),
1006 routing_model.Size() + routing_model.vehicles()),
1007 model_(routing_model),
1008 dimension_(dimension) {
1009 DCHECK(dimension_.HasBreakConstraints());
1010 start_to_vehicle_.resize(Size(), -1);
1011 for (int i = 0; i < routing_model.vehicles(); ++i) {
1012 start_to_vehicle_[routing_model.Start(i)] = i;
1013 }
1014}
1015
1016void VehicleBreaksFilter::FillPathOfVehicle(int64_t vehicle) {
1017 path_.clear();
1018 int current = model_.Start(vehicle);
1019 while (!model_.IsEnd(current)) {
1020 path_.push_back(current);
1021 current = GetNext(current);
1022 }
1023 path_.push_back(current);
1024}
1025
1026bool VehicleBreaksFilter::AcceptPath(int64_t path_start, int64_t chain_start,
1027 int64_t chain_end) {
1028 const int vehicle = start_to_vehicle_[path_start];
1029 if (dimension_.GetBreakIntervalsOfVehicle(vehicle).empty() &&
1030 dimension_.GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
1031 return true;
1032 }
1033 // Fill path and pre/post travel information.
1034 FillPathOfVehicle(vehicle);
1035 FillTravelBoundsOfVehicle(vehicle, path_, dimension_, &travel_bounds_);
1036 // Fill tasks from path, forbidden intervals, breaks and break constraints.
1037 tasks_.Clear();
1038 AppendTasksFromPath(path_, travel_bounds_, dimension_, &tasks_);
1039 tasks_.num_chain_tasks = tasks_.start_min.size();
1041 &tasks_);
1042 // Add forbidden intervals only if a node has some.
1043 tasks_.forbidden_intervals.clear();
1044 if (std::any_of(path_.begin(), path_.end(), [this](int64_t node) {
1045 return dimension_.forbidden_intervals()[node].NumIntervals() > 0;
1046 })) {
1047 tasks_.forbidden_intervals.assign(tasks_.start_min.size(), nullptr);
1048 for (int i = 0; i < path_.size(); ++i) {
1049 tasks_.forbidden_intervals[2 * i] =
1050 &(dimension_.forbidden_intervals()[path_[i]]);
1051 }
1052 }
1053 // Max distance duration constraint.
1054 tasks_.distance_duration =
1055 dimension_.GetBreakDistanceDurationOfVehicle(vehicle);
1056
1057 // Reduce bounds until failure or fixed point is reached.
1058 // We set a maximum amount of iterations to avoid slow propagation.
1059 bool is_feasible = true;
1060 int maximum_num_iterations = 8;
1061 while (--maximum_num_iterations >= 0) {
1062 old_start_min_ = tasks_.start_min;
1063 old_start_max_ = tasks_.start_max;
1064 old_end_min_ = tasks_.end_min;
1065 old_end_max_ = tasks_.end_max;
1066 is_feasible = disjunctive_propagator_.Propagate(&tasks_);
1067 if (!is_feasible) break;
1068 // If fixed point reached, stop.
1069 if ((old_start_min_ == tasks_.start_min) &&
1070 (old_start_max_ == tasks_.start_max) &&
1071 (old_end_min_ == tasks_.end_min) && (old_end_max_ == tasks_.end_max)) {
1072 break;
1073 }
1074 }
1075 return is_feasible;
1076}
1077
1078} // namespace
1079
1081 const RoutingModel& routing_model, const RoutingDimension& dimension) {
1082 return routing_model.solver()->RevAlloc(
1083 new VehicleBreaksFilter(routing_model, dimension));
1084}
1085
1086} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#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.
A Demon is the base element of a propagation queue.
bool EdgeFinding(Tasks *tasks)
Does edge-finding deductions on all tasks.
bool Precedences(Tasks *tasks)
Propagates the deductions from the chain of precedences, if there is one.
bool DistanceDuration(Tasks *tasks)
Propagates distance_duration constraints, if any.
bool MirrorTasks(Tasks *tasks)
Transforms the problem with a time symmetry centered in 0.
bool ForbiddenIntervals(Tasks *tasks)
Tasks might have holes in their domain, this enforces such holes.
bool Propagate(Tasks *tasks)
Computes new bounds for all tasks, returns false if infeasible.
bool DetectablePrecedencesWithChain(Tasks *tasks)
Does detectable precedences deductions on tasks in the chain precedence, taking the time windows of n...
bool ChainSpanMinDynamic(Tasks *tasks)
Computes a lower bound of the span of the chain, taking into account only the first nonchain task.
bool ChainSpanMin(Tasks *tasks)
Propagates a lower bound of the chain span, end[num_chain_tasks] - start[0], to span_min.
void Post() override
This method is called when the constraint is processed by the solver.
void InitialPropagate() override
This method performs the initial propagation of the constraint.
GlobalVehicleBreaksConstraint(const RoutingDimension *dimension)
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64_t Min() const =0
virtual void SetMax(int64_t m)=0
virtual void SetMin(int64_t m)=0
virtual int64_t Max() const =0
virtual void WhenRange(Demon *d)=0
Attach a demon that will watch the min or the max of the expression.
virtual void WhenBound(Demon *d)=0
This method attaches a demon that will be awakened when the variable is bound.
Interval variables are often used in scheduling.
virtual int64_t DurationMax() const =0
virtual int64_t DurationMin() const =0
These methods query, set, and watch the duration of the interval var.
virtual bool MustBePerformed() const =0
These methods query, set, and watch the performed status of the interval var.
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 int64_t StartMax() const =0
void EnqueueDelayedDemon(Demon *const d)
This method pushes the demon onto the propagation queue.
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2590
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
Definition: routing.h:2619
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
Definition: routing.h:2609
IntVar * SlackVar(int64_t index) const
Definition: routing.h:2614
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
Definition: routing.h:2673
IntVar * FixedTransitVar(int64_t index) const
Definition: routing.h:2611
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
Definition: routing.cc:7136
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
Definition: routing.cc:7147
RoutingModel * model() const
Returns the model on which the dimension was created.
Definition: routing.h:2594
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
Definition: routing.cc:7140
const std::vector< SortedDisjointIntervalList > & forbidden_intervals() const
Returns forbidden intervals for each node.
Definition: routing.h:2625
const std::vector< std::pair< int64_t, int64_t > > & GetBreakDistanceDurationOfVehicle(int vehicle) const
Returns the pairs (distance, duration) specified by break distance constraints.
Definition: routing.cc:7175
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
Definition: routing.cc:7153
Solver * solver() const
Returns the underlying constraint solver.
Definition: routing.h:1511
const TransitCallback2 & TransitCallback(int callback_index) const
Definition: routing.h:509
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1382
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
Definition: routing.h:1366
const std::vector< IntVar * > & Nexts() const
Returns all next variables of the model, such that Nexts(i) is the next variable of the node correspo...
Definition: routing.h:1353
int64_t Start(int vehicle) const
Model inspection.
Definition: routing.h:1333
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1529
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1339
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1335
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
T * RevAlloc(T *object)
Registers the given object as being reversible.
void GetEventsWithOptionalEnvelopeGreaterThan(IntegerType target_envelope, int *critical_event, int *optional_event, IntegerType *available_energy) const
Definition: theta_tree.cc:190
void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
Definition: theta_tree.cc:125
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
Definition: theta_tree.cc:112
GRBmodel * model
int index
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
int64_t CapSub(int64_t x, int64_t y)
void AppendTasksFromIntervals(const std::vector< IntervalVar * > &intervals, DisjunctivePropagator::Tasks *tasks)
void AppendTasksFromPath(const std::vector< int64_t > &path, const TravelBounds &travel_bounds, const RoutingDimension &dimension, DisjunctivePropagator::Tasks *tasks)
void FillPathEvaluation(const std::vector< int64_t > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
Definition: routing.cc:6452
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
void FillTravelBoundsOfVehicle(int vehicle, const std::vector< int64_t > &path, const RoutingDimension &dimension, TravelBounds *travel_bounds)
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
int64_t time
Definition: resource.cc:1693
IntervalVar * interval
Definition: resource.cc:100
Rev< int64_t > start_max
Rev< int64_t > start_min
A structure to hold tasks described by their features.
Definition: routing.h:2183
std::vector< std::pair< int64_t, int64_t > > distance_duration
Definition: routing.h:2193
std::vector< const SortedDisjointIntervalList * > forbidden_intervals
Definition: routing.h:2192
std::vector< int64_t > post_travels
Definition: routing.h:2257
std::vector< int64_t > max_travels
Definition: routing.h:2255
std::vector< int64_t > pre_travels
Definition: routing.h:2256
std::vector< int64_t > min_travels
Definition: routing.h:2254