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