69 if (num_chain_tasks > 0) {
72 for (
int task = 0; task < num_chain_tasks; ++task) {
82 for (
int task = num_chain_tasks - 1; task >= 0; --task) {
86 if (time < tasks->
start_min[task])
return false;
91 const int num_tasks = tasks->
start_min.size();
92 for (
int task = 0; task < num_tasks; ++task) {
123 const int num_tasks = tasks->
start_min.size();
125 for (
int task = 0; task < num_tasks; ++task) {
126 const int64_t t = -tasks->
start_min[task];
131 for (
int task = 0; task < num_tasks; ++task) {
132 const int64_t t = -tasks->
start_max[task];
142 std::reverse(it, it + num_chain_tasks);
143 std::reverse(it + num_chain_tasks, it + num_tasks);
153 const int num_tasks = tasks->
start_min.size();
155 tasks_by_start_min_.resize(num_tasks);
156 std::iota(tasks_by_start_min_.begin(), tasks_by_start_min_.end(), 0);
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;
165 tasks_by_end_max_.resize(num_tasks);
166 std::iota(tasks_by_end_max_.begin(), tasks_by_end_max_.end(), 0);
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]; });
173 theta_lambda_tree_.
Reset(num_tasks);
174 for (
const int task : tasks_by_end_max_) {
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();
193 int64_t available_energy;
195 tasks->
end_max[task], &critical_event, &optional_event,
197 const int optional_task = tasks_by_start_min_[optional_event];
207 theta_lambda_tree_.
RemoveEvent(event_of_task_[task]);
214 const int num_tasks = tasks->
start_min.size();
216 tasks_by_start_min_.resize(num_tasks);
217 std::iota(tasks_by_start_min_.begin(), tasks_by_start_min_.end(), 0);
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;
225 theta_lambda_tree_.
Reset(num_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];
240 int index_nonchain = 0;
241 for (
int i = 0; i < num_chain_tasks; ++i) {
244 while (index_nonchain < nonchain_tasks_by_start_max_.size()) {
245 const int task = nonchain_tasks_by_start_max_[index_nonchain];
250 event_of_task_[task], tasks->
start_min[task],
256 const int64_t new_start_min = theta_lambda_tree_.
GetEnvelope();
268 const int num_tasks = tasks->
start_min.size();
269 for (
int task = 0; task < num_tasks; ++task) {
305 const int route_start = 0;
307 const int num_tasks = tasks->
start_min.size();
326 for (
int task = tasks->
num_chain_tasks + 1; task < num_tasks; ++task) {
330 for (
int task = num_tasks - 2; task >= tasks->
num_chain_tasks; --task) {
336 while (index_break_by_emax < num_tasks &&
337 tasks->
end_max[index_break_by_emax] <= tasks->
end_min[route_start]) {
338 ++index_break_by_emax;
341 if (index_break_by_emax == num_tasks) {
357 int64_t xor_active_tasks = route_start;
358 int num_active_tasks = 1;
360 const int64_t route_start_time =
362 const int64_t route_end_time = tasks->
start_min[route_end];
366 int index_break_by_smin = index_break_by_emax;
367 while (index_break_by_emax < num_tasks) {
369 int64_t current_time =
371 if (index_break_by_smin < num_tasks) {
375 if (previous_time < route_start_time && route_start_time < current_time) {
376 current_time = route_start_time;
378 if (previous_time < route_end_time && route_end_time < current_time) {
379 current_time = route_end_time;
383 if (num_active_tasks == 1) {
386 if (xor_active_tasks != route_end) {
387 tasks->
end_min[xor_active_tasks] =
389 CapSub(current_time, max_distance));
390 if (xor_active_tasks != route_start) {
394 minimum_break_duration,
395 CapSub(
CapSub(current_time, max_distance), previous_time)));
400 while (index_break_by_smin < num_tasks &&
401 current_time == tasks->
start_min[index_break_by_smin]) {
403 minimum_break_duration) {
404 xor_active_tasks ^= index_break_by_smin;
407 ++index_break_by_smin;
409 while (index_break_by_emax < num_tasks &&
413 minimum_break_duration) {
414 xor_active_tasks ^= index_break_by_emax;
417 ++index_break_by_emax;
419 if (current_time == route_start_time) {
420 xor_active_tasks ^= route_start;
423 if (current_time == route_end_time) {
424 xor_active_tasks ^= route_end;
429 if (num_active_tasks <= 0)
return false;
430 if (num_active_tasks == 1) {
431 if (xor_active_tasks != route_start) {
436 if (xor_active_tasks != route_end) {
438 tasks->
duration_min[xor_active_tasks], minimum_break_duration);
442 previous_time = current_time;
450 if (num_chain_tasks < 1)
return true;
455 int64_t sum_chain_durations = 0;
456 const auto duration_start = tasks->
duration_min.begin();
457 const auto duration_end = tasks->
duration_min.begin() + num_chain_tasks;
458 for (
auto it = duration_start; it != duration_end; ++it) {
459 sum_chain_durations =
CapAdd(sum_chain_durations, *it);
461 int64_t sum_forced_nonchain_durations = 0;
462 for (
int i = num_chain_tasks; i < tasks->
start_min.size(); ++i) {
468 sum_forced_nonchain_durations =
473 CapAdd(sum_chain_durations, sum_forced_nonchain_durations));
477 const int64_t end_minus_start =
491 if (num_chain_tasks < 1)
return true;
492 if (num_chain_tasks == tasks->
start_min.size())
return true;
493 const int task_index = num_chain_tasks;
495 const int64_t min_possible_chain_end = tasks->
end_min[num_chain_tasks - 1];
496 const int64_t max_possible_chain_start = tasks->
start_max[0];
498 int64_t total_duration = 0;
500 total_duration_before_.resize(num_chain_tasks);
501 for (
int i = 0; i < num_chain_tasks; ++i) {
502 total_duration_before_[i] = total_duration;
512 const int64_t chain_span_min =
513 min_possible_chain_end -
515 if (chain_span_min > tasks->
span_max) {
530 bool schedule_is_feasible =
false;
531 for (
int i = 0; i < num_chain_tasks; ++i) {
536 const int64_t block_start_min =
539 const int64_t block_start_max =
542 if (block_start_min > block_start_max)
continue;
556 const int64_t head_inflection =
557 max_possible_chain_start + total_duration_before_[i];
560 const int64_t tail_inflection =
561 min_possible_chain_end - (total_duration - total_duration_before_[i]) -
575 const int64_t optimal_interval_min_start =
576 std::min(head_inflection, tail_inflection);
577 const int64_t optimal_interval_max_start =
578 std::max(head_inflection, tail_inflection);
581 int64_t block_start =
std::max(optimal_interval_min_start, block_start_min);
585 if (optimal_interval_max_start < block_start_min) {
587 block_start = block_start_min;
588 }
else if (block_start_max < optimal_interval_min_start) {
590 block_start = block_start_max;
593 const int64_t head_duration =
594 std::max(block_start, head_inflection) - max_possible_chain_start;
595 const int64_t tail_duration =
596 min_possible_chain_end -
std::min(block_start, tail_inflection);
597 const int64_t optimal_span_at_i = head_duration + tail_duration;
598 span_min =
std::min(span_min, optimal_span_at_i);
599 schedule_is_feasible =
true;
601 if (!schedule_is_feasible || span_min > tasks->
span_max) {
613 const int num_nodes = path.size();
614 DCHECK_EQ(travel_bounds.
pre_travels.size(), num_nodes - 1);
615 DCHECK_EQ(travel_bounds.
post_travels.size(), num_nodes - 1);
616 for (
int i = 0; i < num_nodes; ++i) {
617 const int64_t cumul_min = dimension.
CumulVar(path[i])->
Min();
618 const int64_t cumul_max = dimension.
CumulVar(path[i])->
Max();
623 const int64_t before_visit =
625 const int64_t after_visit =
626 (i == num_nodes - 1) ? 0 : travel_bounds.
pre_travels[i];
636 if (i == num_nodes - 1)
break;
643 const int64_t pre_travel = travel_bounds.
pre_travels[i];
644 const int64_t post_travel = travel_bounds.
post_travels[i];
649 CapAdd(pre_travel, post_travel))));
654 CapAdd(pre_travel, post_travel))));
670 const int num_travels = travel_bounds->
min_travels.size();
710 model_(dimension->
model()),
711 dimension_(dimension) {
712 vehicle_demons_.resize(model_->
vehicles());
716 for (
int vehicle = 0; vehicle < model_->
vehicles(); vehicle++) {
722 solver(),
this, &GlobalVehicleBreaksConstraint::PropagateVehicle,
723 "PropagateVehicle", vehicle);
726 interval->WhenAnything(vehicle_demons_[vehicle]);
729 const int num_cumuls = dimension_->
cumuls().size();
730 const int num_nexts = model_->
Nexts().size();
731 for (
int node = 0; node < num_cumuls; node++) {
733 solver(),
this, &GlobalVehicleBreaksConstraint::PropagateNode,
734 "PropagateNode", node);
735 if (node < num_nexts) {
745 for (
int vehicle = 0; vehicle < model_->
vehicles(); vehicle++) {
748 PropagateVehicle(vehicle);
756 void GlobalVehicleBreaksConstraint::PropagateNode(
int node) {
759 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
763 void GlobalVehicleBreaksConstraint::FillPartialPathOfVehicle(
int vehicle) {
765 int current = model_->
Start(vehicle);
766 while (!model_->
IsEnd(current)) {
767 path_.push_back(current);
770 : model_->
End(vehicle);
772 path_.push_back(current);
775 void GlobalVehicleBreaksConstraint::FillPathTravels(
776 const std::vector<int64_t>& path) {
777 const int num_travels = path.size() - 1;
780 for (
int i = 0; i < num_travels; ++i) {
788 void GlobalVehicleBreaksConstraint::PropagateVehicle(
int vehicle) {
790 FillPartialPathOfVehicle(vehicle);
791 const int num_nodes = path_.size();
792 FillPathTravels(path_);
796 travel_bounds_.
pre_travels.assign(num_nodes - 1, 0);
832 task_translators_.clear();
833 for (
int i = 0; i < num_nodes; ++i) {
834 const int64_t before_visit =
836 const int64_t after_visit =
837 (i == num_nodes - 1) ? 0 : travel_bounds_.
pre_travels[i];
838 task_translators_.emplace_back(dimension_->
CumulVar(path_[i]), before_visit,
840 if (i == num_nodes - 1)
break;
841 task_translators_.emplace_back();
845 if (!
interval->MustBePerformed())
continue;
846 task_translators_.emplace_back(
interval);
850 const int num_tasks = tasks_.
start_min.size();
851 for (
int task = 0; task < num_tasks; ++task) {
852 task_translators_[task].SetStartMin(tasks_.
start_min[task]);
853 task_translators_[task].SetStartMax(tasks_.
start_max[task]);
854 task_translators_[task].SetDurationMin(tasks_.
duration_min[task]);
855 task_translators_[task].SetEndMin(tasks_.
end_min[task]);
856 task_translators_[task].SetEndMax(tasks_.
end_max[task]);
864 const int64_t last_bound_arc =
865 num_nodes - 2 - (model_->
NextVar(path_[num_nodes - 2])->
Bound() ? 0 : 1);
866 for (
int i = 0; i <= last_bound_arc; ++i) {
867 const int64_t arc_start_max =
870 const int64_t arc_end_min =
872 i < num_nodes - 2 ? travel_bounds_.
pre_travels[i + 1] : 0);
873 int64_t total_break_inside_arc = 0;
876 if (!
interval->MustBePerformed())
continue;
877 const int64_t interval_start_max =
interval->StartMax();
878 const int64_t interval_end_min =
interval->EndMin();
879 const int64_t interval_duration_min =
interval->DurationMin();
882 if (arc_start_max < interval_end_min &&
883 interval_start_max < arc_end_min) {
884 total_break_inside_arc += interval_duration_min;
893 bool has_optional =
false;
901 if (!has_optional)
return;
903 const std::vector<IntervalVar*>& break_intervals =
905 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
906 const int64_t current_slack_max = dimension_->
SlackVar(path_[pos])->
Max();
907 const int64_t visit_start_offset =
909 const int64_t visit_start_max =
911 const int64_t visit_end_offset =
912 (pos < num_nodes - 1) ? travel_bounds_.
pre_travels[pos] : 0;
913 const int64_t visit_end_min =
916 for (IntervalVar*
interval : break_intervals) {
917 if (!
interval->MayBePerformed())
continue;
918 const bool interval_is_performed =
interval->MustBePerformed();
919 const int64_t interval_start_max =
interval->StartMax();
920 const int64_t interval_end_min =
interval->EndMin();
921 const int64_t interval_duration_min =
interval->DurationMin();
924 if (pos < num_nodes - 1 && interval_duration_min > current_slack_max) {
927 const int64_t arc_start_offset =
929 const int64_t arc_start_max = visit_start_max;
930 const int64_t arc_end_offset =
931 (pos < num_nodes - 2) ? travel_bounds_.
pre_travels[pos + 1] : 0;
932 const int64_t arc_end_min =
935 if (arc_start_max < interval_end_min) {
937 if (interval_is_performed) {
938 dimension_->
CumulVar(path_[pos + 1])
943 if (interval_start_max < arc_end_min) {
945 if (interval_is_performed) {
955 if (visit_start_max < interval_end_min) {
956 interval->SetStartMin(visit_end_min);
957 if (interval_is_performed) {
963 if (interval_start_max < visit_end_min) {
964 interval->SetEndMax(visit_start_max);
965 if (interval_is_performed) {
975 class VehicleBreaksFilter :
public BasePathFilter {
977 VehicleBreaksFilter(
const RoutingModel& routing_model,
978 const RoutingDimension& dimension);
979 std::string DebugString()
const override {
return "VehicleBreaksFilter"; }
980 bool AcceptPath(int64_t path_start, int64_t chain_start,
981 int64_t chain_end)
override;
985 void FillPathOfVehicle(int64_t vehicle);
986 std::vector<int64_t> path_;
988 const RoutingModel& model_;
989 const RoutingDimension& dimension_;
991 DisjunctivePropagator disjunctive_propagator_;
992 DisjunctivePropagator::Tasks tasks_;
994 std::vector<int64_t> old_start_min_;
995 std::vector<int64_t> old_start_max_;
996 std::vector<int64_t> old_end_min_;
997 std::vector<int64_t> old_end_max_;
999 std::vector<int> start_to_vehicle_;
1000 TravelBounds travel_bounds_;
1003 VehicleBreaksFilter::VehicleBreaksFilter(
const RoutingModel& routing_model,
1004 const RoutingDimension& dimension)
1005 : BasePathFilter(routing_model.Nexts(),
1006 routing_model.Size() + routing_model.vehicles()),
1007 model_(routing_model),
1008 dimension_(dimension) {
1010 start_to_vehicle_.resize(Size(), -1);
1011 for (
int i = 0; i < routing_model.vehicles(); ++i) {
1012 start_to_vehicle_[routing_model.Start(i)] = i;
1016 void VehicleBreaksFilter::FillPathOfVehicle(int64_t vehicle) {
1018 int current = model_.
Start(vehicle);
1019 while (!model_.
IsEnd(current)) {
1020 path_.push_back(current);
1021 current = GetNext(current);
1023 path_.push_back(current);
1026 bool VehicleBreaksFilter::AcceptPath(int64_t path_start, int64_t chain_start,
1027 int64_t chain_end) {
1028 const int vehicle = start_to_vehicle_[path_start];
1034 FillPathOfVehicle(vehicle);
1039 tasks_.num_chain_tasks = tasks_.start_min.size();
1043 tasks_.forbidden_intervals.clear();
1044 if (std::any_of(path_.begin(), path_.end(), [
this](int64_t node) {
1045 return dimension_.forbidden_intervals()[node].NumIntervals() > 0;
1047 tasks_.forbidden_intervals.assign(tasks_.start_min.size(),
nullptr);
1048 for (
int i = 0; i < path_.size(); ++i) {
1049 tasks_.forbidden_intervals[2 * i] =
1054 tasks_.distance_duration =
1059 bool is_feasible =
true;
1060 int maximum_num_iterations = 8;
1061 while (--maximum_num_iterations >= 0) {
1062 old_start_min_ = tasks_.start_min;
1063 old_start_max_ = tasks_.start_max;
1064 old_end_min_ = tasks_.end_min;
1065 old_end_max_ = tasks_.end_max;
1066 is_feasible = disjunctive_propagator_.
Propagate(&tasks_);
1067 if (!is_feasible)
break;
1069 if ((old_start_min_ == tasks_.start_min) &&
1070 (old_start_max_ == tasks_.start_max) &&
1071 (old_end_min_ == tasks_.end_min) && (old_end_max_ == tasks_.end_max)) {
1083 new VehicleBreaksFilter(routing_model, dimension));
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.
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
IntVar * FixedTransitVar(int64_t index) const
RoutingModel * model() const
Returns the model on which the dimension was created.
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
IntVar * SlackVar(int64_t index) const
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
const std::vector< std::pair< int64_t, int64_t > > & GetBreakDistanceDurationOfVehicle(int vehicle) const
Returns the pairs (distance, duration) specified by break distance constraints.
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
const std::vector< SortedDisjointIntervalList > & forbidden_intervals() const
Returns forbidden intervals for each node.
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
Solver * solver() const
Returns the underlying constraint solver.
int64_t Start(int vehicle) const
Model inspection.
int vehicles() const
Returns the number of vehicle routes in the model.
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...
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
const TransitCallback2 & TransitCallback(int callback_index) const
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
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
IntegerType GetOptionalEnvelope() const
void RemoveEvent(int event)
void Reset(int num_events)
void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
IntegerType GetEnvelope() const
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)
void FillTravelBoundsOfVehicle(int vehicle, const std::vector< int64_t > &path, const RoutingDimension &dimension, TravelBounds *travel_bounds)
A structure to hold tasks described by their features.
std::vector< std::pair< int64_t, int64_t > > distance_duration
std::vector< int64_t > end_min
std::vector< int64_t > start_min
std::vector< const SortedDisjointIntervalList * > forbidden_intervals
std::vector< bool > is_preemptible
std::vector< int64_t > end_max
std::vector< int64_t > duration_max
std::vector< int64_t > start_max
std::vector< int64_t > duration_min
std::vector< int64_t > post_travels
std::vector< int64_t > max_travels
std::vector< int64_t > pre_travels
std::vector< int64_t > min_travels