Skip to content

Commit

Permalink
Merge branch 'refactor/cost-wrapper'
Browse files Browse the repository at this point in the history
  • Loading branch information
jcoupey committed Nov 23, 2022
2 parents e11cd85 + 0aeb03c commit a4955ba
Show file tree
Hide file tree
Showing 63 changed files with 700 additions and 461 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- Refactor `TSP` cost functions (#812)
- CI builds now use clang++ 14 and g++ 11 on Ubuntu 22.04 (#816)
- Refactor `CVRP::solve` and `VRPTW::solve` functions (#818)
- Refactor `CostWrapper` (#828)

### Fixed

Expand Down
6 changes: 3 additions & 3 deletions libvroom_examples/libvroom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ void run_example_with_osrm() {
vroom::Amount job_empty_pickup(amount_dimension);
job_pickup[0] = 1;

vroom::Duration setup = 0;
vroom::Duration service = 5 * 60; // 5 minutes
vroom::UserDuration setup = 0;
vroom::UserDuration service = 5 * 60; // 5 minutes
vehicle_capacity[0] = 4;

// Define vehicle breaks.
Expand Down Expand Up @@ -225,7 +225,7 @@ void run_example_with_custom_matrix() {
vroom::Input problem_instance;

// Define custom matrix and bypass OSRM call.
vroom::Matrix<vroom::Duration> matrix_input(4);
vroom::Matrix<vroom::UserDuration> matrix_input(4);

matrix_input[0][0] = 0;
matrix_input[0][1] = 2104;
Expand Down
37 changes: 17 additions & 20 deletions src/algorithms/heuristics/heuristics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,8 @@ template <class T> T basic(const Input& input, INIT init, double lambda) {
// regrets[v][j] holds the min cost for reaching job j in an empty
// route across all remaining vehicles **after** vehicle at rank v
// in vehicle_ranks.
std::vector<std::vector<SignedCost>> regrets(nb_vehicles,
std::vector<SignedCost>(
input.jobs.size()));
std::vector<std::vector<Cost>> regrets(nb_vehicles,
std::vector<Cost>(input.jobs.size()));

// Use own cost for last vehicle regret values.
auto& last_regrets = regrets.back();
Expand Down Expand Up @@ -168,7 +167,7 @@ template <class T> T basic(const Input& input, INIT init, double lambda) {
}

bool is_valid =
(evals[job_rank][v_rank].duration <= vehicle.max_travel_time) &&
(vehicle.ok_for_travel_time(evals[job_rank][v_rank].duration)) &&
current_r
.is_valid_addition_for_capacity(input,
input.jobs[job_rank].pickup,
Expand Down Expand Up @@ -267,8 +266,8 @@ template <class T> T basic(const Input& input, INIT init, double lambda) {
lambda * static_cast<double>(regrets[v][job_rank]);

if (current_cost < best_cost and
(current_route_duration + current_add.duration <=
vehicle.max_travel_time) and
(vehicle.ok_for_travel_time(current_route_duration +
current_add.duration)) and
current_r
.is_valid_addition_for_capacity(input,
input.jobs[job_rank].pickup,
Expand Down Expand Up @@ -360,8 +359,8 @@ template <class T> T basic(const Input& input, INIT init, double lambda) {

// Update best cost depending on validity.
bool valid =
(current_route_duration + current_add.duration <=
vehicle.max_travel_time) &&
(vehicle.ok_for_travel_time(current_route_duration +
current_add.duration)) &&
current_r
.is_valid_addition_for_capacity_inclusion(input,
modified_delivery,
Expand Down Expand Up @@ -449,11 +448,10 @@ T dynamic_vehicle_choice(const Input& input, INIT init, double lambda) {
// (resp. jobs_second_min_costs[j]) holds the min cost
// (resp. second min cost) of picking the job in an empty route
// for any remaining vehicle.
std::vector<SignedCost>
jobs_min_costs(input.jobs.size(), std::numeric_limits<SignedCost>::max());
std::vector<SignedCost>
jobs_second_min_costs(input.jobs.size(),
std::numeric_limits<Cost>::max());
std::vector<Cost> jobs_min_costs(input.jobs.size(),
std::numeric_limits<Cost>::max());
std::vector<Cost> jobs_second_min_costs(input.jobs.size(),
std::numeric_limits<Cost>::max());
for (const auto job_rank : unassigned) {
for (const auto v_rank : vehicles_ranks) {
if (evals[job_rank][v_rank].cost <= jobs_min_costs[job_rank]) {
Expand Down Expand Up @@ -498,8 +496,7 @@ T dynamic_vehicle_choice(const Input& input, INIT init, double lambda) {
// Once current vehicle is decided, regrets[j] holds the min cost
// of picking the job in an empty route for other remaining
// vehicles.
std::vector<SignedCost> regrets(input.jobs.size(),
input.get_cost_upper_bound());
std::vector<Cost> regrets(input.jobs.size(), input.get_cost_upper_bound());
for (const auto job_rank : unassigned) {
if (jobs_min_costs[job_rank] < evals[job_rank][v_rank].cost) {
regrets[job_rank] = jobs_min_costs[job_rank];
Expand Down Expand Up @@ -562,7 +559,7 @@ T dynamic_vehicle_choice(const Input& input, INIT init, double lambda) {
}

bool is_valid =
(evals[job_rank][v_rank].duration <= vehicle.max_travel_time) &&
(vehicle.ok_for_travel_time(evals[job_rank][v_rank].duration)) &&
current_r
.is_valid_addition_for_capacity(input,
input.jobs[job_rank].pickup,
Expand Down Expand Up @@ -662,8 +659,8 @@ T dynamic_vehicle_choice(const Input& input, INIT init, double lambda) {
lambda * static_cast<double>(regrets[job_rank]);

if (current_cost < best_cost and
(current_route_duration + current_add.duration <=
vehicle.max_travel_time) and
(vehicle.ok_for_travel_time(current_route_duration +
current_add.duration)) and
current_r
.is_valid_addition_for_capacity(input,
input.jobs[job_rank].pickup,
Expand Down Expand Up @@ -755,8 +752,8 @@ T dynamic_vehicle_choice(const Input& input, INIT init, double lambda) {

// Update best cost depending on validity.
bool valid =
(current_route_duration + current_add.duration <=
vehicle.max_travel_time) &&
(vehicle.ok_for_travel_time(current_route_duration +
current_add.duration)) &&
current_r
.is_valid_addition_for_capacity_inclusion(input,
modified_delivery,
Expand Down
4 changes: 2 additions & 2 deletions src/algorithms/kruskal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ UndirectedGraph<T> minimum_spanning_tree(const UndirectedGraph<T>& graph) {
return UndirectedGraph<T>(mst);
}

template UndirectedGraph<Cost>
minimum_spanning_tree(const UndirectedGraph<Cost>& graph);
template UndirectedGraph<UserCost>
minimum_spanning_tree(const UndirectedGraph<UserCost>& graph);

} // namespace utils
} // namespace vroom
6 changes: 3 additions & 3 deletions src/algorithms/local_search/insertion_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ compute_best_insertion_single(const Input& input,
Eval current_eval =
utils::addition_cost(input, j, v_target, route.route, rank);
if (current_eval.cost < result.eval.cost &&
(sol_state.route_evals[v].duration + current_eval.duration <=
v_target.max_travel_time) &&
v_target.ok_for_travel_time(sol_state.route_evals[v].duration +
current_eval.duration) &&
route.is_valid_addition_for_capacity(input,
current_job.pickup,
current_job.delivery,
Expand Down Expand Up @@ -161,7 +161,7 @@ RouteInsertion compute_best_insertion_pd(const Input& input,
}

if (pd_eval < result.eval &&
target_travel_time + pd_eval.duration <= v_target.max_travel_time) {
v_target.ok_for_travel_time(target_travel_time + pd_eval.duration)) {
modified_with_pd.push_back(j + 1);

// Update best cost depending on validity.
Expand Down
30 changes: 15 additions & 15 deletions src/algorithms/local_search/local_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ RouteInsertion compute_best_insertion(const Input& input,
if (insert.eval != NO_EVAL) {
// Normalize cost per job for consistency with single jobs.
insert.eval.cost =
static_cast<SignedCost>(static_cast<double>(insert.eval.cost) / 2);
static_cast<Cost>(static_cast<double>(insert.eval.cost) / 2);
}
return insert;
}
Expand Down Expand Up @@ -191,9 +191,8 @@ void LocalSearch<Route,
continue;
}

// Implicit cast to signed value.
SignedCost smallest = _input.get_cost_upper_bound();
SignedCost second_smallest = _input.get_cost_upper_bound();
auto smallest = _input.get_cost_upper_bound();
auto second_smallest = _input.get_cost_upper_bound();
std::size_t smallest_idx = std::numeric_limits<std::size_t>::max();

for (std::size_t i = 0; i < routes.size(); ++i) {
Expand Down Expand Up @@ -1500,7 +1499,8 @@ void LocalSearch<Route,
const auto s_travel_time = _sol_state.route_evals[s_t.first].duration;
const auto s_removal_duration_gain =
_sol_state.pd_gains[s_t.first][s_p_rank].duration;
if (s_travel_time > v_s.max_travel_time + s_removal_duration_gain) {
if (!v_s.ok_for_travel_time(s_travel_time -
s_removal_duration_gain)) {
// Removing shipment from source route actually breaks
// max_travel_time constraint in source.
continue;
Expand Down Expand Up @@ -1663,8 +1663,8 @@ void LocalSearch<Route,
_sol_state.update_route_eval(_sol[v_rank].route, v_rank);

assert(_sol[v_rank].size() <= _input.vehicles[v_rank].max_tasks);
assert(_sol_state.route_evals[v_rank].duration <=
_input.vehicles[v_rank].max_travel_time);
assert(_input.vehicles[v_rank].ok_for_travel_time(
_sol_state.route_evals[v_rank].duration));
}

#ifndef NDEBUG
Expand Down Expand Up @@ -1927,7 +1927,7 @@ Eval LocalSearch<Route,
Index r) {
assert(v != v_target);

Eval eval(INFINITE_COST, 0);
Eval eval = NO_EVAL;
const auto job_index = _input.jobs[_sol[v].route[r]].index();

const auto& vehicle = _input.vehicles[v_target];
Expand Down Expand Up @@ -1994,7 +1994,7 @@ Eval LocalSearch<Route,
IntraTwoOpt,
PDShift,
RouteExchange>::relocate_cost_lower_bound(Index v, Index r) {
Eval best_bound(INFINITE_COST, 0);
Eval best_bound = NO_EVAL;

for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) {
if (other_v == v or
Expand Down Expand Up @@ -2044,7 +2044,7 @@ Eval LocalSearch<Route,
RouteExchange>::relocate_cost_lower_bound(Index v,
Index r1,
Index r2) {
Eval best_bound(INFINITE_COST, 0);
Eval best_bound = NO_EVAL;

for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) {
if (other_v == v or
Expand Down Expand Up @@ -2121,7 +2121,6 @@ void LocalSearch<Route,
Index best_rank = 0;
Eval best_gain = NO_GAIN;

const auto max_travel_time = _input.vehicles[v].max_travel_time;
const auto current_travel_time = _sol_state.route_evals[v].duration;

for (std::size_t r = 0; r < _sol[v].size(); ++r) {
Expand All @@ -2139,9 +2138,9 @@ void LocalSearch<Route,

if (current_gain > best_gain) {
// Only check validity if required.
valid_removal =
(current_travel_time <= max_travel_time + removal_gain.duration) &&
_sol[v].is_valid_removal(_input, r, 1);
valid_removal = _input.vehicles[v].ok_for_travel_time(
current_travel_time - removal_gain.duration) &&
_sol[v].is_valid_removal(_input, r, 1);
}
} else {
assert(current_job.type == JOB_TYPE::PICKUP);
Expand All @@ -2151,7 +2150,8 @@ void LocalSearch<Route,
removal_gain - relocate_cost_lower_bound(v, r, delivery_r);

if (current_gain > best_gain &&
(current_travel_time <= max_travel_time + removal_gain.duration)) {
_input.vehicles[v].ok_for_travel_time(current_travel_time -
removal_gain.duration)) {
// Only check validity if required.
if (delivery_r == r + 1) {
valid_removal = _sol[v].is_valid_removal(_input, r, 2);
Expand Down
12 changes: 6 additions & 6 deletions src/algorithms/local_search/operator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@ Eval Operator::gain() {

bool Operator::is_valid_for_source_max_travel_time() const {
const auto& s_v = _input.vehicles[s_vehicle];
const auto s_travel_time = _sol_state.route_evals[s_vehicle].duration;
return s_travel_time <= s_v.max_travel_time + s_gain.duration;
return s_v.ok_for_travel_time(_sol_state.route_evals[s_vehicle].duration -
s_gain.duration);
}

bool Operator::is_valid_for_target_max_travel_time() const {
const auto& t_v = _input.vehicles[t_vehicle];
const auto t_travel_time = _sol_state.route_evals[t_vehicle].duration;
return t_travel_time <= t_v.max_travel_time + t_gain.duration;
return t_v.ok_for_travel_time(_sol_state.route_evals[t_vehicle].duration -
t_gain.duration);
}

bool Operator::is_valid_for_max_travel_time() const {
assert(s_vehicle == t_vehicle);
assert(gain_computed);

const auto& s_v = _input.vehicles[s_vehicle];
const auto s_travel_time = _sol_state.route_evals[s_vehicle].duration;
return s_travel_time <= s_v.max_travel_time + stored_gain.duration;
return s_v.ok_for_travel_time(_sol_state.route_evals[s_vehicle].duration -
stored_gain.duration);
}

std::vector<Index> Operator::required_unassigned() const {
Expand Down
13 changes: 7 additions & 6 deletions src/algorithms/local_search/swap_star_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,11 @@ SwapChoice compute_best_swap_star_choice(const Input& input,

Eval current_gain = in_place_s_gain + in_place_t_gain;

if (s_travel_time <= s_v.max_travel_time + in_place_s_gain.duration) {
if (s_v.ok_for_travel_time(s_travel_time - in_place_s_gain.duration)) {
// Only bother further checking in-place insertion in source
// route if max travel time constraint is OK.
if (current_gain > best_gain and
t_travel_time <= t_v.max_travel_time + in_place_t_gain.duration) {
t_v.ok_for_travel_time(t_travel_time - in_place_t_gain.duration)) {
SwapChoice sc({current_gain, s_rank, t_rank, s_rank, t_rank});
if (valid_choice_for_insertion_ranks(sol_state,
s_vehicle,
Expand All @@ -309,7 +309,7 @@ SwapChoice compute_best_swap_star_choice(const Input& input,
const Eval t_gain = target_delta - ti.cost;
current_gain = in_place_s_gain + t_gain;
if (current_gain > best_gain and
t_travel_time <= t_v.max_travel_time + t_gain.duration) {
t_v.ok_for_travel_time(t_travel_time - t_gain.duration)) {
SwapChoice sc({current_gain, s_rank, t_rank, s_rank, ti.rank});
if (valid_choice_for_insertion_ranks(sol_state,
s_vehicle,
Expand All @@ -333,15 +333,16 @@ SwapChoice compute_best_swap_star_choice(const Input& input,
(si.cost != NO_EVAL)) {
const Eval s_gain = source_delta - si.cost;

if (s_travel_time > s_v.max_travel_time + s_gain.duration) {
if (!s_v.ok_for_travel_time(s_travel_time - s_gain.duration)) {
// Don't bother further checking if max travel time
// constraint is violated for source route.
continue;
}

current_gain = s_gain + in_place_t_gain;
if (current_gain > best_gain and
t_travel_time <= t_v.max_travel_time + in_place_t_gain.duration) {
t_v.ok_for_travel_time(t_travel_time -
in_place_t_gain.duration)) {
SwapChoice sc({current_gain, s_rank, t_rank, si.rank, t_rank});
if (valid_choice_for_insertion_ranks(sol_state,
s_vehicle,
Expand All @@ -359,7 +360,7 @@ SwapChoice compute_best_swap_star_choice(const Input& input,
const Eval t_gain = target_delta - ti.cost;
current_gain = s_gain + t_gain;
if (current_gain > best_gain and
t_travel_time <= t_v.max_travel_time + t_gain.duration) {
t_v.ok_for_travel_time(t_travel_time - t_gain.duration)) {
SwapChoice sc({current_gain, s_rank, t_rank, si.rank, ti.rank});
if (valid_choice_for_insertion_ranks(sol_state,
s_vehicle,
Expand Down
4 changes: 2 additions & 2 deletions src/algorithms/munkres.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,10 @@ greedy_symmetric_approx_mwpm(const Matrix<T>& m) {
}

template std::unordered_map<Index, Index>
minimum_weight_perfect_matching(const Matrix<Cost>& m);
minimum_weight_perfect_matching(const Matrix<UserCost>& m);

template std::unordered_map<Index, Index>
greedy_symmetric_approx_mwpm(const Matrix<Cost>& m);
greedy_symmetric_approx_mwpm(const Matrix<UserCost>& m);

} // namespace utils
} // namespace vroom
Loading

0 comments on commit a4955ba

Please sign in to comment.