Skip to content

Commit

Permalink
Merge branch 'enhancement/heuristic-regrets'
Browse files Browse the repository at this point in the history
  • Loading branch information
jcoupey committed Nov 25, 2024
2 parents 31bace4 + 2b3074f commit 9d58251
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#### Core solving

- Solution quality regression when using lots of skills (#1193)
- Crash due to wrong delivery values in some validity checks (#1164)
- Crash when input is valid JSON but not an object (#1172)
- Capacity array check consistency (#1086)
Expand Down
61 changes: 46 additions & 15 deletions src/algorithms/heuristics/heuristics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,26 @@ Eval basic(const Input& input,
for (Index rev_v = 0; rev_v < nb_vehicles - 1; ++rev_v) {
// Going trough vehicles backward from second to last.
const auto v = nb_vehicles - 2 - rev_v;

bool all_compatible_jobs_later_undoable = true;
for (const auto j : unassigned) {
regrets[v][j] =
std::min(regrets[v + 1][j], (evals[j][vehicles_ranks[v + 1]]).cost);
if (input.vehicle_ok_with_job(vehicles_ranks[v], j) &&
regrets[v][j] < input.get_cost_upper_bound()) {
all_compatible_jobs_later_undoable = false;
}
}

if (all_compatible_jobs_later_undoable) {
// We don't want to use all regrets equal to the cost upper
// bound in this situation: it would defeat the purpose of using
// regrets in the first place as all lambda values would yield
// the same choices. Using the same approach as with last
// vehicle.
for (const auto j : unassigned) {
regrets[v][j] = evals[j][vehicles_ranks[v]].cost;
}
}
}

Expand Down Expand Up @@ -449,14 +466,14 @@ Eval dynamic_vehicle_choice(const Input& input,
input.get_cost_upper_bound());
std::vector<Cost> jobs_second_min_costs(input.jobs.size(),
input.get_cost_upper_bound());
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]) {
jobs_second_min_costs[job_rank] = jobs_min_costs[job_rank];
jobs_min_costs[job_rank] = evals[job_rank][v_rank].cost;
for (const auto j : unassigned) {
for (const auto v : vehicles_ranks) {
if (evals[j][v].cost <= jobs_min_costs[j]) {
jobs_second_min_costs[j] = jobs_min_costs[j];
jobs_min_costs[j] = evals[j][v].cost;
} else {
if (evals[job_rank][v_rank].cost < jobs_second_min_costs[job_rank]) {
jobs_second_min_costs[job_rank] = evals[job_rank][v_rank].cost;
if (evals[j][v].cost < jobs_second_min_costs[j]) {
jobs_second_min_costs[j] = evals[j][v].cost;
}
}
}
Expand All @@ -466,10 +483,10 @@ Eval dynamic_vehicle_choice(const Input& input,
// unassigned jobs closest to him than to any other different
// vehicle still available.
std::vector<unsigned> closest_jobs_count(input.vehicles.size(), 0);
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]) {
++closest_jobs_count[v_rank];
for (const auto j : unassigned) {
for (const auto v : vehicles_ranks) {
if (evals[j][v].cost == jobs_min_costs[j]) {
++closest_jobs_count[v];
}
}
}
Expand Down Expand Up @@ -516,11 +533,25 @@ Eval dynamic_vehicle_choice(const Input& input,
// empty routes evaluations so do not account for initial routes
// if any.
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];

bool all_compatible_jobs_later_undoable = true;
for (const auto j : unassigned) {
if (jobs_min_costs[j] < evals[j][v_rank].cost) {
regrets[j] = jobs_min_costs[j];
} else {
regrets[job_rank] = jobs_second_min_costs[job_rank];
regrets[j] = jobs_second_min_costs[j];
}

if (input.vehicle_ok_with_job(v_rank, j) &&
regrets[j] < input.get_cost_upper_bound()) {
all_compatible_jobs_later_undoable = false;
}
}

if (all_compatible_jobs_later_undoable) {
// Same approach as for basic heuristic.
for (const auto j : unassigned) {
regrets[j] = evals[j][v_rank].cost;
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/structures/vroom/eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ struct Eval {
}
};

constexpr Eval MAX_EVAL = {std::numeric_limits<Cost>::max(),
std::numeric_limits<Duration>::max(),
std::numeric_limits<Distance>::max()};
constexpr Eval NO_EVAL = {std::numeric_limits<Cost>::max(), 0, 0};
constexpr Eval NO_GAIN = {std::numeric_limits<Cost>::min(), 0, 0};

Expand Down
2 changes: 1 addition & 1 deletion src/structures/vroom/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ void Input::set_jobs_vehicles_evals() {
_jobs_vehicles_evals =
std::vector<std::vector<Eval>>(jobs.size(),
std::vector<Eval>(vehicles.size(),
MAX_EVAL));
Eval(_cost_upper_bound)));

for (std::size_t j = 0; j < jobs.size(); ++j) {
Index j_index = jobs[j].index();
Expand Down

0 comments on commit 9d58251

Please sign in to comment.