OR-Tools  9.0
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 
25 #include "ortools/base/logging.h"
30 #include "ortools/sat/theta_tree.h"
33 
34 namespace 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) ||
61  !DetectablePrecedencesWithChain(tasks) || !MirrorTasks(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.
194  theta_lambda_tree_.GetEventsWithOptionalEnvelopeGreaterThan(
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_max[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  int index_break_by_smin = tasks->num_chain_tasks;
364  while (index_break_by_emax < num_tasks) {
365  // Find next time point among start/end of covering intervals.
366  int64_t current_time =
367  CapAdd(tasks->end_max[index_break_by_emax], max_distance);
368  if (index_break_by_smin < num_tasks) {
369  current_time =
370  std::min(current_time, tasks->start_min[index_break_by_smin]);
371  }
372  if (previous_time < route_start_time && route_start_time < current_time) {
373  current_time = route_start_time;
374  }
375  if (previous_time < route_end_time && route_end_time < current_time) {
376  current_time = route_end_time;
377  }
378  // If num_active_tasks was 1, the unique active task must cover from
379  // previous_time to current_time.
380  if (num_active_tasks == 1) {
381  // xor_active_tasks is the unique task that can cover [previous_time,
382  // current_time).
383  if (xor_active_tasks != route_end) {
384  tasks->end_min[xor_active_tasks] =
385  std::max(tasks->end_min[xor_active_tasks],
386  CapSub(current_time, max_distance));
387  if (xor_active_tasks != route_start) {
388  tasks->duration_min[xor_active_tasks] = std::max(
389  tasks->duration_min[xor_active_tasks],
390  std::max(
391  minimum_break_duration,
392  CapSub(CapSub(current_time, max_distance), previous_time)));
393  }
394  }
395  }
396  // Process covering intervals that start or end at current_time.
397  while (index_break_by_smin < num_tasks &&
398  current_time == tasks->start_min[index_break_by_smin]) {
399  if (tasks->duration_max[index_break_by_smin] >=
400  minimum_break_duration) {
401  xor_active_tasks ^= index_break_by_smin;
402  ++num_active_tasks;
403  }
404  ++index_break_by_smin;
405  }
406  while (index_break_by_emax < num_tasks &&
407  current_time ==
408  CapAdd(tasks->end_max[index_break_by_emax], max_distance)) {
409  if (tasks->duration_max[index_break_by_emax] >=
410  minimum_break_duration) {
411  xor_active_tasks ^= index_break_by_emax;
412  --num_active_tasks;
413  }
414  ++index_break_by_emax;
415  }
416  if (current_time == route_start_time) {
417  xor_active_tasks ^= route_start;
418  --num_active_tasks;
419  }
420  if (current_time == route_end_time) {
421  xor_active_tasks ^= route_end;
422  ++num_active_tasks;
423  }
424  // If num_active_tasks becomes 1, the unique active task must cover from
425  // current_time.
426  if (num_active_tasks <= 0) return false;
427  if (num_active_tasks == 1) {
428  if (xor_active_tasks != route_start) {
429  // xor_active_tasks is the unique task that can cover from
430  // current_time to the next time point.
431  tasks->start_max[xor_active_tasks] =
432  std::min(tasks->start_max[xor_active_tasks], current_time);
433  if (xor_active_tasks != route_end) {
434  tasks->duration_min[xor_active_tasks] = std::max(
435  tasks->duration_min[xor_active_tasks], minimum_break_duration);
436  }
437  }
438  }
439  previous_time = current_time;
440  }
441  }
442  return true;
443 }
444 
446  const int num_chain_tasks = tasks->num_chain_tasks;
447  if (num_chain_tasks < 1) return true;
448  // TODO(user): add stronger bounds.
449  // The duration of the chain plus that of nonchain tasks that must be
450  // performed during the chain is a lower bound of the chain span.
451  {
452  int64_t sum_chain_durations = 0;
453  const auto duration_start = tasks->duration_min.begin();
454  const auto duration_end = tasks->duration_min.begin() + num_chain_tasks;
455  for (auto it = duration_start; it != duration_end; ++it) {
456  sum_chain_durations = CapAdd(sum_chain_durations, *it);
457  }
458  int64_t sum_forced_nonchain_durations = 0;
459  for (int i = num_chain_tasks; i < tasks->start_min.size(); ++i) {
460  // Tasks that can be executed before or after are skipped.
461  if (tasks->end_min[i] <= tasks->start_max[0] ||
462  tasks->end_min[num_chain_tasks - 1] <= tasks->start_max[i]) {
463  continue;
464  }
465  sum_forced_nonchain_durations =
466  CapAdd(sum_forced_nonchain_durations, tasks->duration_min[i]);
467  }
468  tasks->span_min =
469  std::max(tasks->span_min,
470  CapAdd(sum_chain_durations, sum_forced_nonchain_durations));
471  }
472  // The difference end of the chain - start of the chain is a lower bound.
473  {
474  const int64_t end_minus_start =
475  CapSub(tasks->end_min[num_chain_tasks - 1], tasks->start_max[0]);
476  tasks->span_min = std::max(tasks->span_min, end_minus_start);
477  }
478 
479  return tasks->span_min <= tasks->span_max;
480 }
481 
482 // Computes a lower bound of the span of the chain, taking into account only
483 // the first nonchain task.
484 // TODO(user): extend to arbitrary number of nonchain tasks.
486  // Do nothing if there are no chain tasks or no nonchain tasks.
487  const int num_chain_tasks = tasks->num_chain_tasks;
488  if (num_chain_tasks < 1) return true;
489  if (num_chain_tasks == tasks->start_min.size()) return true;
490  const int task_index = num_chain_tasks;
491  if (!Precedences(tasks)) return false;
492  const int64_t min_possible_chain_end = tasks->end_min[num_chain_tasks - 1];
493  const int64_t max_possible_chain_start = tasks->start_max[0];
494  // For each chain task i, compute cumulated duration of chain tasks before it.
495  int64_t total_duration = 0;
496  {
497  total_duration_before_.resize(num_chain_tasks);
498  for (int i = 0; i < num_chain_tasks; ++i) {
499  total_duration_before_[i] = total_duration;
500  total_duration = CapAdd(total_duration, tasks->duration_min[i]);
501  }
502  }
503  // Estimate span min of chain tasks. Use the schedule that ends at
504  // min_possible_chain_end and starts at smallest of start_max[0] or the
505  // threshold where pushing start[0] later does not make a difference to the
506  // chain span because of chain precedence constraints,
507  // i.e. min_possible_chain_end - total_duration.
508  {
509  const int64_t chain_span_min =
510  min_possible_chain_end -
511  std::min(tasks->start_max[0], min_possible_chain_end - total_duration);
512  if (chain_span_min > tasks->span_max) {
513  return false;
514  } else {
515  tasks->span_min = std::max(tasks->span_min, chain_span_min);
516  }
517  // If task can be performed before or after the chain,
518  // span_min is chain_span_min.
519  if (tasks->end_min[task_index] <= tasks->start_max[0] ||
520  tasks->end_min[num_chain_tasks - 1] <= tasks->start_max[task_index]) {
521  return true;
522  }
523  }
524  // Scan all possible preemption positions of the nontask chain,
525  // keep the one that yields the minimum span.
526  int64_t span_min = std::numeric_limits<int64_t>::max();
527  bool schedule_is_feasible = false;
528  for (int i = 0; i < num_chain_tasks; ++i) {
529  if (!tasks->is_preemptible[i]) continue;
530  // Estimate span min if tasks is performed during i.
531  // For all possible minimal-span schedules, there is a schedule where task i
532  // and nonchain task form a single block. Thus, we only consider those.
533  const int64_t block_start_min =
534  std::max(tasks->start_min[i],
535  tasks->start_min[task_index] - tasks->duration_min[i]);
536  const int64_t block_start_max =
537  std::min(tasks->start_max[task_index],
538  tasks->start_max[i] - tasks->duration_min[task_index]);
539  if (block_start_min > block_start_max) continue;
540 
541  // Compute the block start that yields the minimal span.
542  // Given a feasible block start, a chain of minimum span constrained to
543  // this particular block start can be obtained by scheduling all tasks after
544  // the block at their earliest, and all tasks before it at their latest.
545  // The span can be decomposed into two parts: the head, which are the
546  // tasks that are before the block, and the tail, which are the block and
547  // the tasks after it.
548  // When the block start varies, the head length of the optimal schedule
549  // described above decreases as much as the block start decreases, until
550  // an inflection point at which it stays constant. That inflection value
551  // is the one where the precedence constraints force the chain start to
552  // decrease because of durations.
553  const int64_t head_inflection =
554  max_possible_chain_start + total_duration_before_[i];
555  // The map from block start to minimal tail length also has an inflection
556  // point, that additionally depends on the nonchain task's duration.
557  const int64_t tail_inflection =
558  min_possible_chain_end - (total_duration - total_duration_before_[i]) -
559  tasks->duration_min[task_index];
560  // All block start values between these two yield the same minimal span.
561  // Indeed, first, mind that the inflection points might be in any order.
562  // - if head_inflection < tail_inflection, then inside the interval
563  // [head_inflection, tail_inflection], increasing the block start by delta
564  // decreases the tail length by delta and increases the head length by
565  // delta too.
566  // - if tail_inflection < head_inflection, then inside the interval
567  // [tail_inflection, head_inflection], head length is constantly at
568  // total_duration_before_[i], and tail length is also constant.
569  // In both cases, outside of the interval, one part is constant and the
570  // other increases as much as the distance to the interval.
571  // We can abstract inflection point to the interval they form.
572  const int64_t optimal_interval_min_start =
573  std::min(head_inflection, tail_inflection);
574  const int64_t optimal_interval_max_start =
575  std::max(head_inflection, tail_inflection);
576  // If the optimal interval for block start intersects the feasible interval,
577  // we can select any point within it, for instance the earliest one.
578  int64_t block_start = std::max(optimal_interval_min_start, block_start_min);
579  // If the intervals do not intersect, the feasible value closest to the
580  // optimal interval has the minimal span, because the span increases as
581  // much as the distance to the optimal interval.
582  if (optimal_interval_max_start < block_start_min) {
583  // Optimal interval is before feasible interval, closest is feasible min.
584  block_start = block_start_min;
585  } else if (block_start_max < optimal_interval_min_start) {
586  // Optimal interval is after feasible interval, closest is feasible max.
587  block_start = block_start_max;
588  }
589  // Compute span for the chosen block start.
590  const int64_t head_duration =
591  std::max(block_start, head_inflection) - max_possible_chain_start;
592  const int64_t tail_duration =
593  min_possible_chain_end - std::min(block_start, tail_inflection);
594  const int64_t optimal_span_at_i = head_duration + tail_duration;
595  span_min = std::min(span_min, optimal_span_at_i);
596  schedule_is_feasible = true;
597  }
598  if (!schedule_is_feasible || span_min > tasks->span_max) {
599  return false;
600  } else {
601  tasks->span_min = std::max(tasks->span_min, span_min);
602  return true;
603  }
604 }
605 
606 void AppendTasksFromPath(const std::vector<int64_t>& path,
607  const TravelBounds& travel_bounds,
608  const RoutingDimension& dimension,
610  const int num_nodes = path.size();
611  DCHECK_EQ(travel_bounds.pre_travels.size(), num_nodes - 1);
612  DCHECK_EQ(travel_bounds.post_travels.size(), num_nodes - 1);
613  for (int i = 0; i < num_nodes; ++i) {
614  const int64_t cumul_min = dimension.CumulVar(path[i])->Min();
615  const int64_t cumul_max = dimension.CumulVar(path[i])->Max();
616  // Add task associated to visit i.
617  // Visits start at Cumul(path[i]) - before_visit
618  // and end at Cumul(path[i]) + after_visit
619  {
620  const int64_t before_visit =
621  (i == 0) ? 0 : travel_bounds.post_travels[i - 1];
622  const int64_t after_visit =
623  (i == num_nodes - 1) ? 0 : travel_bounds.pre_travels[i];
624 
625  tasks->start_min.push_back(CapSub(cumul_min, before_visit));
626  tasks->start_max.push_back(CapSub(cumul_max, before_visit));
627  tasks->duration_min.push_back(CapAdd(before_visit, after_visit));
628  tasks->duration_max.push_back(CapAdd(before_visit, after_visit));
629  tasks->end_min.push_back(CapAdd(cumul_min, after_visit));
630  tasks->end_max.push_back(CapAdd(cumul_max, after_visit));
631  tasks->is_preemptible.push_back(false);
632  }
633  if (i == num_nodes - 1) break;
634 
635  // Tasks from travels.
636  // A travel task starts at Cumul(path[i]) + pre_travel,
637  // last for FixedTransitVar(path[i]) - pre_travel - post_travel,
638  // and must end at the latest at Cumul(path[i+1]) - post_travel.
639  {
640  const int64_t pre_travel = travel_bounds.pre_travels[i];
641  const int64_t post_travel = travel_bounds.post_travels[i];
642  tasks->start_min.push_back(CapAdd(cumul_min, pre_travel));
643  tasks->start_max.push_back(CapAdd(cumul_max, pre_travel));
644  tasks->duration_min.push_back(
645  std::max<int64_t>(0, CapSub(travel_bounds.min_travels[i],
646  CapAdd(pre_travel, post_travel))));
647  tasks->duration_max.push_back(
648  travel_bounds.max_travels[i] == std::numeric_limits<int64_t>::max()
650  : std::max<int64_t>(0, CapSub(travel_bounds.max_travels[i],
651  CapAdd(pre_travel, post_travel))));
652  tasks->end_min.push_back(
653  CapSub(dimension.CumulVar(path[i + 1])->Min(), post_travel));
654  tasks->end_max.push_back(
655  CapSub(dimension.CumulVar(path[i + 1])->Max(), post_travel));
656  tasks->is_preemptible.push_back(true);
657  }
658  }
659 }
660 
661 void FillTravelBoundsOfVehicle(int vehicle, const std::vector<int64_t>& path,
662  const RoutingDimension& dimension,
663  TravelBounds* travel_bounds) {
664  // Fill path and min/max/pre/post travel bounds.
665  FillPathEvaluation(path, dimension.transit_evaluator(vehicle),
666  &travel_bounds->min_travels);
667  const int num_travels = travel_bounds->min_travels.size();
668  travel_bounds->max_travels.assign(num_travels,
670  {
671  const int index = dimension.GetPreTravelEvaluatorOfVehicle(vehicle);
672  if (index == -1) {
673  travel_bounds->pre_travels.assign(num_travels, 0);
674  } else {
675  FillPathEvaluation(path, dimension.model()->TransitCallback(index),
676  &travel_bounds->pre_travels);
677  }
678  }
679  {
680  const int index = dimension.GetPostTravelEvaluatorOfVehicle(vehicle);
681  if (index == -1) {
682  travel_bounds->post_travels.assign(num_travels, 0);
683  } else {
684  FillPathEvaluation(path, dimension.model()->TransitCallback(index),
685  &travel_bounds->post_travels);
686  }
687  }
688 }
689 
690 void AppendTasksFromIntervals(const std::vector<IntervalVar*>& intervals,
692  for (IntervalVar* interval : intervals) {
693  if (!interval->MustBePerformed()) continue;
694  tasks->start_min.push_back(interval->StartMin());
695  tasks->start_max.push_back(interval->StartMax());
696  tasks->duration_min.push_back(interval->DurationMin());
697  tasks->duration_max.push_back(interval->DurationMax());
698  tasks->end_min.push_back(interval->EndMin());
699  tasks->end_max.push_back(interval->EndMax());
700  tasks->is_preemptible.push_back(false);
701  }
702 }
703 
705  const RoutingDimension* dimension)
706  : Constraint(dimension->model()->solver()),
707  model_(dimension->model()),
708  dimension_(dimension) {
709  vehicle_demons_.resize(model_->vehicles());
710 }
711 
713  for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
714  if (dimension_->GetBreakIntervalsOfVehicle(vehicle).empty() &&
715  dimension_->GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
716  continue;
717  }
718  vehicle_demons_[vehicle] = MakeDelayedConstraintDemon1(
719  solver(), this, &GlobalVehicleBreaksConstraint::PropagateVehicle,
720  "PropagateVehicle", vehicle);
721  for (IntervalVar* interval :
722  dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
723  interval->WhenAnything(vehicle_demons_[vehicle]);
724  }
725  }
726  const int num_cumuls = dimension_->cumuls().size();
727  const int num_nexts = model_->Nexts().size();
728  for (int node = 0; node < num_cumuls; node++) {
729  Demon* dimension_demon = MakeConstraintDemon1(
730  solver(), this, &GlobalVehicleBreaksConstraint::PropagateNode,
731  "PropagateNode", node);
732  if (node < num_nexts) {
733  model_->NextVar(node)->WhenBound(dimension_demon);
734  dimension_->SlackVar(node)->WhenRange(dimension_demon);
735  }
736  model_->VehicleVar(node)->WhenBound(dimension_demon);
737  dimension_->CumulVar(node)->WhenRange(dimension_demon);
738  }
739 }
740 
742  for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
743  if (!dimension_->GetBreakIntervalsOfVehicle(vehicle).empty() ||
744  !dimension_->GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
745  PropagateVehicle(vehicle);
746  }
747  }
748 }
749 
750 // This dispatches node events to the right vehicle propagator.
751 // It also filters out a part of uninteresting events, on which the vehicle
752 // propagator will not find anything new.
753 void GlobalVehicleBreaksConstraint::PropagateNode(int node) {
754  if (!model_->VehicleVar(node)->Bound()) return;
755  const int vehicle = model_->VehicleVar(node)->Min();
756  if (vehicle < 0 || vehicle_demons_[vehicle] == nullptr) return;
757  EnqueueDelayedDemon(vehicle_demons_[vehicle]);
758 }
759 
760 void GlobalVehicleBreaksConstraint::FillPartialPathOfVehicle(int vehicle) {
761  path_.clear();
762  int current = model_->Start(vehicle);
763  while (!model_->IsEnd(current)) {
764  path_.push_back(current);
765  current = model_->NextVar(current)->Bound()
766  ? model_->NextVar(current)->Min()
767  : model_->End(vehicle);
768  }
769  path_.push_back(current);
770 }
771 
772 void GlobalVehicleBreaksConstraint::FillPathTravels(
773  const std::vector<int64_t>& path) {
774  const int num_travels = path.size() - 1;
775  travel_bounds_.min_travels.resize(num_travels);
776  travel_bounds_.max_travels.resize(num_travels);
777  for (int i = 0; i < num_travels; ++i) {
778  travel_bounds_.min_travels[i] = dimension_->FixedTransitVar(path[i])->Min();
779  travel_bounds_.max_travels[i] = dimension_->FixedTransitVar(path[i])->Max();
780  }
781 }
782 
783 // First, perform energy-based reasoning on intervals and cumul variables.
784 // Then, perform reasoning on slack variables.
785 void GlobalVehicleBreaksConstraint::PropagateVehicle(int vehicle) {
786  // Fill path and pre/post travel information.
787  FillPartialPathOfVehicle(vehicle);
788  const int num_nodes = path_.size();
789  FillPathTravels(path_);
790  {
791  const int index = dimension_->GetPreTravelEvaluatorOfVehicle(vehicle);
792  if (index == -1) {
793  travel_bounds_.pre_travels.assign(num_nodes - 1, 0);
794  } else {
795  FillPathEvaluation(path_, model_->TransitCallback(index),
796  &travel_bounds_.pre_travels);
797  }
798  }
799  {
800  const int index = dimension_->GetPostTravelEvaluatorOfVehicle(vehicle);
801  if (index == -1) {
802  travel_bounds_.post_travels.assign(num_nodes - 1, 0);
803  } else {
804  FillPathEvaluation(path_, model_->TransitCallback(index),
805  &travel_bounds_.post_travels);
806  }
807  }
808  // The last travel might not be fixed: in that case, relax its information.
809  if (!model_->NextVar(path_[num_nodes - 2])->Bound()) {
810  travel_bounds_.min_travels.back() = 0;
811  travel_bounds_.max_travels.back() = std::numeric_limits<int64_t>::max();
812  travel_bounds_.pre_travels.back() = 0;
813  travel_bounds_.post_travels.back() = 0;
814  }
815 
816  // Fill tasks from path, break intervals, and break constraints.
817  tasks_.Clear();
818  AppendTasksFromPath(path_, travel_bounds_, *dimension_, &tasks_);
819  tasks_.num_chain_tasks = tasks_.start_min.size();
821  &tasks_);
822  tasks_.distance_duration =
823  dimension_->GetBreakDistanceDurationOfVehicle(vehicle);
824 
825  // Do the actual reasoning, no need to continue if infeasible.
826  if (!disjunctive_propagator_.Propagate(&tasks_)) solver()->Fail();
827 
828  // Make task translators to help set new bounds of CP variables.
829  task_translators_.clear();
830  for (int i = 0; i < num_nodes; ++i) {
831  const int64_t before_visit =
832  (i == 0) ? 0 : travel_bounds_.post_travels[i - 1];
833  const int64_t after_visit =
834  (i == num_nodes - 1) ? 0 : travel_bounds_.pre_travels[i];
835  task_translators_.emplace_back(dimension_->CumulVar(path_[i]), before_visit,
836  after_visit);
837  if (i == num_nodes - 1) break;
838  task_translators_.emplace_back(); // Dummy translator for travel tasks.
839  }
840  for (IntervalVar* interval :
841  dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
842  if (!interval->MustBePerformed()) continue;
843  task_translators_.emplace_back(interval);
844  }
845 
846  // Push new bounds to CP variables.
847  const int num_tasks = tasks_.start_min.size();
848  for (int task = 0; task < num_tasks; ++task) {
849  task_translators_[task].SetStartMin(tasks_.start_min[task]);
850  task_translators_[task].SetStartMax(tasks_.start_max[task]);
851  task_translators_[task].SetDurationMin(tasks_.duration_min[task]);
852  task_translators_[task].SetEndMin(tasks_.end_min[task]);
853  task_translators_[task].SetEndMax(tasks_.end_max[task]);
854  }
855 
856  // Reasoning on slack variables: when intervals must be inside an arc,
857  // that arc's slack must be large enough to accommodate for those.
858  // TODO(user): Make a version more efficient than O(n^2).
859  if (dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) return;
860  // If the last arc of the path was not bound, do not change slack.
861  const int64_t last_bound_arc =
862  num_nodes - 2 - (model_->NextVar(path_[num_nodes - 2])->Bound() ? 0 : 1);
863  for (int i = 0; i <= last_bound_arc; ++i) {
864  const int64_t arc_start_max =
865  CapSub(dimension_->CumulVar(path_[i])->Max(),
866  i > 0 ? travel_bounds_.post_travels[i - 1] : 0);
867  const int64_t arc_end_min =
868  CapAdd(dimension_->CumulVar(path_[i + 1])->Min(),
869  i < num_nodes - 2 ? travel_bounds_.pre_travels[i + 1] : 0);
870  int64_t total_break_inside_arc = 0;
871  for (IntervalVar* interval :
872  dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
873  if (!interval->MustBePerformed()) continue;
874  const int64_t interval_start_max = interval->StartMax();
875  const int64_t interval_end_min = interval->EndMin();
876  const int64_t interval_duration_min = interval->DurationMin();
877  // If interval cannot end before the arc's from node and
878  // cannot start after the 'to' node, then it must be inside the arc.
879  if (arc_start_max < interval_end_min &&
880  interval_start_max < arc_end_min) {
881  total_break_inside_arc += interval_duration_min;
882  }
883  }
884  dimension_->SlackVar(path_[i])->SetMin(total_break_inside_arc);
885  }
886  // Reasoning on optional intervals.
887  // TODO(user): merge this with energy-based reasoning.
888  // If there is no optional interval, skip the rest of this function.
889  {
890  bool has_optional = false;
891  for (const IntervalVar* interval :
892  dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
893  if (interval->MayBePerformed() && !interval->MustBePerformed()) {
894  has_optional = true;
895  break;
896  }
897  }
898  if (!has_optional) return;
899  }
900  const std::vector<IntervalVar*>& break_intervals =
901  dimension_->GetBreakIntervalsOfVehicle(vehicle);
902  for (int pos = 0; pos < num_nodes - 1; ++pos) {
903  const int64_t current_slack_max = dimension_->SlackVar(path_[pos])->Max();
904  const int64_t visit_start_offset =
905  pos > 0 ? travel_bounds_.post_travels[pos - 1] : 0;
906  const int64_t visit_start_max =
907  CapSub(dimension_->CumulVar(path_[pos])->Max(), visit_start_offset);
908  const int64_t visit_end_offset =
909  (pos < num_nodes - 1) ? travel_bounds_.pre_travels[pos] : 0;
910  const int64_t visit_end_min =
911  CapAdd(dimension_->CumulVar(path_[pos])->Min(), visit_end_offset);
912 
913  for (IntervalVar* interval : break_intervals) {
914  if (!interval->MayBePerformed()) continue;
915  const bool interval_is_performed = interval->MustBePerformed();
916  const int64_t interval_start_max = interval->StartMax();
917  const int64_t interval_end_min = interval->EndMin();
918  const int64_t interval_duration_min = interval->DurationMin();
919  // When interval cannot fit inside current arc,
920  // do disjunctive reasoning on full arc.
921  if (pos < num_nodes - 1 && interval_duration_min > current_slack_max) {
922  // The arc lasts from CumulVar(path_[pos]) - post_travel_[pos] to
923  // CumulVar(path_[pos+1]) + pre_travel_[pos+1].
924  const int64_t arc_start_offset =
925  pos > 0 ? travel_bounds_.post_travels[pos - 1] : 0;
926  const int64_t arc_start_max = visit_start_max;
927  const int64_t arc_end_offset =
928  (pos < num_nodes - 2) ? travel_bounds_.pre_travels[pos + 1] : 0;
929  const int64_t arc_end_min =
930  CapAdd(dimension_->CumulVar(path_[pos + 1])->Min(), arc_end_offset);
931  // Interval not before.
932  if (arc_start_max < interval_end_min) {
933  interval->SetStartMin(arc_end_min);
934  if (interval_is_performed) {
935  dimension_->CumulVar(path_[pos + 1])
936  ->SetMax(CapSub(interval_start_max, arc_end_offset));
937  }
938  }
939  // Interval not after.
940  if (interval_start_max < arc_end_min) {
941  interval->SetEndMax(arc_start_max);
942  if (interval_is_performed) {
943  dimension_->CumulVar(path_[pos])
944  ->SetMin(CapSub(interval_end_min, arc_start_offset));
945  }
946  }
947  continue;
948  }
949  // Interval could fit inside arc: do disjunctive reasoning between
950  // interval and visit.
951  // Interval not before.
952  if (visit_start_max < interval_end_min) {
953  interval->SetStartMin(visit_end_min);
954  if (interval_is_performed) {
955  dimension_->CumulVar(path_[pos])
956  ->SetMax(CapSub(interval_start_max, visit_end_offset));
957  }
958  }
959  // Interval not after.
960  if (interval_start_max < visit_end_min) {
961  interval->SetEndMax(visit_start_max);
962  if (interval_is_performed) {
963  dimension_->CumulVar(path_[pos])
964  ->SetMin(CapAdd(interval_end_min, visit_start_offset));
965  }
966  }
967  }
968  }
969 }
970 
971 namespace {
972 class VehicleBreaksFilter : public BasePathFilter {
973  public:
974  VehicleBreaksFilter(const RoutingModel& routing_model,
975  const RoutingDimension& dimension);
976  std::string DebugString() const override { return "VehicleBreaksFilter"; }
977  bool AcceptPath(int64_t path_start, int64_t chain_start,
978  int64_t chain_end) override;
979 
980  private:
981  // Fills path_ with the path of vehicle, start to end.
982  void FillPathOfVehicle(int64_t vehicle);
983  std::vector<int64_t> path_;
984  // Handles to model.
985  const RoutingModel& model_;
986  const RoutingDimension& dimension_;
987  // Strong energy-based filtering algorithm.
988  DisjunctivePropagator disjunctive_propagator_;
989  DisjunctivePropagator::Tasks tasks_;
990  // Used to check whether propagation changed a vector.
991  std::vector<int64_t> old_start_min_;
992  std::vector<int64_t> old_start_max_;
993  std::vector<int64_t> old_end_min_;
994  std::vector<int64_t> old_end_max_;
995 
996  std::vector<int> start_to_vehicle_;
997  TravelBounds travel_bounds_;
998 };
999 
1000 VehicleBreaksFilter::VehicleBreaksFilter(const RoutingModel& routing_model,
1001  const RoutingDimension& dimension)
1002  : BasePathFilter(routing_model.Nexts(),
1003  routing_model.Size() + routing_model.vehicles()),
1004  model_(routing_model),
1005  dimension_(dimension) {
1006  DCHECK(dimension_.HasBreakConstraints());
1007  start_to_vehicle_.resize(Size(), -1);
1008  for (int i = 0; i < routing_model.vehicles(); ++i) {
1009  start_to_vehicle_[routing_model.Start(i)] = i;
1010  }
1011 }
1012 
1013 void VehicleBreaksFilter::FillPathOfVehicle(int64_t vehicle) {
1014  path_.clear();
1015  int current = model_.Start(vehicle);
1016  while (!model_.IsEnd(current)) {
1017  path_.push_back(current);
1018  current = GetNext(current);
1019  }
1020  path_.push_back(current);
1021 }
1022 
1023 bool VehicleBreaksFilter::AcceptPath(int64_t path_start, int64_t chain_start,
1024  int64_t chain_end) {
1025  const int vehicle = start_to_vehicle_[path_start];
1026  if (dimension_.GetBreakIntervalsOfVehicle(vehicle).empty() &&
1027  dimension_.GetBreakDistanceDurationOfVehicle(vehicle).empty()) {
1028  return true;
1029  }
1030  // Fill path and pre/post travel information.
1031  FillPathOfVehicle(vehicle);
1032  FillTravelBoundsOfVehicle(vehicle, path_, dimension_, &travel_bounds_);
1033  // Fill tasks from path, forbidden intervals, breaks and break constraints.
1034  tasks_.Clear();
1035  AppendTasksFromPath(path_, travel_bounds_, dimension_, &tasks_);
1036  tasks_.num_chain_tasks = tasks_.start_min.size();
1038  &tasks_);
1039  // Add forbidden intervals only if a node has some.
1040  tasks_.forbidden_intervals.clear();
1041  if (std::any_of(path_.begin(), path_.end(), [this](int64_t node) {
1042  return dimension_.forbidden_intervals()[node].NumIntervals() > 0;
1043  })) {
1044  tasks_.forbidden_intervals.assign(tasks_.start_min.size(), nullptr);
1045  for (int i = 0; i < path_.size(); ++i) {
1046  tasks_.forbidden_intervals[2 * i] =
1047  &(dimension_.forbidden_intervals()[path_[i]]);
1048  }
1049  }
1050  // Max distance duration constraint.
1051  tasks_.distance_duration =
1052  dimension_.GetBreakDistanceDurationOfVehicle(vehicle);
1053 
1054  // Reduce bounds until failure or fixed point is reached.
1055  // We set a maximum amount of iterations to avoid slow propagation.
1056  bool is_feasible = true;
1057  int maximum_num_iterations = 8;
1058  while (--maximum_num_iterations >= 0) {
1059  old_start_min_ = tasks_.start_min;
1060  old_start_max_ = tasks_.start_max;
1061  old_end_min_ = tasks_.end_min;
1062  old_end_max_ = tasks_.end_max;
1063  is_feasible = disjunctive_propagator_.Propagate(&tasks_);
1064  if (!is_feasible) break;
1065  // If fixed point reached, stop.
1066  if ((old_start_min_ == tasks_.start_min) &&
1067  (old_start_max_ == tasks_.start_max) &&
1068  (old_end_min_ == tasks_.end_min) && (old_end_max_ == tasks_.end_max)) {
1069  break;
1070  }
1071  }
1072  return is_feasible;
1073 }
1074 
1075 } // namespace
1076 
1078  const RoutingModel& routing_model, const RoutingDimension& dimension) {
1079  return routing_model.solver()->RevAlloc(
1080  new VehicleBreaksFilter(routing_model, dimension));
1081 }
1082 
1083 } // 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:895
#define DCHECK(condition)
Definition: base/logging.h:892
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
A constraint is the main modeling object.
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:2374
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
Definition: routing.h:2403
IntVar * FixedTransitVar(int64_t index) const
Definition: routing.h:2395
RoutingModel * model() const
Returns the model on which the dimension was created.
Definition: routing.h:2378
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
Definition: routing.cc:6374
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
Definition: routing.cc:6385
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
Definition: routing.cc:6378
IntVar * SlackVar(int64_t index) const
Definition: routing.h:2398
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:2457
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:2393
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:6413
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
Definition: routing.cc:6391
const std::vector< SortedDisjointIntervalList > & forbidden_intervals() const
Returns forbidden intervals for each node.
Definition: routing.h:2409
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
Definition: routing.h:1211
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1226
Solver * solver() const
Returns the underlying constraint solver.
Definition: routing.h:1332
int64_t Start(int vehicle) const
Model inspection.
Definition: routing.h:1184
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1350
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:1204
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1190
const TransitCallback2 & TransitCallback(int callback_index) const
Definition: routing.h:408
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1186
T * RevAlloc(T *object)
Registers the given object as being reversible.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
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
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
int64_t CapSub(int64_t x, int64_t y)
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)
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:5690
void FillTravelBoundsOfVehicle(int vehicle, const std::vector< int64_t > &path, const RoutingDimension &dimension, TravelBounds *travel_bounds)
int index
Definition: pack.cc:509
int64_t time
Definition: resource.cc:1691
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:1967
std::vector< std::pair< int64_t, int64_t > > distance_duration
Definition: routing.h:1977
std::vector< const SortedDisjointIntervalList * > forbidden_intervals
Definition: routing.h:1976
std::vector< int64_t > post_travels
Definition: routing.h:2041
std::vector< int64_t > max_travels
Definition: routing.h:2039
std::vector< int64_t > pre_travels
Definition: routing.h:2040
std::vector< int64_t > min_travels
Definition: routing.h:2038