Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 0 additions & 159 deletions src/algorithms/heuristics/heuristics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,157 +711,6 @@ Eval dynamic_vehicle_choice(const Input& input,
return sol_eval;
}

template <class Route>
void set_route(const Input& input,
Route& route,
std::unordered_set<Index>& assigned) {
assert(route.empty());
const auto& vehicle = input.vehicles[route.v_rank];

// Startup load is the sum of deliveries for (single) jobs.
Amount single_jobs_deliveries(input.zero_amount());
for (const auto& step : vehicle.steps) {
if (step.type == STEP_TYPE::JOB) {
assert(step.job_type.has_value());

if (step.job_type.value() == JOB_TYPE::SINGLE) {
single_jobs_deliveries += input.jobs[step.rank].delivery;
}
}
}
if (!(single_jobs_deliveries <= vehicle.capacity)) {
throw InputException(
std::format("Route over capacity for vehicle {}.", vehicle.id));
}

// Track load and travel time during the route for validity.
Amount current_load = single_jobs_deliveries;
Eval eval_sum;
std::optional<Index> previous_index;
if (vehicle.has_start()) {
previous_index = vehicle.start.value().index();
}

std::vector<Index> job_ranks;
job_ranks.reserve(vehicle.steps.size());
std::unordered_set<Index> expected_delivery_ranks;
for (const auto& step : vehicle.steps) {
if (step.type != STEP_TYPE::JOB) {
continue;
}

const auto job_rank = step.rank;
const auto& job = input.jobs[job_rank];
job_ranks.push_back(job_rank);

assert(!assigned.contains(job_rank));
assigned.insert(job_rank);

if (!input.vehicle_ok_with_job(route.v_rank, job_rank)) {
throw InputException(
std::format("Missing skill or step out of reach for vehicle {} and "
"job {}.",
vehicle.id,
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: {
current_load += job.pickup;
current_load -= job.delivery;
break;
}
case JOB_TYPE::PICKUP: {
expected_delivery_ranks.insert(job_rank + 1);

current_load += job.pickup;
break;
}
case JOB_TYPE::DELIVERY: {
auto search = expected_delivery_ranks.find(job_rank);
if (search == expected_delivery_ranks.end()) {
throw InputException(
std::format("Invalid shipment in route for vehicle {}.", vehicle.id));
}
expected_delivery_ranks.erase(search);

current_load -= job.delivery;
break;
}
default:
assert(false);
}

// Check validity after this step wrt capacity.
if (!(current_load <= vehicle.capacity)) {
throw InputException(
std::format("Route over capacity for vehicle {}.", vehicle.id));
}
}

if (vehicle.has_end() && !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(
std::format("Route over max_travel_time for vehicle {}.", vehicle.id));
}
if (!vehicle.ok_for_distance(eval_sum.distance)) {
throw InputException(
std::format("Route over max_distance for vehicle {}.", vehicle.id));
}

if (vehicle.max_tasks < job_ranks.size()) {
throw InputException(
std::format("Too many tasks for vehicle {}.", vehicle.id));
}

if (!expected_delivery_ranks.empty()) {
throw InputException(
std::format("Invalid shipment in route for vehicle {}.", vehicle.id));
}

// Now route is OK with regard to capacity, max_travel_time,
// max_tasks, precedence and skills constraints.
if (!job_ranks.empty()) {
if (!route.is_valid_addition_for_tw(input,
single_jobs_deliveries,
job_ranks.begin(),
job_ranks.end(),
0,
0)) {
throw InputException(
std::format("Infeasible route for vehicle {}.", vehicle.id));
}

route.replace(input,
single_jobs_deliveries,
job_ranks.begin(),
job_ranks.end(),
0,
0);
}
}

template <class Route>
void set_initial_routes(const Input& input,
std::vector<Route>& routes,
std::unordered_set<Index>& assigned) {
std::ranges::for_each(routes,
[&](auto& r) { set_route(input, r, assigned); });
}

using RawSolution = std::vector<RawRoute>;
using TWSolution = std::vector<TWRoute>;

Expand All @@ -881,10 +730,6 @@ template Eval dynamic_vehicle_choice(const Input& input,
double lambda,
SORT sort);

template void set_initial_routes(const Input& input,
RawSolution& routes,
std::unordered_set<Index>& assigned);

template Eval basic(const Input& input,
TWSolution& routes,
std::set<Index> unassigned,
Expand All @@ -901,8 +746,4 @@ template Eval dynamic_vehicle_choice(const Input& input,
double lambda,
SORT sort);

template void set_initial_routes(const Input& input,
TWSolution& routes,
std::unordered_set<Index>& assigned);

} // namespace vroom::heuristics
2 changes: 1 addition & 1 deletion src/algorithms/local_search/route_split_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ compute_best_route_split_choice(const Input& input,
std::vector<Route> empty_routes;
empty_routes.reserve(empty_route_ranks.size());
for (auto v : empty_route_ranks) {
empty_routes.emplace_back(input, v, input.zero_amount().size());
empty_routes.emplace_back(input, v);
}

for (Index r = 1; r < source.size(); ++r) {
Expand Down
2 changes: 1 addition & 1 deletion src/problems/cvrp/cvrp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Solution CVRP::solve(const unsigned nb_searches,

const TSP p(_input, std::move(job_ranks), 0);

RawRoute r(_input, 0, 0);
RawRoute r(_input, 0);
r.set_route(_input, p.raw_solve(nb_threads, timeout));

return utils::format_solution(_input, {r});
Expand Down
2 changes: 1 addition & 1 deletion src/problems/cvrp/operators/tsp_fix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ bool TSPFix::is_valid() {
bool valid = is_valid_for_source_range_bounds();

if (valid) {
const RawRoute route(_input, s_vehicle, _input.zero_amount().size());
const RawRoute route(_input, s_vehicle);

valid = route.is_valid_addition_for_capacity_inclusion(_input,
_s_delivery,
Expand Down
2 changes: 1 addition & 1 deletion src/problems/tsp/tsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Solution TSP::solve(unsigned,
unsigned,
unsigned nb_threads,
const Timeout& timeout) const {
RawRoute r(_input, 0, 0);
RawRoute r(_input, 0);
r.set_route(_input, raw_solve(nb_threads, timeout));
return utils::format_solution(_input, {r});
}
Expand Down
21 changes: 10 additions & 11 deletions src/problems/vrp.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,18 @@ All rights reserved (see LICENSE).

namespace vroom {

template <class Route>
std::vector<Route> set_init_sol(const Input& input,
std::unordered_set<Index>& init_assigned) {
template <class Route> std::vector<Route> set_init_sol(const Input& input) {
std::vector<Route> init_sol;
init_sol.reserve(input.vehicles.size());

for (Index v = 0; v < input.vehicles.size(); ++v) {
init_sol.emplace_back(input, v, input.zero_amount().size());
}

if (input.has_initial_routes()) {
heuristics::set_initial_routes<Route>(input, init_sol, init_assigned);
init_sol.emplace_back(input, v).populate_from_steps(input);
}

return init_sol;
}

template <class Route> struct SolvingContext {
std::unordered_set<Index> init_assigned;
const std::vector<Route> init_sol;
std::set<Index> unassigned;
std::vector<Index> vehicles_ranks;
Expand All @@ -55,15 +48,21 @@ template <class Route> struct SolvingContext {
std::mutex heuristic_indicators_m;

SolvingContext(const Input& input, unsigned nb_searches)
: init_sol(set_init_sol<Route>(input, init_assigned)),
: init_sol(set_init_sol<Route>(input)),
vehicles_ranks(input.vehicles.size()),
solutions(nb_searches, init_sol),
sol_indicators(nb_searches) {

// Deduce unassigned jobs from initial solution.
std::unordered_set<Index> init_assigned;
for (const auto& r : init_sol) {
for (const Index i : r.route) {
init_assigned.insert(i);
}
}
std::ranges::copy_if(std::views::iota(0u, input.jobs.size()),
std::inserter(unassigned, unassigned.begin()),
[this](const Index j) {
[&](const Index j) {
return !init_assigned.contains(j);
});

Expand Down
6 changes: 1 addition & 5 deletions src/structures/vroom/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,6 @@ bool Input::has_homogeneous_costs() const {
return _homogeneous_costs;
}

bool Input::has_initial_routes() const {
return _has_initial_routes;
}

bool Input::vehicle_ok_with_vehicle(Index v1_index, Index v2_index) const {
return _vehicle_to_vehicle_compatibility[v1_index][v2_index];
}
Expand Down Expand Up @@ -551,7 +547,7 @@ void Input::set_extra_compatibility() {
compatible_vehicles_for_job = std::vector<std::vector<Index>>(jobs.size());

for (std::size_t v = 0; v < vehicles.size(); ++v) {
const TWRoute empty_route(*this, v, _zero.size());
const TWRoute empty_route(*this, v);
for (Index j = 0; j < jobs.size(); ++j) {
if (!_vehicle_to_job_compatibility[v][j]) {
continue;
Expand Down
2 changes: 0 additions & 2 deletions src/structures/vroom/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,6 @@ class Input {

bool has_homogeneous_costs() const;

bool has_initial_routes() const;

bool vehicle_ok_with_job(size_t v_index, size_t j_index) const {
return static_cast<bool>(_vehicle_to_job_compatibility[v_index][j_index]);
}
Expand Down
Loading
Loading