From 890f5e876a337ffe4b97b2d7363ecbf4ecd38fce Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 4 Aug 2023 09:38:26 +0200 Subject: [PATCH 1/4] Reserve vector capacity in initial_routes. --- src/algorithms/heuristics/heuristics.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/algorithms/heuristics/heuristics.cpp b/src/algorithms/heuristics/heuristics.cpp index 4e2ce303e..b5bd52d27 100644 --- a/src/algorithms/heuristics/heuristics.cpp +++ b/src/algorithms/heuristics/heuristics.cpp @@ -886,6 +886,7 @@ T dynamic_vehicle_choice(const Input& input, template T initial_routes(const Input& input) { T routes; + routes.reserve(input.vehicles.size()); for (Index v = 0; v < input.vehicles.size(); ++v) { routes.emplace_back(input, v, input.zero_amount().size()); @@ -912,6 +913,7 @@ template T initial_routes(const Input& input) { Amount current_load = single_jobs_deliveries; std::vector job_ranks; + job_ranks.reserve(vehicle.steps.size()); std::unordered_set expected_delivery_ranks; for (const auto& step : vehicle.steps) { if (step.type != STEP_TYPE::JOB) { From 1a7120dad030390e1aac30a89d99e328b5e906fb Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 4 Aug 2023 10:29:45 +0200 Subject: [PATCH 2/4] Add missing max_travel_time check in initial_routes, fixes #954. --- CHANGELOG.md | 1 + src/algorithms/heuristics/heuristics.cpp | 29 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28d3dcc6e..c510ac3b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - Comparison of index-based and coordinates-based locations (#935) - `max_travel_time` parameter not taken into account in edge case (#884) - Meaningless `location_index` provided in output for break steps (#877) +- `max_travel_time` not accounted for with vehicle steps in solving mode (#954) ## [v1.13.0] - 2023-01-31 diff --git a/src/algorithms/heuristics/heuristics.cpp b/src/algorithms/heuristics/heuristics.cpp index b5bd52d27..54b1183d6 100644 --- a/src/algorithms/heuristics/heuristics.cpp +++ b/src/algorithms/heuristics/heuristics.cpp @@ -909,8 +909,13 @@ template T initial_routes(const Input& input) { std::to_string(vehicle.id) + "."); } - // Startup load is the sum of deliveries for (single) jobs. + // Track load and travel time during the route for validity. Amount current_load = single_jobs_deliveries; + Eval eval_sum; + std::optional previous_index; + if (vehicle.has_start()) { + previous_index = vehicle.start.value().index(); + } std::vector job_ranks; job_ranks.reserve(vehicle.steps.size()); @@ -930,6 +935,13 @@ template T initial_routes(const Input& input) { std::to_string(job.id) + "."); } + // Update current travel time. + if (previous_index.has_value()) { + eval_sum += vehicle.eval(previous_index.value(), job.index()); + } + previous_index = job.index(); + + // Handle load. assert(step.job_type.has_value()); switch (step.job_type.value()) { case JOB_TYPE::SINGLE: { @@ -963,6 +975,17 @@ template T initial_routes(const Input& input) { } } + if (vehicle.has_end() and !job_ranks.empty()) { + // Update with last route leg. + assert(previous_index.has_value()); + eval_sum += + vehicle.eval(previous_index.value(), vehicle.end.value().index()); + } + if (!vehicle.ok_for_travel_time(eval_sum.duration)) { + throw InputException("Route over max_travel_time for vehicle " + + std::to_string(vehicle.id) + "."); + } + if (vehicle.max_tasks < job_ranks.size()) { throw InputException("Too many tasks for vehicle " + std::to_string(vehicle.id) + "."); @@ -973,8 +996,8 @@ template T initial_routes(const Input& input) { std::to_string(vehicle.id) + "."); } - // Now route is OK with regard to capacity, precedence and skills - // constraints. + // Now route is OK with regard to capacity, max_travel_time, + // max_tasks, precedence and skills constraints. if (!job_ranks.empty()) { if (!current_r.is_valid_addition_for_tw(input, single_jobs_deliveries, From 22572a801f7ec39d91b9572019f08c28d3e6474f Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 4 Aug 2023 11:26:35 +0200 Subject: [PATCH 3/4] Add max_travel_time related checks in RouteSplit. --- CHANGELOG.md | 1 + src/algorithms/local_search/route_split_utils.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c510ac3b0..8a5128eab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - `max_travel_time` parameter not taken into account in edge case (#884) - Meaningless `location_index` provided in output for break steps (#877) - `max_travel_time` not accounted for with vehicle steps in solving mode (#954) +- `max_travel_time` not accounted for in `RouteSplit` (#941) ## [v1.13.0] - 2023-01-31 diff --git a/src/algorithms/local_search/route_split_utils.h b/src/algorithms/local_search/route_split_utils.h index fac86dc14..862e0a21e 100644 --- a/src/algorithms/local_search/route_split_utils.h +++ b/src/algorithms/local_search/route_split_utils.h @@ -78,6 +78,10 @@ compute_best_route_split_choice(const Input& input, source.route.begin() + r, source.route.end()); + if (!end_v.ok_for_travel_time(current_end_eval.duration)) { + continue; + } + if (current_end_eval < second_best_end_eval) { // Worth checking end route full validity. @@ -139,6 +143,10 @@ compute_best_route_split_choice(const Input& input, source.route.begin(), source.route.begin() + r); + if (!begin_v.ok_for_travel_time(current_begin_eval.duration)) { + continue; + } + if (current_begin_eval < second_best_begin_eval) { // Worth checking begin route full validity. From 966a03f41629bfaa1c57ed1b951ca66ba42413b4 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 4 Aug 2023 14:11:25 +0200 Subject: [PATCH 4/4] Evaluate route portion in constant time in compute_best_route_split_choice, fixes #962. --- CHANGELOG.md | 1 + .../local_search/route_split_utils.h | 33 +++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5128eab..2faf6c002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Switch to C++20 (#851) - Improved error messages for file-related IO errors (#553) - Add job id to error message for unreachable step (#946) +- Reduce `compute_best_route_split_choice` complexity (#962) ### Fixed diff --git a/src/algorithms/local_search/route_split_utils.h b/src/algorithms/local_search/route_split_utils.h index 862e0a21e..1cfdb493f 100644 --- a/src/algorithms/local_search/route_split_utils.h +++ b/src/algorithms/local_search/route_split_utils.h @@ -72,11 +72,17 @@ compute_best_route_split_choice(const Input& input, continue; } - Eval current_end_eval = - utils::route_eval_for_vehicle(input, - v, - source.route.begin() + r, - source.route.end()); + Eval current_end_eval(end_v.fixed_cost(), 0); + current_end_eval += sol_state.fwd_costs[s_vehicle][v].back() - + sol_state.fwd_costs[s_vehicle][v][r]; + if (end_v.has_start()) { + current_end_eval += end_v.eval(end_v.start.value().index(), + input.jobs[source.route[r]].index()); + } + if (end_v.has_end()) { + current_end_eval += end_v.eval(input.jobs[source.route.back()].index(), + end_v.end.value().index()); + } if (!end_v.ok_for_travel_time(current_end_eval.duration)) { continue; @@ -137,11 +143,18 @@ compute_best_route_split_choice(const Input& input, continue; } - Eval current_begin_eval = - utils::route_eval_for_vehicle(input, - v, - source.route.begin(), - source.route.begin() + r); + Eval current_begin_eval(begin_v.fixed_cost(), 0); + current_begin_eval += sol_state.fwd_costs[s_vehicle][v][r - 1]; + if (begin_v.has_start()) { + current_begin_eval += + begin_v.eval(begin_v.start.value().index(), + input.jobs[source.route.front()].index()); + } + if (begin_v.has_end()) { + current_begin_eval += + begin_v.eval(input.jobs[source.route[r - 1]].index(), + begin_v.end.value().index()); + } if (!begin_v.ok_for_travel_time(current_begin_eval.duration)) { continue;