From 268addf133666734ca201d5312f0b3abc9f7c787 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Wed, 12 Apr 2023 08:39:42 +0200 Subject: [PATCH 01/15] Apply TSP fix as a local search move, YOLO way. --- src/algorithms/local_search/local_search.cpp | 103 +++++++++++++------ src/algorithms/local_search/local_search.h | 3 +- src/problems/cvrp/cvrp.cpp | 4 +- src/problems/cvrp/operators/tsp_fix.cpp | 63 ++++++++++++ src/problems/cvrp/operators/tsp_fix.h | 40 +++++++ src/problems/vrptw/operators/tsp_fix.cpp | 39 +++++++ src/problems/vrptw/operators/tsp_fix.h | 34 ++++++ src/problems/vrptw/vrptw.cpp | 4 +- src/structures/typedefs.h | 1 + src/utils/helpers.h | 3 +- 10 files changed, 261 insertions(+), 33 deletions(-) create mode 100644 src/problems/cvrp/operators/tsp_fix.cpp create mode 100644 src/problems/cvrp/operators/tsp_fix.h create mode 100644 src/problems/vrptw/operators/tsp_fix.cpp create mode 100644 src/problems/vrptw/operators/tsp_fix.h diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index 4e82787e2..bd4e6188e 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -24,6 +24,7 @@ All rights reserved (see LICENSE). #include "problems/vrptw/operators/route_exchange.h" #include "problems/vrptw/operators/route_split.h" #include "problems/vrptw/operators/swap_star.h" +#include "problems/vrptw/operators/tsp_fix.h" #include "problems/vrptw/operators/two_opt.h" #include "problems/vrptw/operators/unassigned_exchange.h" #include "utils/helpers.h" @@ -47,7 +48,8 @@ template + class RouteSplit, + class TSPFix> LocalSearch::LocalSearch(const Input& input, - std::vector& sol, - unsigned max_nb_jobs_removal, - const Timeout& timeout) + RouteSplit, + TSPFix>::LocalSearch(const Input& input, + std::vector& sol, + unsigned max_nb_jobs_removal, + const Timeout& timeout) : _input(input), _nb_vehicles(_input.vehicles.size()), _max_nb_jobs_removal(max_nb_jobs_removal), @@ -131,7 +134,8 @@ template + class RouteSplit, + class TSPFix> void LocalSearch::try_job_additions(const std::vector& - routes, - double regret_coeff) { + RouteSplit, + TSPFix>::try_job_additions(const std::vector& routes, + double regret_coeff) { bool job_added; std::vector> route_job_insertions; @@ -325,7 +329,8 @@ template + class RouteSplit, + class TSPFix> void LocalSearch::run_ls_step() { + RouteSplit, + TSPFix>::run_ls_step() { // Store best move involving a pair of routes. std::vector>> best_ops(_nb_vehicles); for (std::size_t v = 0; v < _nb_vehicles; ++v) { @@ -1104,6 +1110,29 @@ void LocalSearch 0 or + _sol[s_t.first].size() < 2) { + // TODO do not try with binding constraints. + continue; + } + +#ifdef LOG_LS_OPERATORS + ++tried_moves[OperatorName::TSPFix]; +#endif + TSPFix op(_input, _sol_state, _sol[s_t.first], s_t.first); + + if (op.gain() > best_gains[s_t.first][s_t.second]) { + best_gains[s_t.first][s_t.second] = op.gain(); + best_ops[s_t.first][s_t.second] = std::make_unique(op); + } + } + } + // IntraExchange stuff for (const auto& s_t : s_t_pairs) { if (s_t.first != s_t.second or best_priorities[s_t.first] > 0 or @@ -1838,7 +1867,8 @@ template + class RouteSplit, + class TSPFix> void LocalSearch::run() { + RouteSplit, + TSPFix>::run() { bool try_ls_step = true; bool first_step = true; @@ -1945,7 +1976,8 @@ template + class RouteSplit, + class TSPFix> std::array LocalSearch::get_stats() const { + RouteSplit, + TSPFix>::get_stats() const { std::array stats; for (auto op = 0; op < OperatorName::MAX; ++op) { stats[op] = OperatorStats(tried_moves.at(op), applied_moves.at(op)); @@ -1991,7 +2024,8 @@ template + class RouteSplit, + class TSPFix> Eval LocalSearch::job_route_cost(Index v_target, Index v, Index r) { + RouteSplit, + TSPFix>::job_route_cost(Index v_target, Index v, Index r) { assert(v != v_target); Eval eval = NO_EVAL; @@ -2062,7 +2097,8 @@ template + class RouteSplit, + class TSPFix> Eval LocalSearch::relocate_cost_lower_bound(Index v, Index r) { + RouteSplit, + TSPFix>::relocate_cost_lower_bound(Index v, Index r) { Eval best_bound = NO_EVAL; for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) { @@ -2112,7 +2149,8 @@ template + class RouteSplit, + class TSPFix> Eval LocalSearch::relocate_cost_lower_bound(Index v, - Index r1, - Index r2) { + RouteSplit, + TSPFix>::relocate_cost_lower_bound(Index v, + Index r1, + Index r2) { Eval best_bound = NO_EVAL; for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) { @@ -2166,7 +2205,8 @@ template + class RouteSplit, + class TSPFix> void LocalSearch::remove_from_routes() { + RouteSplit, + TSPFix>::remove_from_routes() { // Store nearest job from and to any job in any route for constant // time access down the line. for (std::size_t v1 = 0; v1 < _nb_vehicles; ++v1) { @@ -2321,7 +2362,8 @@ template + class RouteSplit, + class TSPFix> utils::SolutionIndicators LocalSearch LocalSearch::indicators() const { + RouteSplit, + TSPFix>::indicators() const { return _best_sol_indicators; } @@ -2360,7 +2403,8 @@ template class LocalSearch; + vrptw::RouteSplit, + vrptw::TSPFix>; template class LocalSearch; + cvrp::RouteSplit, + cvrp::TSPFix>; } // namespace vroom::ls diff --git a/src/algorithms/local_search/local_search.h b/src/algorithms/local_search/local_search.h index c92ed0d2d..7b34aa5a4 100644 --- a/src/algorithms/local_search/local_search.h +++ b/src/algorithms/local_search/local_search.h @@ -32,7 +32,8 @@ template + class RouteSplit, + class TSPFix> class LocalSearch { private: const Input& _input; diff --git a/src/problems/cvrp/cvrp.cpp b/src/problems/cvrp/cvrp.cpp index 28171a587..583ae0efa 100644 --- a/src/problems/cvrp/cvrp.cpp +++ b/src/problems/cvrp/cvrp.cpp @@ -25,6 +25,7 @@ All rights reserved (see LICENSE). #include "problems/cvrp/operators/route_exchange.h" #include "problems/cvrp/operators/route_split.h" #include "problems/cvrp/operators/swap_star.h" +#include "problems/cvrp/operators/tsp_fix.h" #include "problems/cvrp/operators/two_opt.h" #include "problems/cvrp/operators/unassigned_exchange.h" #include "problems/tsp/tsp.h" @@ -51,7 +52,8 @@ using LocalSearch = ls::LocalSearch; + cvrp::RouteSplit, + cvrp::TSPFix>; } // namespace cvrp const std::vector CVRP::homogeneous_parameters = diff --git a/src/problems/cvrp/operators/tsp_fix.cpp b/src/problems/cvrp/operators/tsp_fix.cpp new file mode 100644 index 000000000..3a68c56cf --- /dev/null +++ b/src/problems/cvrp/operators/tsp_fix.cpp @@ -0,0 +1,63 @@ +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/cvrp/operators/tsp_fix.h" +#include "problems/tsp/tsp.h" + +namespace vroom::cvrp { + +TSPFix::TSPFix(const Input& input, + const utils::SolutionState& sol_state, + RawRoute& s_route, + Index s_vehicle) + // Use dummy 0 values for unused ranks. + : Operator(OperatorName::TSPFix, + input, + sol_state, + s_route, + s_vehicle, + 0, + s_route, + s_vehicle, + 0) { + assert(s_route.size() >= 2); +} + +void TSPFix::compute_gain() { + std::vector jobs = s_route; + TSP tsp(_input, std::move(jobs), s_vehicle); + tsp_route = tsp.raw_solve(1, Timeout()); + + const auto tsp_eval = + utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); + + stored_gain = _sol_state.route_evals[s_vehicle] - tsp_eval; + gain_computed = true; +} + +bool TSPFix::is_valid() { + // TODO check! + return true; +} + +void TSPFix::apply() { + s_route = std::move(tsp_route); + + source.update_amounts(_input); +} + +std::vector TSPFix::addition_candidates() const { + return {s_vehicle}; +} + +std::vector TSPFix::update_candidates() const { + return {s_vehicle}; +} + +} // namespace vroom::cvrp diff --git a/src/problems/cvrp/operators/tsp_fix.h b/src/problems/cvrp/operators/tsp_fix.h new file mode 100644 index 000000000..79f68cbb0 --- /dev/null +++ b/src/problems/cvrp/operators/tsp_fix.h @@ -0,0 +1,40 @@ +#ifndef CVRP_TSP_FIX_H +#define CVRP_TSP_FIX_H + +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "algorithms/local_search/operator.h" + +namespace vroom::cvrp { + +class TSPFix : public ls::Operator { +protected: + std::vector tsp_route; + + void compute_gain() override; + +public: + TSPFix(const Input& input, + const utils::SolutionState& sol_state, + RawRoute& s_route, + Index s_vehicle); + + bool is_valid() override; + + void apply() override; + + std::vector addition_candidates() const override; + + std::vector update_candidates() const override; +}; + +} // namespace vroom::cvrp + +#endif diff --git a/src/problems/vrptw/operators/tsp_fix.cpp b/src/problems/vrptw/operators/tsp_fix.cpp new file mode 100644 index 000000000..cff01baee --- /dev/null +++ b/src/problems/vrptw/operators/tsp_fix.cpp @@ -0,0 +1,39 @@ +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/vrptw/operators/tsp_fix.h" + +namespace vroom::vrptw { + +TSPFix::TSPFix(const Input& input, + const utils::SolutionState& sol_state, + TWRoute& tw_s_route, + Index s_vehicle) + : cvrp::TSPFix(input, + sol_state, + static_cast(tw_s_route), + s_vehicle), + _tw_s_route(tw_s_route) { +} + +bool TSPFix::is_valid() { + // TODO check! + return true; +} + +void TSPFix::apply() { + _tw_s_route.replace(_input, + source.job_deliveries_sum(), + tsp_route.begin(), + tsp_route.end(), + 0, + s_route.size()); +} + +} // namespace vroom::vrptw diff --git a/src/problems/vrptw/operators/tsp_fix.h b/src/problems/vrptw/operators/tsp_fix.h new file mode 100644 index 000000000..8f9d5faa7 --- /dev/null +++ b/src/problems/vrptw/operators/tsp_fix.h @@ -0,0 +1,34 @@ +#ifndef VRPTW_TSP_FIX_H +#define VRPTW_TSP_FIX_H + +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/cvrp/operators/tsp_fix.h" + +namespace vroom::vrptw { + +class TSPFix : public cvrp::TSPFix { +private: + TWRoute& _tw_s_route; + +public: + TSPFix(const Input& input, + const utils::SolutionState& sol_state, + TWRoute& tw_s_route, + Index s_vehicle); + + bool is_valid() override; + + void apply() override; +}; + +} // namespace vroom::vrptw + +#endif diff --git a/src/problems/vrptw/vrptw.cpp b/src/problems/vrptw/vrptw.cpp index c837a993f..8356cff52 100644 --- a/src/problems/vrptw/vrptw.cpp +++ b/src/problems/vrptw/vrptw.cpp @@ -25,6 +25,7 @@ All rights reserved (see LICENSE). #include "problems/vrptw/operators/route_exchange.h" #include "problems/vrptw/operators/route_split.h" #include "problems/vrptw/operators/swap_star.h" +#include "problems/vrptw/operators/tsp_fix.h" #include "problems/vrptw/operators/two_opt.h" #include "problems/vrptw/operators/unassigned_exchange.h" #include "utils/helpers.h" @@ -50,7 +51,8 @@ using LocalSearch = ls::LocalSearch; + vrptw::RouteSplit, + vrptw::TSPFix>; } // namespace vrptw const std::vector VRPTW::homogeneous_parameters = diff --git a/src/structures/typedefs.h b/src/structures/typedefs.h index c84e1c5b1..9a7dfde56 100644 --- a/src/structures/typedefs.h +++ b/src/structures/typedefs.h @@ -164,6 +164,7 @@ enum OperatorName { RouteExchange, SwapStar, RouteSplit, + TSPFix, MAX }; diff --git a/src/utils/helpers.h b/src/utils/helpers.h index bc3283490..590277024 100644 --- a/src/utils/helpers.h +++ b/src/utils/helpers.h @@ -99,7 +99,8 @@ const std::array "PDShift", "RouteExchange", "SwapStar", - "RouteSplit"}); + "RouteSplit", + "TSPFix"}); inline void log_LS_operators( const std::vector>& From fa3984681121a60d309b425f5c3a7070d0e1ead8 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Tue, 29 Aug 2023 13:43:02 +0200 Subject: [PATCH 02/15] Rename operator to RouteFix. --- src/algorithms/local_search/local_search.cpp | 66 +++++++++---------- src/algorithms/local_search/local_search.h | 2 +- src/problems/cvrp/cvrp.cpp | 4 +- .../operators/{tsp_fix.cpp => route_fix.cpp} | 22 +++---- .../cvrp/operators/{tsp_fix.h => route_fix.h} | 14 ++-- .../operators/{tsp_fix.cpp => route_fix.cpp} | 22 +++---- src/problems/vrptw/operators/route_fix.h | 34 ++++++++++ src/problems/vrptw/operators/tsp_fix.h | 34 ---------- src/problems/vrptw/vrptw.cpp | 4 +- src/structures/typedefs.h | 2 +- src/utils/helpers.h | 2 +- 11 files changed, 103 insertions(+), 103 deletions(-) rename src/problems/cvrp/operators/{tsp_fix.cpp => route_fix.cpp} (66%) rename src/problems/cvrp/operators/{tsp_fix.h => route_fix.h} (67%) rename src/problems/vrptw/operators/{tsp_fix.cpp => route_fix.cpp} (52%) create mode 100644 src/problems/vrptw/operators/route_fix.h delete mode 100644 src/problems/vrptw/operators/tsp_fix.h diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index bd4e6188e..de1bd6479 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -22,9 +22,9 @@ All rights reserved (see LICENSE). #include "problems/vrptw/operators/relocate.h" #include "problems/vrptw/operators/reverse_two_opt.h" #include "problems/vrptw/operators/route_exchange.h" +#include "problems/vrptw/operators/route_fix.h" #include "problems/vrptw/operators/route_split.h" #include "problems/vrptw/operators/swap_star.h" -#include "problems/vrptw/operators/tsp_fix.h" #include "problems/vrptw/operators/two_opt.h" #include "problems/vrptw/operators/unassigned_exchange.h" #include "utils/helpers.h" @@ -49,7 +49,7 @@ template + class RouteFix> LocalSearch::LocalSearch(const Input& input, - std::vector& sol, - unsigned max_nb_jobs_removal, - const Timeout& timeout) + RouteFix>::LocalSearch(const Input& input, + std::vector& sol, + unsigned max_nb_jobs_removal, + const Timeout& timeout) : _input(input), _nb_vehicles(_input.vehicles.size()), _max_nb_jobs_removal(max_nb_jobs_removal), @@ -135,7 +135,7 @@ template + class RouteFix> void LocalSearch::try_job_additions(const std::vector& routes, - double regret_coeff) { + RouteFix>::try_job_additions(const std::vector& routes, + double regret_coeff) { bool job_added; std::vector> route_job_insertions; @@ -330,7 +330,7 @@ template + class RouteFix> void LocalSearch::run_ls_step() { + RouteFix>::run_ls_step() { // Store best move involving a pair of routes. std::vector>> best_ops(_nb_vehicles); for (std::size_t v = 0; v < _nb_vehicles; ++v) { @@ -1110,7 +1110,7 @@ void LocalSearch best_gains[s_t.first][s_t.second]) { best_gains[s_t.first][s_t.second] = op.gain(); - best_ops[s_t.first][s_t.second] = std::make_unique(op); + best_ops[s_t.first][s_t.second] = std::make_unique(op); } } } @@ -1868,7 +1868,7 @@ template + class RouteFix> void LocalSearch::run() { + RouteFix>::run() { bool try_ls_step = true; bool first_step = true; @@ -1977,7 +1977,7 @@ template + class RouteFix> std::array LocalSearch::get_stats() const { + RouteFix>::get_stats() const { std::array stats; for (auto op = 0; op < OperatorName::MAX; ++op) { stats[op] = OperatorStats(tried_moves.at(op), applied_moves.at(op)); @@ -2025,7 +2025,7 @@ template + class RouteFix> Eval LocalSearch::job_route_cost(Index v_target, Index v, Index r) { + RouteFix>::job_route_cost(Index v_target, Index v, Index r) { assert(v != v_target); Eval eval = NO_EVAL; @@ -2098,7 +2098,7 @@ template + class RouteFix> Eval LocalSearch::relocate_cost_lower_bound(Index v, Index r) { + RouteFix>::relocate_cost_lower_bound(Index v, Index r) { Eval best_bound = NO_EVAL; for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) { @@ -2150,7 +2150,7 @@ template + class RouteFix> Eval LocalSearch::relocate_cost_lower_bound(Index v, - Index r1, - Index r2) { + RouteFix>::relocate_cost_lower_bound(Index v, + Index r1, + Index r2) { Eval best_bound = NO_EVAL; for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) { @@ -2206,7 +2206,7 @@ template + class RouteFix> void LocalSearch::remove_from_routes() { + RouteFix>::remove_from_routes() { // Store nearest job from and to any job in any route for constant // time access down the line. for (std::size_t v1 = 0; v1 < _nb_vehicles; ++v1) { @@ -2363,7 +2363,7 @@ template + class RouteFix> utils::SolutionIndicators LocalSearch LocalSearch::indicators() const { + RouteFix>::indicators() const { return _best_sol_indicators; } @@ -2404,7 +2404,7 @@ template class LocalSearch; + vrptw::RouteFix>; template class LocalSearch; + cvrp::RouteFix>; } // namespace vroom::ls diff --git a/src/algorithms/local_search/local_search.h b/src/algorithms/local_search/local_search.h index 7b34aa5a4..c393b8d48 100644 --- a/src/algorithms/local_search/local_search.h +++ b/src/algorithms/local_search/local_search.h @@ -33,7 +33,7 @@ template + class RouteFix> class LocalSearch { private: const Input& _input; diff --git a/src/problems/cvrp/cvrp.cpp b/src/problems/cvrp/cvrp.cpp index 583ae0efa..bbded59f6 100644 --- a/src/problems/cvrp/cvrp.cpp +++ b/src/problems/cvrp/cvrp.cpp @@ -23,9 +23,9 @@ All rights reserved (see LICENSE). #include "problems/cvrp/operators/relocate.h" #include "problems/cvrp/operators/reverse_two_opt.h" #include "problems/cvrp/operators/route_exchange.h" +#include "problems/cvrp/operators/route_fix.h" #include "problems/cvrp/operators/route_split.h" #include "problems/cvrp/operators/swap_star.h" -#include "problems/cvrp/operators/tsp_fix.h" #include "problems/cvrp/operators/two_opt.h" #include "problems/cvrp/operators/unassigned_exchange.h" #include "problems/tsp/tsp.h" @@ -53,7 +53,7 @@ using LocalSearch = ls::LocalSearch; + cvrp::RouteFix>; } // namespace cvrp const std::vector CVRP::homogeneous_parameters = diff --git a/src/problems/cvrp/operators/tsp_fix.cpp b/src/problems/cvrp/operators/route_fix.cpp similarity index 66% rename from src/problems/cvrp/operators/tsp_fix.cpp rename to src/problems/cvrp/operators/route_fix.cpp index 3a68c56cf..2e24fdf3c 100644 --- a/src/problems/cvrp/operators/tsp_fix.cpp +++ b/src/problems/cvrp/operators/route_fix.cpp @@ -7,17 +7,17 @@ All rights reserved (see LICENSE). */ -#include "problems/cvrp/operators/tsp_fix.h" +#include "problems/cvrp/operators/route_fix.h" #include "problems/tsp/tsp.h" namespace vroom::cvrp { -TSPFix::TSPFix(const Input& input, - const utils::SolutionState& sol_state, - RawRoute& s_route, - Index s_vehicle) +RouteFix::RouteFix(const Input& input, + const utils::SolutionState& sol_state, + RawRoute& s_route, + Index s_vehicle) // Use dummy 0 values for unused ranks. - : Operator(OperatorName::TSPFix, + : Operator(OperatorName::RouteFix, input, sol_state, s_route, @@ -29,7 +29,7 @@ TSPFix::TSPFix(const Input& input, assert(s_route.size() >= 2); } -void TSPFix::compute_gain() { +void RouteFix::compute_gain() { std::vector jobs = s_route; TSP tsp(_input, std::move(jobs), s_vehicle); tsp_route = tsp.raw_solve(1, Timeout()); @@ -41,22 +41,22 @@ void TSPFix::compute_gain() { gain_computed = true; } -bool TSPFix::is_valid() { +bool RouteFix::is_valid() { // TODO check! return true; } -void TSPFix::apply() { +void RouteFix::apply() { s_route = std::move(tsp_route); source.update_amounts(_input); } -std::vector TSPFix::addition_candidates() const { +std::vector RouteFix::addition_candidates() const { return {s_vehicle}; } -std::vector TSPFix::update_candidates() const { +std::vector RouteFix::update_candidates() const { return {s_vehicle}; } diff --git a/src/problems/cvrp/operators/tsp_fix.h b/src/problems/cvrp/operators/route_fix.h similarity index 67% rename from src/problems/cvrp/operators/tsp_fix.h rename to src/problems/cvrp/operators/route_fix.h index 79f68cbb0..ac6bfe13d 100644 --- a/src/problems/cvrp/operators/tsp_fix.h +++ b/src/problems/cvrp/operators/route_fix.h @@ -1,5 +1,5 @@ -#ifndef CVRP_TSP_FIX_H -#define CVRP_TSP_FIX_H +#ifndef CVRP_ROUTE_FIX_H +#define CVRP_ROUTE_FIX_H /* @@ -14,17 +14,17 @@ All rights reserved (see LICENSE). namespace vroom::cvrp { -class TSPFix : public ls::Operator { +class RouteFix : public ls::Operator { protected: std::vector tsp_route; void compute_gain() override; public: - TSPFix(const Input& input, - const utils::SolutionState& sol_state, - RawRoute& s_route, - Index s_vehicle); + RouteFix(const Input& input, + const utils::SolutionState& sol_state, + RawRoute& s_route, + Index s_vehicle); bool is_valid() override; diff --git a/src/problems/vrptw/operators/tsp_fix.cpp b/src/problems/vrptw/operators/route_fix.cpp similarity index 52% rename from src/problems/vrptw/operators/tsp_fix.cpp rename to src/problems/vrptw/operators/route_fix.cpp index cff01baee..b80ba4be9 100644 --- a/src/problems/vrptw/operators/tsp_fix.cpp +++ b/src/problems/vrptw/operators/route_fix.cpp @@ -7,27 +7,27 @@ All rights reserved (see LICENSE). */ -#include "problems/vrptw/operators/tsp_fix.h" +#include "problems/vrptw/operators/route_fix.h" namespace vroom::vrptw { -TSPFix::TSPFix(const Input& input, - const utils::SolutionState& sol_state, - TWRoute& tw_s_route, - Index s_vehicle) - : cvrp::TSPFix(input, - sol_state, - static_cast(tw_s_route), - s_vehicle), +RouteFix::RouteFix(const Input& input, + const utils::SolutionState& sol_state, + TWRoute& tw_s_route, + Index s_vehicle) + : cvrp::RouteFix(input, + sol_state, + static_cast(tw_s_route), + s_vehicle), _tw_s_route(tw_s_route) { } -bool TSPFix::is_valid() { +bool RouteFix::is_valid() { // TODO check! return true; } -void TSPFix::apply() { +void RouteFix::apply() { _tw_s_route.replace(_input, source.job_deliveries_sum(), tsp_route.begin(), diff --git a/src/problems/vrptw/operators/route_fix.h b/src/problems/vrptw/operators/route_fix.h new file mode 100644 index 000000000..5f6e46a7e --- /dev/null +++ b/src/problems/vrptw/operators/route_fix.h @@ -0,0 +1,34 @@ +#ifndef VRPTW_ROUTE_FIX_H +#define VRPTW_ROUTE_FIX_H + +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/cvrp/operators/route_fix.h" + +namespace vroom::vrptw { + +class RouteFix : public cvrp::RouteFix { +private: + TWRoute& _tw_s_route; + +public: + RouteFix(const Input& input, + const utils::SolutionState& sol_state, + TWRoute& tw_s_route, + Index s_vehicle); + + bool is_valid() override; + + void apply() override; +}; + +} // namespace vroom::vrptw + +#endif diff --git a/src/problems/vrptw/operators/tsp_fix.h b/src/problems/vrptw/operators/tsp_fix.h deleted file mode 100644 index 8f9d5faa7..000000000 --- a/src/problems/vrptw/operators/tsp_fix.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef VRPTW_TSP_FIX_H -#define VRPTW_TSP_FIX_H - -/* - -This file is part of VROOM. - -Copyright (c) 2015-2022, Julien Coupey. -All rights reserved (see LICENSE). - -*/ - -#include "problems/cvrp/operators/tsp_fix.h" - -namespace vroom::vrptw { - -class TSPFix : public cvrp::TSPFix { -private: - TWRoute& _tw_s_route; - -public: - TSPFix(const Input& input, - const utils::SolutionState& sol_state, - TWRoute& tw_s_route, - Index s_vehicle); - - bool is_valid() override; - - void apply() override; -}; - -} // namespace vroom::vrptw - -#endif diff --git a/src/problems/vrptw/vrptw.cpp b/src/problems/vrptw/vrptw.cpp index 8356cff52..7cfb71f27 100644 --- a/src/problems/vrptw/vrptw.cpp +++ b/src/problems/vrptw/vrptw.cpp @@ -23,9 +23,9 @@ All rights reserved (see LICENSE). #include "problems/vrptw/operators/relocate.h" #include "problems/vrptw/operators/reverse_two_opt.h" #include "problems/vrptw/operators/route_exchange.h" +#include "problems/vrptw/operators/route_fix.h" #include "problems/vrptw/operators/route_split.h" #include "problems/vrptw/operators/swap_star.h" -#include "problems/vrptw/operators/tsp_fix.h" #include "problems/vrptw/operators/two_opt.h" #include "problems/vrptw/operators/unassigned_exchange.h" #include "utils/helpers.h" @@ -52,7 +52,7 @@ using LocalSearch = ls::LocalSearch; + vrptw::RouteFix>; } // namespace vrptw const std::vector VRPTW::homogeneous_parameters = diff --git a/src/structures/typedefs.h b/src/structures/typedefs.h index 9a7dfde56..159141ce0 100644 --- a/src/structures/typedefs.h +++ b/src/structures/typedefs.h @@ -164,7 +164,7 @@ enum OperatorName { RouteExchange, SwapStar, RouteSplit, - TSPFix, + RouteFix, MAX }; diff --git a/src/utils/helpers.h b/src/utils/helpers.h index 590277024..8009de544 100644 --- a/src/utils/helpers.h +++ b/src/utils/helpers.h @@ -100,7 +100,7 @@ const std::array "RouteExchange", "SwapStar", "RouteSplit", - "TSPFix"}); + "RouteFix"}); inline void log_LS_operators( const std::vector>& From 4d2f450728ba60cda7092e5d5f725f96ad5b9a88 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Tue, 29 Aug 2023 17:38:49 +0200 Subject: [PATCH 03/15] Add default values for basic heuristic regret and sort. --- src/algorithms/heuristics/heuristics.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/heuristics/heuristics.h b/src/algorithms/heuristics/heuristics.h index a02c33046..665114e23 100644 --- a/src/algorithms/heuristics/heuristics.h +++ b/src/algorithms/heuristics/heuristics.h @@ -24,8 +24,8 @@ Eval basic(const Input& input, const InputIterator vehicles_begin, const InputIterator vehicles_end, INIT init, - double lambda, - SORT sort); + double lambda = 0, + SORT sort = SORT::CAPACITY); // Adjusting the above for situations with heterogeneous fleet. template From 8d8d8f3d863aee36cd0ef34d09a389f756a66d8c Mon Sep 17 00:00:00 2001 From: jcoupey Date: Tue, 29 Aug 2023 17:40:32 +0200 Subject: [PATCH 04/15] Make cvrp::RouteSplit::compute_gain private. --- src/problems/cvrp/operators/route_split.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/problems/cvrp/operators/route_split.h b/src/problems/cvrp/operators/route_split.h index 9ca0bb6b9..e1a18b94b 100644 --- a/src/problems/cvrp/operators/route_split.h +++ b/src/problems/cvrp/operators/route_split.h @@ -16,6 +16,9 @@ All rights reserved (see LICENSE). namespace vroom::cvrp { class RouteSplit : public ls::Operator { +private: + void compute_gain() override; + protected: const Eval _best_known_gain; const std::vector _empty_route_ranks; @@ -24,8 +27,6 @@ class RouteSplit : public ls::Operator { static const std::vector> dummy_route_refs; - void compute_gain() override; - public: RouteSplit(const Input& input, const utils::SolutionState& sol_state, From cb943d29711a2433b2027c9bdaf3e1d5c1c6cb7e Mon Sep 17 00:00:00 2001 From: jcoupey Date: Tue, 29 Aug 2023 17:46:08 +0200 Subject: [PATCH 05/15] Switch from TSP call to using basic heuristic. --- src/problems/cvrp/operators/route_fix.cpp | 34 ++++++++++++++------ src/problems/cvrp/operators/route_fix.h | 7 ++-- src/problems/vrptw/operators/route_fix.cpp | 37 ++++++++++++++++++++-- src/problems/vrptw/operators/route_fix.h | 2 ++ 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/problems/cvrp/operators/route_fix.cpp b/src/problems/cvrp/operators/route_fix.cpp index 2e24fdf3c..fe6235d9e 100644 --- a/src/problems/cvrp/operators/route_fix.cpp +++ b/src/problems/cvrp/operators/route_fix.cpp @@ -8,7 +8,7 @@ All rights reserved (see LICENSE). */ #include "problems/cvrp/operators/route_fix.h" -#include "problems/tsp/tsp.h" +#include "algorithms/heuristics/heuristics.h" namespace vroom::cvrp { @@ -30,24 +30,40 @@ RouteFix::RouteFix(const Input& input, } void RouteFix::compute_gain() { - std::vector jobs = s_route; - TSP tsp(_input, std::move(jobs), s_vehicle); - tsp_route = tsp.raw_solve(1, Timeout()); + std::vector fix_sol; + fix_sol.reserve(_input.vehicles.size()); - const auto tsp_eval = - utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); + for (Index v = 0; v < _input.vehicles.size(); ++v) { + fix_sol.emplace_back(_input, v, _input.zero_amount().size()); + } - stored_gain = _sol_state.route_evals[s_vehicle] - tsp_eval; + std::vector vehicles_ranks({s_vehicle}); + + const auto fix_eval = heuristics::basic(_input, + fix_sol, + s_route.cbegin(), + s_route.cend(), + vehicles_ranks.cbegin(), + vehicles_ranks.cend(), + INIT::NONE); + + const auto assigned_jobs = fix_sol[s_vehicle].size(); + + stored_gain = (assigned_jobs < s_route.size()) + ? NO_GAIN + : _sol_state.route_evals[s_vehicle] - fix_eval; + heuristic_route = std::move(fix_sol[s_vehicle].route); gain_computed = true; } bool RouteFix::is_valid() { - // TODO check! + // Not supposed to be used. + assert(false); return true; } void RouteFix::apply() { - s_route = std::move(tsp_route); + s_route = std::move(heuristic_route); source.update_amounts(_input); } diff --git a/src/problems/cvrp/operators/route_fix.h b/src/problems/cvrp/operators/route_fix.h index ac6bfe13d..f1399b5e2 100644 --- a/src/problems/cvrp/operators/route_fix.h +++ b/src/problems/cvrp/operators/route_fix.h @@ -15,11 +15,12 @@ All rights reserved (see LICENSE). namespace vroom::cvrp { class RouteFix : public ls::Operator { -protected: - std::vector tsp_route; - +private: void compute_gain() override; +protected: + std::vector heuristic_route; + public: RouteFix(const Input& input, const utils::SolutionState& sol_state, diff --git a/src/problems/vrptw/operators/route_fix.cpp b/src/problems/vrptw/operators/route_fix.cpp index b80ba4be9..6bffc8a55 100644 --- a/src/problems/vrptw/operators/route_fix.cpp +++ b/src/problems/vrptw/operators/route_fix.cpp @@ -8,6 +8,7 @@ All rights reserved (see LICENSE). */ #include "problems/vrptw/operators/route_fix.h" +#include "algorithms/heuristics/heuristics.h" namespace vroom::vrptw { @@ -22,16 +23,46 @@ RouteFix::RouteFix(const Input& input, _tw_s_route(tw_s_route) { } +void RouteFix::compute_gain() { + // Similar to cvrp::RouteFix::compute_gain but makes sure to + // trigger heuristics::basic. + std::vector fix_sol; + fix_sol.reserve(_input.vehicles.size()); + + for (Index v = 0; v < _input.vehicles.size(); ++v) { + fix_sol.emplace_back(_input, v, _input.zero_amount().size()); + } + + std::vector vehicles_ranks({s_vehicle}); + + const auto fix_eval = heuristics::basic(_input, + fix_sol, + s_route.cbegin(), + s_route.cend(), + vehicles_ranks.cbegin(), + vehicles_ranks.cend(), + INIT::NONE); + + const auto assigned_jobs = fix_sol[s_vehicle].size(); + + stored_gain = (assigned_jobs < s_route.size()) + ? NO_GAIN + : _sol_state.route_evals[s_vehicle] - fix_eval; + heuristic_route = std::move(fix_sol[s_vehicle].route); + gain_computed = true; +} + bool RouteFix::is_valid() { - // TODO check! + // Not supposed to be used. + assert(false); return true; } void RouteFix::apply() { _tw_s_route.replace(_input, source.job_deliveries_sum(), - tsp_route.begin(), - tsp_route.end(), + heuristic_route.begin(), + heuristic_route.end(), 0, s_route.size()); } diff --git a/src/problems/vrptw/operators/route_fix.h b/src/problems/vrptw/operators/route_fix.h index 5f6e46a7e..881168e5e 100644 --- a/src/problems/vrptw/operators/route_fix.h +++ b/src/problems/vrptw/operators/route_fix.h @@ -18,6 +18,8 @@ class RouteFix : public cvrp::RouteFix { private: TWRoute& _tw_s_route; + void compute_gain() override; + public: RouteFix(const Input& input, const utils::SolutionState& sol_state, From 5cbb4dbb2734ea4104e044ebd34bb16238e27563 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Tue, 29 Aug 2023 17:48:46 +0200 Subject: [PATCH 06/15] Remove some restrictions as the heuristic checks for validity. --- src/algorithms/local_search/local_search.cpp | 25 ++++++++------------ 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index de1bd6479..0f3a71e01 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -1111,25 +1111,20 @@ void LocalSearch 0 or - _sol[s_t.first].size() < 2) { - // TODO do not try with binding constraints. - continue; - } + for (const auto& s_t : s_t_pairs) { + if (s_t.second != s_t.first or best_priorities[s_t.first] > 0 or + _sol[s_t.first].size() < 2) { + continue; + } #ifdef LOG_LS_OPERATORS - ++tried_moves[OperatorName::RouteFix]; + ++tried_moves[OperatorName::RouteFix]; #endif - RouteFix op(_input, _sol_state, _sol[s_t.first], s_t.first); + RouteFix op(_input, _sol_state, _sol[s_t.first], s_t.first); - if (op.gain() > best_gains[s_t.first][s_t.second]) { - best_gains[s_t.first][s_t.second] = op.gain(); - best_ops[s_t.first][s_t.second] = std::make_unique(op); - } + if (op.gain() > best_gains[s_t.first][s_t.second]) { + best_gains[s_t.first][s_t.second] = op.gain(); + best_ops[s_t.first][s_t.second] = std::make_unique(op); } } From c253699a7639d2827c0cd9ffa0ce1ec7e0af3da4 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 8 Sep 2023 14:20:39 +0200 Subject: [PATCH 07/15] Reverting changes in fa398468..5cbb4dbb range. --- src/algorithms/heuristics/heuristics.h | 4 +- src/algorithms/local_search/local_search.cpp | 85 ++++++++++--------- src/algorithms/local_search/local_search.h | 2 +- src/problems/cvrp/cvrp.cpp | 4 +- src/problems/cvrp/operators/route_fix.cpp | 79 ----------------- src/problems/cvrp/operators/route_split.h | 5 +- src/problems/cvrp/operators/tsp_fix.cpp | 63 ++++++++++++++ .../cvrp/operators/{route_fix.h => tsp_fix.h} | 21 +++-- src/problems/vrptw/operators/route_fix.cpp | 70 --------------- src/problems/vrptw/operators/route_fix.h | 36 -------- src/problems/vrptw/operators/tsp_fix.cpp | 39 +++++++++ src/problems/vrptw/operators/tsp_fix.h | 34 ++++++++ src/problems/vrptw/vrptw.cpp | 4 +- src/structures/typedefs.h | 2 +- src/utils/helpers.h | 2 +- 15 files changed, 202 insertions(+), 248 deletions(-) delete mode 100644 src/problems/cvrp/operators/route_fix.cpp create mode 100644 src/problems/cvrp/operators/tsp_fix.cpp rename src/problems/cvrp/operators/{route_fix.h => tsp_fix.h} (62%) delete mode 100644 src/problems/vrptw/operators/route_fix.cpp delete mode 100644 src/problems/vrptw/operators/route_fix.h create mode 100644 src/problems/vrptw/operators/tsp_fix.cpp create mode 100644 src/problems/vrptw/operators/tsp_fix.h diff --git a/src/algorithms/heuristics/heuristics.h b/src/algorithms/heuristics/heuristics.h index 665114e23..a02c33046 100644 --- a/src/algorithms/heuristics/heuristics.h +++ b/src/algorithms/heuristics/heuristics.h @@ -24,8 +24,8 @@ Eval basic(const Input& input, const InputIterator vehicles_begin, const InputIterator vehicles_end, INIT init, - double lambda = 0, - SORT sort = SORT::CAPACITY); + double lambda, + SORT sort); // Adjusting the above for situations with heterogeneous fleet. template diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index 0f3a71e01..bd4e6188e 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -22,9 +22,9 @@ All rights reserved (see LICENSE). #include "problems/vrptw/operators/relocate.h" #include "problems/vrptw/operators/reverse_two_opt.h" #include "problems/vrptw/operators/route_exchange.h" -#include "problems/vrptw/operators/route_fix.h" #include "problems/vrptw/operators/route_split.h" #include "problems/vrptw/operators/swap_star.h" +#include "problems/vrptw/operators/tsp_fix.h" #include "problems/vrptw/operators/two_opt.h" #include "problems/vrptw/operators/unassigned_exchange.h" #include "utils/helpers.h" @@ -49,7 +49,7 @@ template + class TSPFix> LocalSearch::LocalSearch(const Input& input, - std::vector& sol, - unsigned max_nb_jobs_removal, - const Timeout& timeout) + TSPFix>::LocalSearch(const Input& input, + std::vector& sol, + unsigned max_nb_jobs_removal, + const Timeout& timeout) : _input(input), _nb_vehicles(_input.vehicles.size()), _max_nb_jobs_removal(max_nb_jobs_removal), @@ -135,7 +135,7 @@ template + class TSPFix> void LocalSearch::try_job_additions(const std::vector& routes, - double regret_coeff) { + TSPFix>::try_job_additions(const std::vector& routes, + double regret_coeff) { bool job_added; std::vector> route_job_insertions; @@ -330,7 +330,7 @@ template + class TSPFix> void LocalSearch::run_ls_step() { + TSPFix>::run_ls_step() { // Store best move involving a pair of routes. std::vector>> best_ops(_nb_vehicles); for (std::size_t v = 0; v < _nb_vehicles; ++v) { @@ -1110,21 +1110,26 @@ void LocalSearch 0 or - _sol[s_t.first].size() < 2) { - continue; - } + // TSPFix stuff + if (!_input.has_shipments()) { + // TODO no shipments for current route only. + + for (const auto& s_t : s_t_pairs) { + if (s_t.second != s_t.first or best_priorities[s_t.first] > 0 or + _sol[s_t.first].size() < 2) { + // TODO do not try with binding constraints. + continue; + } #ifdef LOG_LS_OPERATORS - ++tried_moves[OperatorName::RouteFix]; + ++tried_moves[OperatorName::TSPFix]; #endif - RouteFix op(_input, _sol_state, _sol[s_t.first], s_t.first); + TSPFix op(_input, _sol_state, _sol[s_t.first], s_t.first); - if (op.gain() > best_gains[s_t.first][s_t.second]) { - best_gains[s_t.first][s_t.second] = op.gain(); - best_ops[s_t.first][s_t.second] = std::make_unique(op); + if (op.gain() > best_gains[s_t.first][s_t.second]) { + best_gains[s_t.first][s_t.second] = op.gain(); + best_ops[s_t.first][s_t.second] = std::make_unique(op); + } } } @@ -1863,7 +1868,7 @@ template + class TSPFix> void LocalSearch::run() { + TSPFix>::run() { bool try_ls_step = true; bool first_step = true; @@ -1972,7 +1977,7 @@ template + class TSPFix> std::array LocalSearch::get_stats() const { + TSPFix>::get_stats() const { std::array stats; for (auto op = 0; op < OperatorName::MAX; ++op) { stats[op] = OperatorStats(tried_moves.at(op), applied_moves.at(op)); @@ -2020,7 +2025,7 @@ template + class TSPFix> Eval LocalSearch::job_route_cost(Index v_target, Index v, Index r) { + TSPFix>::job_route_cost(Index v_target, Index v, Index r) { assert(v != v_target); Eval eval = NO_EVAL; @@ -2093,7 +2098,7 @@ template + class TSPFix> Eval LocalSearch::relocate_cost_lower_bound(Index v, Index r) { + TSPFix>::relocate_cost_lower_bound(Index v, Index r) { Eval best_bound = NO_EVAL; for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) { @@ -2145,7 +2150,7 @@ template + class TSPFix> Eval LocalSearch::relocate_cost_lower_bound(Index v, - Index r1, - Index r2) { + TSPFix>::relocate_cost_lower_bound(Index v, + Index r1, + Index r2) { Eval best_bound = NO_EVAL; for (std::size_t other_v = 0; other_v < _sol.size(); ++other_v) { @@ -2201,7 +2206,7 @@ template + class TSPFix> void LocalSearch::remove_from_routes() { + TSPFix>::remove_from_routes() { // Store nearest job from and to any job in any route for constant // time access down the line. for (std::size_t v1 = 0; v1 < _nb_vehicles; ++v1) { @@ -2358,7 +2363,7 @@ template + class TSPFix> utils::SolutionIndicators LocalSearch LocalSearch::indicators() const { + TSPFix>::indicators() const { return _best_sol_indicators; } @@ -2399,7 +2404,7 @@ template class LocalSearch; + vrptw::TSPFix>; template class LocalSearch; + cvrp::TSPFix>; } // namespace vroom::ls diff --git a/src/algorithms/local_search/local_search.h b/src/algorithms/local_search/local_search.h index c393b8d48..7b34aa5a4 100644 --- a/src/algorithms/local_search/local_search.h +++ b/src/algorithms/local_search/local_search.h @@ -33,7 +33,7 @@ template + class TSPFix> class LocalSearch { private: const Input& _input; diff --git a/src/problems/cvrp/cvrp.cpp b/src/problems/cvrp/cvrp.cpp index bbded59f6..583ae0efa 100644 --- a/src/problems/cvrp/cvrp.cpp +++ b/src/problems/cvrp/cvrp.cpp @@ -23,9 +23,9 @@ All rights reserved (see LICENSE). #include "problems/cvrp/operators/relocate.h" #include "problems/cvrp/operators/reverse_two_opt.h" #include "problems/cvrp/operators/route_exchange.h" -#include "problems/cvrp/operators/route_fix.h" #include "problems/cvrp/operators/route_split.h" #include "problems/cvrp/operators/swap_star.h" +#include "problems/cvrp/operators/tsp_fix.h" #include "problems/cvrp/operators/two_opt.h" #include "problems/cvrp/operators/unassigned_exchange.h" #include "problems/tsp/tsp.h" @@ -53,7 +53,7 @@ using LocalSearch = ls::LocalSearch; + cvrp::TSPFix>; } // namespace cvrp const std::vector CVRP::homogeneous_parameters = diff --git a/src/problems/cvrp/operators/route_fix.cpp b/src/problems/cvrp/operators/route_fix.cpp deleted file mode 100644 index fe6235d9e..000000000 --- a/src/problems/cvrp/operators/route_fix.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - -This file is part of VROOM. - -Copyright (c) 2015-2022, Julien Coupey. -All rights reserved (see LICENSE). - -*/ - -#include "problems/cvrp/operators/route_fix.h" -#include "algorithms/heuristics/heuristics.h" - -namespace vroom::cvrp { - -RouteFix::RouteFix(const Input& input, - const utils::SolutionState& sol_state, - RawRoute& s_route, - Index s_vehicle) - // Use dummy 0 values for unused ranks. - : Operator(OperatorName::RouteFix, - input, - sol_state, - s_route, - s_vehicle, - 0, - s_route, - s_vehicle, - 0) { - assert(s_route.size() >= 2); -} - -void RouteFix::compute_gain() { - std::vector fix_sol; - fix_sol.reserve(_input.vehicles.size()); - - for (Index v = 0; v < _input.vehicles.size(); ++v) { - fix_sol.emplace_back(_input, v, _input.zero_amount().size()); - } - - std::vector vehicles_ranks({s_vehicle}); - - const auto fix_eval = heuristics::basic(_input, - fix_sol, - s_route.cbegin(), - s_route.cend(), - vehicles_ranks.cbegin(), - vehicles_ranks.cend(), - INIT::NONE); - - const auto assigned_jobs = fix_sol[s_vehicle].size(); - - stored_gain = (assigned_jobs < s_route.size()) - ? NO_GAIN - : _sol_state.route_evals[s_vehicle] - fix_eval; - heuristic_route = std::move(fix_sol[s_vehicle].route); - gain_computed = true; -} - -bool RouteFix::is_valid() { - // Not supposed to be used. - assert(false); - return true; -} - -void RouteFix::apply() { - s_route = std::move(heuristic_route); - - source.update_amounts(_input); -} - -std::vector RouteFix::addition_candidates() const { - return {s_vehicle}; -} - -std::vector RouteFix::update_candidates() const { - return {s_vehicle}; -} - -} // namespace vroom::cvrp diff --git a/src/problems/cvrp/operators/route_split.h b/src/problems/cvrp/operators/route_split.h index e1a18b94b..9ca0bb6b9 100644 --- a/src/problems/cvrp/operators/route_split.h +++ b/src/problems/cvrp/operators/route_split.h @@ -16,9 +16,6 @@ All rights reserved (see LICENSE). namespace vroom::cvrp { class RouteSplit : public ls::Operator { -private: - void compute_gain() override; - protected: const Eval _best_known_gain; const std::vector _empty_route_ranks; @@ -27,6 +24,8 @@ class RouteSplit : public ls::Operator { static const std::vector> dummy_route_refs; + void compute_gain() override; + public: RouteSplit(const Input& input, const utils::SolutionState& sol_state, diff --git a/src/problems/cvrp/operators/tsp_fix.cpp b/src/problems/cvrp/operators/tsp_fix.cpp new file mode 100644 index 000000000..3a68c56cf --- /dev/null +++ b/src/problems/cvrp/operators/tsp_fix.cpp @@ -0,0 +1,63 @@ +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/cvrp/operators/tsp_fix.h" +#include "problems/tsp/tsp.h" + +namespace vroom::cvrp { + +TSPFix::TSPFix(const Input& input, + const utils::SolutionState& sol_state, + RawRoute& s_route, + Index s_vehicle) + // Use dummy 0 values for unused ranks. + : Operator(OperatorName::TSPFix, + input, + sol_state, + s_route, + s_vehicle, + 0, + s_route, + s_vehicle, + 0) { + assert(s_route.size() >= 2); +} + +void TSPFix::compute_gain() { + std::vector jobs = s_route; + TSP tsp(_input, std::move(jobs), s_vehicle); + tsp_route = tsp.raw_solve(1, Timeout()); + + const auto tsp_eval = + utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); + + stored_gain = _sol_state.route_evals[s_vehicle] - tsp_eval; + gain_computed = true; +} + +bool TSPFix::is_valid() { + // TODO check! + return true; +} + +void TSPFix::apply() { + s_route = std::move(tsp_route); + + source.update_amounts(_input); +} + +std::vector TSPFix::addition_candidates() const { + return {s_vehicle}; +} + +std::vector TSPFix::update_candidates() const { + return {s_vehicle}; +} + +} // namespace vroom::cvrp diff --git a/src/problems/cvrp/operators/route_fix.h b/src/problems/cvrp/operators/tsp_fix.h similarity index 62% rename from src/problems/cvrp/operators/route_fix.h rename to src/problems/cvrp/operators/tsp_fix.h index f1399b5e2..79f68cbb0 100644 --- a/src/problems/cvrp/operators/route_fix.h +++ b/src/problems/cvrp/operators/tsp_fix.h @@ -1,5 +1,5 @@ -#ifndef CVRP_ROUTE_FIX_H -#define CVRP_ROUTE_FIX_H +#ifndef CVRP_TSP_FIX_H +#define CVRP_TSP_FIX_H /* @@ -14,18 +14,17 @@ All rights reserved (see LICENSE). namespace vroom::cvrp { -class RouteFix : public ls::Operator { -private: - void compute_gain() override; - +class TSPFix : public ls::Operator { protected: - std::vector heuristic_route; + std::vector tsp_route; + + void compute_gain() override; public: - RouteFix(const Input& input, - const utils::SolutionState& sol_state, - RawRoute& s_route, - Index s_vehicle); + TSPFix(const Input& input, + const utils::SolutionState& sol_state, + RawRoute& s_route, + Index s_vehicle); bool is_valid() override; diff --git a/src/problems/vrptw/operators/route_fix.cpp b/src/problems/vrptw/operators/route_fix.cpp deleted file mode 100644 index 6bffc8a55..000000000 --- a/src/problems/vrptw/operators/route_fix.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - -This file is part of VROOM. - -Copyright (c) 2015-2022, Julien Coupey. -All rights reserved (see LICENSE). - -*/ - -#include "problems/vrptw/operators/route_fix.h" -#include "algorithms/heuristics/heuristics.h" - -namespace vroom::vrptw { - -RouteFix::RouteFix(const Input& input, - const utils::SolutionState& sol_state, - TWRoute& tw_s_route, - Index s_vehicle) - : cvrp::RouteFix(input, - sol_state, - static_cast(tw_s_route), - s_vehicle), - _tw_s_route(tw_s_route) { -} - -void RouteFix::compute_gain() { - // Similar to cvrp::RouteFix::compute_gain but makes sure to - // trigger heuristics::basic. - std::vector fix_sol; - fix_sol.reserve(_input.vehicles.size()); - - for (Index v = 0; v < _input.vehicles.size(); ++v) { - fix_sol.emplace_back(_input, v, _input.zero_amount().size()); - } - - std::vector vehicles_ranks({s_vehicle}); - - const auto fix_eval = heuristics::basic(_input, - fix_sol, - s_route.cbegin(), - s_route.cend(), - vehicles_ranks.cbegin(), - vehicles_ranks.cend(), - INIT::NONE); - - const auto assigned_jobs = fix_sol[s_vehicle].size(); - - stored_gain = (assigned_jobs < s_route.size()) - ? NO_GAIN - : _sol_state.route_evals[s_vehicle] - fix_eval; - heuristic_route = std::move(fix_sol[s_vehicle].route); - gain_computed = true; -} - -bool RouteFix::is_valid() { - // Not supposed to be used. - assert(false); - return true; -} - -void RouteFix::apply() { - _tw_s_route.replace(_input, - source.job_deliveries_sum(), - heuristic_route.begin(), - heuristic_route.end(), - 0, - s_route.size()); -} - -} // namespace vroom::vrptw diff --git a/src/problems/vrptw/operators/route_fix.h b/src/problems/vrptw/operators/route_fix.h deleted file mode 100644 index 881168e5e..000000000 --- a/src/problems/vrptw/operators/route_fix.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef VRPTW_ROUTE_FIX_H -#define VRPTW_ROUTE_FIX_H - -/* - -This file is part of VROOM. - -Copyright (c) 2015-2022, Julien Coupey. -All rights reserved (see LICENSE). - -*/ - -#include "problems/cvrp/operators/route_fix.h" - -namespace vroom::vrptw { - -class RouteFix : public cvrp::RouteFix { -private: - TWRoute& _tw_s_route; - - void compute_gain() override; - -public: - RouteFix(const Input& input, - const utils::SolutionState& sol_state, - TWRoute& tw_s_route, - Index s_vehicle); - - bool is_valid() override; - - void apply() override; -}; - -} // namespace vroom::vrptw - -#endif diff --git a/src/problems/vrptw/operators/tsp_fix.cpp b/src/problems/vrptw/operators/tsp_fix.cpp new file mode 100644 index 000000000..cff01baee --- /dev/null +++ b/src/problems/vrptw/operators/tsp_fix.cpp @@ -0,0 +1,39 @@ +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/vrptw/operators/tsp_fix.h" + +namespace vroom::vrptw { + +TSPFix::TSPFix(const Input& input, + const utils::SolutionState& sol_state, + TWRoute& tw_s_route, + Index s_vehicle) + : cvrp::TSPFix(input, + sol_state, + static_cast(tw_s_route), + s_vehicle), + _tw_s_route(tw_s_route) { +} + +bool TSPFix::is_valid() { + // TODO check! + return true; +} + +void TSPFix::apply() { + _tw_s_route.replace(_input, + source.job_deliveries_sum(), + tsp_route.begin(), + tsp_route.end(), + 0, + s_route.size()); +} + +} // namespace vroom::vrptw diff --git a/src/problems/vrptw/operators/tsp_fix.h b/src/problems/vrptw/operators/tsp_fix.h new file mode 100644 index 000000000..8f9d5faa7 --- /dev/null +++ b/src/problems/vrptw/operators/tsp_fix.h @@ -0,0 +1,34 @@ +#ifndef VRPTW_TSP_FIX_H +#define VRPTW_TSP_FIX_H + +/* + +This file is part of VROOM. + +Copyright (c) 2015-2022, Julien Coupey. +All rights reserved (see LICENSE). + +*/ + +#include "problems/cvrp/operators/tsp_fix.h" + +namespace vroom::vrptw { + +class TSPFix : public cvrp::TSPFix { +private: + TWRoute& _tw_s_route; + +public: + TSPFix(const Input& input, + const utils::SolutionState& sol_state, + TWRoute& tw_s_route, + Index s_vehicle); + + bool is_valid() override; + + void apply() override; +}; + +} // namespace vroom::vrptw + +#endif diff --git a/src/problems/vrptw/vrptw.cpp b/src/problems/vrptw/vrptw.cpp index 7cfb71f27..8356cff52 100644 --- a/src/problems/vrptw/vrptw.cpp +++ b/src/problems/vrptw/vrptw.cpp @@ -23,9 +23,9 @@ All rights reserved (see LICENSE). #include "problems/vrptw/operators/relocate.h" #include "problems/vrptw/operators/reverse_two_opt.h" #include "problems/vrptw/operators/route_exchange.h" -#include "problems/vrptw/operators/route_fix.h" #include "problems/vrptw/operators/route_split.h" #include "problems/vrptw/operators/swap_star.h" +#include "problems/vrptw/operators/tsp_fix.h" #include "problems/vrptw/operators/two_opt.h" #include "problems/vrptw/operators/unassigned_exchange.h" #include "utils/helpers.h" @@ -52,7 +52,7 @@ using LocalSearch = ls::LocalSearch; + vrptw::TSPFix>; } // namespace vrptw const std::vector VRPTW::homogeneous_parameters = diff --git a/src/structures/typedefs.h b/src/structures/typedefs.h index 159141ce0..9a7dfde56 100644 --- a/src/structures/typedefs.h +++ b/src/structures/typedefs.h @@ -164,7 +164,7 @@ enum OperatorName { RouteExchange, SwapStar, RouteSplit, - RouteFix, + TSPFix, MAX }; diff --git a/src/utils/helpers.h b/src/utils/helpers.h index 8009de544..590277024 100644 --- a/src/utils/helpers.h +++ b/src/utils/helpers.h @@ -100,7 +100,7 @@ const std::array "RouteExchange", "SwapStar", "RouteSplit", - "RouteFix"}); + "TSPFix"}); inline void log_LS_operators( const std::vector>& From 34b44f8419869d10dee15e6b350321d159d52200 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 8 Sep 2023 15:16:54 +0200 Subject: [PATCH 08/15] Implement cvrp::TSPFix::is_valid. --- src/algorithms/local_search/local_search.cpp | 2 +- src/problems/cvrp/operators/tsp_fix.cpp | 25 +++++++++++++++----- src/problems/cvrp/operators/tsp_fix.h | 3 +++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index bd4e6188e..1ff6017d1 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -1126,7 +1126,7 @@ void LocalSearch best_gains[s_t.first][s_t.second]) { + if (op.gain() > best_gains[s_t.first][s_t.second] and op.is_valid()) { best_gains[s_t.first][s_t.second] = op.gain(); best_ops[s_t.first][s_t.second] = std::make_unique(op); } diff --git a/src/problems/cvrp/operators/tsp_fix.cpp b/src/problems/cvrp/operators/tsp_fix.cpp index 3a68c56cf..c180cd2f3 100644 --- a/src/problems/cvrp/operators/tsp_fix.cpp +++ b/src/problems/cvrp/operators/tsp_fix.cpp @@ -25,7 +25,8 @@ TSPFix::TSPFix(const Input& input, 0, s_route, s_vehicle, - 0) { + 0), + _s_delivery(source.load_at_step(0)) { assert(s_route.size() >= 2); } @@ -34,16 +35,28 @@ void TSPFix::compute_gain() { TSP tsp(_input, std::move(jobs), s_vehicle); tsp_route = tsp.raw_solve(1, Timeout()); - const auto tsp_eval = - utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); + tsp_eval = utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); - stored_gain = _sol_state.route_evals[s_vehicle] - tsp_eval; + s_gain = _sol_state.route_evals[s_vehicle] - tsp_eval; + stored_gain = s_gain; gain_computed = true; } bool TSPFix::is_valid() { - // TODO check! - return true; + bool valid = is_valid_for_source_max_travel_time(); + + if (valid) { + RawRoute route(_input, s_vehicle, _input.zero_amount().size()); + + valid = route.is_valid_addition_for_capacity_inclusion(_input, + _s_delivery, + tsp_route.begin(), + tsp_route.end(), + 0, + 0); + } + + return valid; } void TSPFix::apply() { diff --git a/src/problems/cvrp/operators/tsp_fix.h b/src/problems/cvrp/operators/tsp_fix.h index 79f68cbb0..08dfa3d55 100644 --- a/src/problems/cvrp/operators/tsp_fix.h +++ b/src/problems/cvrp/operators/tsp_fix.h @@ -17,9 +17,12 @@ namespace vroom::cvrp { class TSPFix : public ls::Operator { protected: std::vector tsp_route; + Eval tsp_eval; void compute_gain() override; + const Amount _s_delivery; + public: TSPFix(const Input& input, const utils::SolutionState& sol_state, From 291c81c9e22c8bbc86e853672b7a54194db84151 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 8 Sep 2023 15:26:11 +0200 Subject: [PATCH 09/15] Implement vrptw::TSPFix::is_valid. --- src/problems/vrptw/operators/tsp_fix.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/problems/vrptw/operators/tsp_fix.cpp b/src/problems/vrptw/operators/tsp_fix.cpp index cff01baee..3b3a8332f 100644 --- a/src/problems/vrptw/operators/tsp_fix.cpp +++ b/src/problems/vrptw/operators/tsp_fix.cpp @@ -23,8 +23,13 @@ TSPFix::TSPFix(const Input& input, } bool TSPFix::is_valid() { - // TODO check! - return true; + return cvrp::TSPFix::is_valid() and + _tw_s_route.is_valid_addition_for_tw(_input, + _s_delivery, + tsp_route.begin(), + tsp_route.end(), + 0, + s_route.size()); } void TSPFix::apply() { From b2484ee4b3b1d4a4412dd3b5db73905b78d82771 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 15 Sep 2023 13:11:50 +0200 Subject: [PATCH 10/15] Store vehicle-level flag to decide on applying TSPFix operator. --- src/structures/vroom/input/input.cpp | 26 ++++++++++++++++++++++++++ src/structures/vroom/input/input.h | 6 ++++++ 2 files changed, 32 insertions(+) diff --git a/src/structures/vroom/input/input.cpp b/src/structures/vroom/input/input.cpp index d1a356b78..3289ff12a 100644 --- a/src/structures/vroom/input/input.cpp +++ b/src/structures/vroom/input/input.cpp @@ -8,6 +8,7 @@ All rights reserved (see LICENSE). */ #include +#include #include #if USE_LIBOSRM @@ -570,6 +571,30 @@ void Input::set_vehicles_compatibility() { } } +void Input::set_vehicles_TSP_flag() { + _good_TSP_candidate = std::vector(vehicles.size()); + + for (std::size_t v_rank = 0; v_rank < vehicles.size(); ++v_rank) { + const auto& v = vehicles[v_rank]; + + // Check if vehicle TW is in the intersection of its compatible + // jobs TW, i.e. all its compatible jobs have one TW containing + // it. + _good_TSP_candidate[v_rank] = + std::ranges::all_of(std::views::iota(0) | std::views::take(jobs.size()), + [&](Index j) { + return !vehicle_ok_with_job(v_rank, j) or + std::any_of(jobs[j].tws.cbegin(), + jobs[j].tws.cend(), + [&](const auto& tw) { + return tw.start <= + v.tw.start and + v.tw.end <= tw.end; + }); + }); + } +} + void Input::set_vehicles_costs() { for (auto& vehicle : vehicles) { auto d_m = _durations_matrices.find(vehicle.profile); @@ -1047,6 +1072,7 @@ Solution Input::solve(unsigned exploration_level, set_skills_compatibility(); set_extra_compatibility(); set_vehicles_compatibility(); + set_vehicles_TSP_flag(); set_jobs_vehicles_evals(); diff --git a/src/structures/vroom/input/input.h b/src/structures/vroom/input/input.h index b666425c7..275ac807b 100644 --- a/src/structures/vroom/input/input.h +++ b/src/structures/vroom/input/input.h @@ -62,6 +62,7 @@ class Input { Index _max_matrices_used_index{0}; bool _all_locations_have_coords{true}; std::vector> _jobs_vehicles_evals; + std::vector _good_TSP_candidate; unsigned _amount_size{0}; Amount _zero; @@ -81,6 +82,7 @@ class Input { void set_vehicles_costs(); void set_vehicles_max_tasks(); void set_jobs_vehicles_evals(); + void set_vehicles_TSP_flag(); void set_vehicle_steps_ranks(); void set_matrices(unsigned nb_thread); @@ -125,6 +127,10 @@ class Input { bool is_used_several_times(const Location& location) const; + bool is_good_TSP_candidate(const Index v) const { + return _good_TSP_candidate[v]; + } + bool has_skills() const; bool has_jobs() const; From 0b5deca123edb9b2aa1679b70fd2b1ed8b94399f Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 15 Sep 2023 13:12:30 +0200 Subject: [PATCH 11/15] Filter TSPFix checks. --- src/algorithms/local_search/local_search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index 1ff6017d1..e8843beda 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -1112,12 +1112,10 @@ void LocalSearch 0 or + !_input.is_good_TSP_candidate(s_t.first) or _sol[s_t.first].size() < 2) { - // TODO do not try with binding constraints. continue; } From 82817fbf82be09bd1c39ee21b7733648ee4327cf Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 15 Sep 2023 13:25:24 +0200 Subject: [PATCH 12/15] Mention new operator in changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index baef07dc1..a6752ccb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Store distance matrices (#956) - Default radius of 35km for OSRM snapping (#922) - Support for URL path in host (#966) +- `TSPFix` local search operator (#737) ### Changed From 8262be4ac1d655761f42d7b787f68ed3e0344129 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Fri, 15 Sep 2023 16:10:56 +0200 Subject: [PATCH 13/15] The range-based approach that was OK in gcc does not compile with clang. --- src/structures/vroom/input/input.cpp | 32 +++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/structures/vroom/input/input.cpp b/src/structures/vroom/input/input.cpp index 6da1c2981..c46723377 100644 --- a/src/structures/vroom/input/input.cpp +++ b/src/structures/vroom/input/input.cpp @@ -580,26 +580,24 @@ void Input::set_vehicles_compatibility() { } void Input::set_vehicles_TSP_flag() { - _good_TSP_candidate = std::vector(vehicles.size()); + _good_TSP_candidate = std::vector(vehicles.size(), true); - for (std::size_t v_rank = 0; v_rank < vehicles.size(); ++v_rank) { - const auto& v = vehicles[v_rank]; + for (std::size_t v = 0; v < vehicles.size(); ++v) { + const auto& vehicle = vehicles[v]; // Check if vehicle TW is in the intersection of its compatible - // jobs TW, i.e. all its compatible jobs have one TW containing - // it. - _good_TSP_candidate[v_rank] = - std::ranges::all_of(std::views::iota(0) | std::views::take(jobs.size()), - [&](Index j) { - return !vehicle_ok_with_job(v_rank, j) or - std::any_of(jobs[j].tws.cbegin(), - jobs[j].tws.cend(), - [&](const auto& tw) { - return tw.start <= - v.tw.start and - v.tw.end <= tw.end; - }); - }); + // jobs TW, i.e. all its compatible jobs have at least one TW + // containing it. + for (std::size_t j = 0; j < jobs.size() and _good_TSP_candidate[v]; ++j) { + _good_TSP_candidate[v] = + !vehicle_ok_with_job(v, j) or + std::any_of(jobs[j].tws.cbegin(), + jobs[j].tws.cend(), + [&](const auto& tw) { + return tw.start <= vehicle.tw.start and + vehicle.tw.end <= tw.end; + }); + } } } From 5735f403f284be9784c7e4b3d7dc6e1453ee6766 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Mon, 18 Sep 2023 10:04:00 +0200 Subject: [PATCH 14/15] No need to store tsp_eval as a class member. --- src/problems/cvrp/operators/tsp_fix.cpp | 5 ++--- src/problems/cvrp/operators/tsp_fix.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/problems/cvrp/operators/tsp_fix.cpp b/src/problems/cvrp/operators/tsp_fix.cpp index c180cd2f3..62e66098f 100644 --- a/src/problems/cvrp/operators/tsp_fix.cpp +++ b/src/problems/cvrp/operators/tsp_fix.cpp @@ -35,9 +35,8 @@ void TSPFix::compute_gain() { TSP tsp(_input, std::move(jobs), s_vehicle); tsp_route = tsp.raw_solve(1, Timeout()); - tsp_eval = utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); - - s_gain = _sol_state.route_evals[s_vehicle] - tsp_eval; + s_gain = _sol_state.route_evals[s_vehicle] - + utils::route_eval_for_vehicle(_input, s_vehicle, tsp_route); stored_gain = s_gain; gain_computed = true; } diff --git a/src/problems/cvrp/operators/tsp_fix.h b/src/problems/cvrp/operators/tsp_fix.h index 08dfa3d55..3b83de1e9 100644 --- a/src/problems/cvrp/operators/tsp_fix.h +++ b/src/problems/cvrp/operators/tsp_fix.h @@ -17,7 +17,6 @@ namespace vroom::cvrp { class TSPFix : public ls::Operator { protected: std::vector tsp_route; - Eval tsp_eval; void compute_gain() override; From 1616434455af143f0ee9a7763ad752c4d57914ee Mon Sep 17 00:00:00 2001 From: jcoupey Date: Mon, 18 Sep 2023 10:06:58 +0200 Subject: [PATCH 15/15] Remove unused include. --- src/structures/vroom/input/input.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/structures/vroom/input/input.cpp b/src/structures/vroom/input/input.cpp index c46723377..bfbf45b74 100644 --- a/src/structures/vroom/input/input.cpp +++ b/src/structures/vroom/input/input.cpp @@ -8,7 +8,6 @@ All rights reserved (see LICENSE). */ #include -#include #include #if USE_LIBOSRM