Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution ruin steps #1123

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4269214
Rename max_nb_jobs_removal to depth.
jcoupey May 24, 2024
44bd7e4
Explicitely pass number of searches and depth to solving functions.
jcoupey May 24, 2024
8c09577
Allow overriding nb_searches and depth for debug purposes.
jcoupey May 24, 2024
552f710
Adjust libvroom example for solve signature change.
jcoupey Jun 10, 2024
09c5db8
Move operator_names array to typedef.
jcoupey Jun 10, 2024
2e6245f
First sketch for LOG_LS_SEARCH structs.
jcoupey Jun 10, 2024
101270f
Populate vector of ls::log::Dump with heuristic parameters.
jcoupey Jun 10, 2024
d7e4430
Handle writing LS log struct to json.
jcoupey Jun 11, 2024
2dab4b4
Start populating LS solving steps.
jcoupey Jun 11, 2024
76a7cf6
Store LS move steps.
jcoupey Jun 11, 2024
84ea86f
Store job additions when performed after LS move.
jcoupey Jun 26, 2024
db17f07
Log R&R as a single step.
jcoupey Jun 26, 2024
721a43c
Store solution rollback event.
jcoupey Jun 26, 2024
967da55
Also log Rollback events for identical solutions.
jcoupey Jul 4, 2024
041a44a
Use microseconds for LS steps times.
jcoupey Aug 27, 2024
a8cf0b9
Merge branch 'master' into core/solution-ruin
jcoupey Sep 4, 2024
15281fc
Add logging step when reaching local minima.
jcoupey Sep 4, 2024
5c3d08a
Small refactor for solution building in utils.
jcoupey Sep 4, 2024
999c9f5
Differentiate RUIN and RECREATE events for logging.
jcoupey Sep 4, 2024
0b367fb
Optionally store solution in log steps.
jcoupey Sep 4, 2024
cd9d513
Add intermediate solutions in output log.
jcoupey Sep 4, 2024
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
16 changes: 8 additions & 8 deletions libvroom_examples/libvroom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,11 @@ void run_example_with_osrm() {
// - jobs 3 and 4 can only be served by vehicle 2
// - jobs 5 and 6 can be served by either one of the vehicles

// Solve!
// Solve using exploration level as depth and number of searches.
auto sol =
problem_instance.solve(vroom::DEFAULT_EXPLORATION_LEVEL, // Exploration
// level.
vroom::DEFAULT_THREADS_NUMBER); // Use 4 threads.
problem_instance.solve(vroom::DEFAULT_EXPLORATION_LEVEL,
vroom::DEFAULT_EXPLORATION_LEVEL,
vroom::DEFAULT_THREADS_NUMBER); // Use 4 threads.

log_solution(sol, GEOMETRY);
}
Expand Down Expand Up @@ -274,11 +274,11 @@ void run_example_with_custom_matrix() {
problem_instance.add_job(j);
}

// Solve!
// Solve using exploration level as depth and number of searches.
auto sol =
problem_instance.solve(vroom::DEFAULT_EXPLORATION_LEVEL, // Exploration
// level.
vroom::DEFAULT_THREADS_NUMBER); // Use 4 threads.
problem_instance.solve(vroom::DEFAULT_EXPLORATION_LEVEL,
vroom::DEFAULT_EXPLORATION_LEVEL,
vroom::DEFAULT_THREADS_NUMBER); // Use 4 threads.

log_solution(sol, GEOMETRY);
}
Expand Down
80 changes: 75 additions & 5 deletions src/algorithms/local_search/local_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ LocalSearch<Route,
PriorityReplace,
TSPFix>::LocalSearch(const Input& input,
std::vector<Route>& sol,
unsigned max_nb_jobs_removal,
unsigned depth,
const Timeout& timeout)
: _input(input),
_nb_vehicles(_input.vehicles.size()),
_max_nb_jobs_removal(max_nb_jobs_removal),
_depth(depth),
_deadline(timeout.has_value() ? utils::now() + timeout.value()
: Deadline()),
_all_routes(_nb_vehicles),
Expand Down Expand Up @@ -162,7 +162,12 @@ void LocalSearch<Route,
RouteSplit,
PriorityReplace,
TSPFix>::try_job_additions(const std::vector<Index>& routes,
double regret_coeff) {
double regret_coeff
#ifdef LOG_LS
,
bool log_addition_step
#endif
) {
bool job_added;

std::vector<std::vector<RouteInsertion>> route_job_insertions;
Expand Down Expand Up @@ -318,6 +323,16 @@ void LocalSearch<Route,
route_job_insertions[best_route_idx][j].eval.cost += fixed_cost;
}
}

#ifdef LOG_LS
if (log_addition_step) {
steps.push_back({utils::now(),
log::EVENT::JOB_ADDITION,
OperatorName::MAX,
utils::SolutionIndicators<Route>(_input, _sol),
std::nullopt});
}
#endif
}
} while (job_added);

Expand Down Expand Up @@ -1830,6 +1845,14 @@ void LocalSearch<Route,
++applied_moves.at(best_ops[best_source][best_target]->get_name());
#endif

#ifdef LOG_LS
steps.push_back({utils::now(),
log::EVENT::OPERATOR,
best_ops[best_source][best_target]->get_name(),
utils::SolutionIndicators<Route>(_input, _sol),
std::nullopt});
#endif

#ifndef NDEBUG
// Update route costs.
const auto previous_eval =
Expand Down Expand Up @@ -1869,7 +1892,12 @@ void LocalSearch<Route,

try_job_additions(best_ops[best_source][best_target]
->addition_candidates(),
0);
0
#ifdef LOG_LS
,
true
#endif
);

for (auto v_rank : update_candidates) {
_sol_state.update_costs(_sol[v_rank].route, v_rank);
Expand Down Expand Up @@ -1982,6 +2010,14 @@ void LocalSearch<Route,

unsigned current_nb_removal = 1;

#ifdef LOG_LS
steps.push_back({utils::now(),
log::EVENT::START,
OperatorName::MAX,
_best_sol_indicators,
utils::format_solution(_input, _best_sol)});
#endif

while (try_ls_step) {
// A round of local search.
run_ls_step();
Expand All @@ -1992,6 +2028,14 @@ void LocalSearch<Route,
current_sol_indicators < _best_sol_indicators) {
_best_sol_indicators = current_sol_indicators;
_best_sol = _sol;

#ifdef LOG_LS
steps.push_back({utils::now(),
log::EVENT::LOCAL_MINIMA,
OperatorName::MAX,
_best_sol_indicators,
utils::format_solution(_input, _best_sol)});
#endif
} else {
if (!first_step) {
++current_nb_removal;
Expand All @@ -2001,11 +2045,21 @@ void LocalSearch<Route,
_sol = _best_sol;
_sol_state.setup(_sol);
}
#ifdef LOG_LS
if (_best_sol_indicators < current_sol_indicators or
_best_sol_indicators == current_sol_indicators) {
steps.push_back({utils::now(),
log::EVENT::ROLLBACK,
OperatorName::MAX,
_best_sol_indicators,
std::nullopt});
}
#endif
}

// Try again on each improvement until we reach last job removal
// level or deadline is met.
try_ls_step = (current_nb_removal <= _max_nb_jobs_removal) &&
try_ls_step = (current_nb_removal <= _depth) &&
(!_deadline.has_value() || utils::now() < _deadline.value());

if (try_ls_step) {
Expand All @@ -2023,6 +2077,14 @@ void LocalSearch<Route,
}
}

#ifdef LOG_LS
steps.push_back({utils::now(),
log::EVENT::RUIN,
OperatorName::MAX,
utils::SolutionIndicators<Route>(_input, _sol),
utils::format_solution(_input, _sol)});
#endif

// Update insertion ranks ranges.
for (std::size_t v = 0; v < _sol.size(); ++v) {
_sol_state.set_insertion_ranks(_sol[v], v);
Expand All @@ -2043,6 +2105,14 @@ void LocalSearch<Route,
_sol_state.set_pd_matching_ranks(_sol[v].route, v);
_sol_state.set_pd_gains(_sol[v].route, v);
}

#ifdef LOG_LS
steps.push_back({utils::now(),
log::EVENT::RECREATE,
OperatorName::MAX,
utils::SolutionIndicators<Route>(_input, _sol),
utils::format_solution(_input, _sol)});
#endif
}

first_step = false;
Expand Down
26 changes: 23 additions & 3 deletions src/algorithms/local_search/local_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ All rights reserved (see LICENSE).
#include "structures/vroom/solution_indicators.h"
#include "structures/vroom/solution_state.h"

#ifdef LOG_LS
#include "algorithms/local_search/log_local_search.h"
#endif

namespace vroom::ls {

template <class Route,
Expand Down Expand Up @@ -40,7 +44,7 @@ class LocalSearch {
const Input& _input;
const std::size_t _nb_vehicles;

const unsigned _max_nb_jobs_removal;
const unsigned _depth;
const Deadline _deadline;

std::vector<Index> _all_routes;
Expand All @@ -58,7 +62,17 @@ class LocalSearch {
std::array<unsigned, OperatorName::MAX> applied_moves;
#endif

void try_job_additions(const std::vector<Index>& routes, double regret_coeff);
#ifdef LOG_LS
std::vector<log::Step<Route>> steps;
#endif

void try_job_additions(const std::vector<Index>& routes,
double regret_coeff
#ifdef LOG_LS
,
bool log_addition_step = false
#endif
);

void run_ls_step();

Expand All @@ -78,7 +92,7 @@ class LocalSearch {
public:
LocalSearch(const Input& input,
std::vector<Route>& tw_sol,
unsigned max_nb_jobs_removal,
unsigned depth,
const Timeout& timeout);

utils::SolutionIndicators<Route> indicators() const;
Expand All @@ -88,6 +102,12 @@ class LocalSearch {
#ifdef LOG_LS_OPERATORS
std::array<OperatorStats, OperatorName::MAX> get_stats() const;
#endif

#ifdef LOG_LS
std::vector<log::Step<Route>> get_steps() {
return steps;
}
#endif
};

} // namespace vroom::ls
Expand Down
42 changes: 42 additions & 0 deletions src/algorithms/local_search/log_local_search.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef LOG_LOCAL_SEARCH_H
#define LOG_LOCAL_SEARCH_H

/*

This file is part of VROOM.

Copyright (c) 2015-2024, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include "structures/vroom/solution/solution.h"
#include "structures/vroom/solution_indicators.h"

namespace vroom::ls::log {

enum class EVENT {
START,
OPERATOR,
LOCAL_MINIMA,
JOB_ADDITION,
RUIN,
RECREATE,
ROLLBACK
};

template <class Route> struct Step {
TimePoint time_point;
EVENT event;
OperatorName operator_name;
vroom::utils::SolutionIndicators<Route> indicators;
std::optional<Solution> solution;
};

template <class Route> struct Dump {
HeuristicParameters heuristic_parameters;
std::vector<Step<Route>> steps;
};
} // namespace vroom::ls::log

#endif
27 changes: 22 additions & 5 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ int main(int argc, char** argv) {
std::string limit_arg;
std::string output_file;
std::vector<std::string> heuristic_params_arg;
unsigned exploration_level;

cxxopts::Options options("vroom",
"VROOM Copyright (C) 2015-2024, Julien Coupey\n"
Expand Down Expand Up @@ -79,19 +80,28 @@ int main(int argc, char** argv) {
("v,version", "output version information and exit")
("x,explore",
"exploration level to use (0..5)",
cxxopts::value<unsigned>(cl_args.exploration_level)->default_value(std::to_string(vroom::DEFAULT_EXPLORATION_LEVEL)))
cxxopts::value<unsigned>(exploration_level)->default_value(std::to_string(vroom::DEFAULT_EXPLORATION_LEVEL)))
("stdin",
"optional input positional arg",
cxxopts::value<std::string>(cl_args.input));

// we don't want to print debug args on --help
std::optional<unsigned> debug_depth;
std::optional<unsigned> debug_nb_searches;

options.add_options("debug_group")
("e,heuristic-param",
"Heuristic parameter",
cxxopts::value<std::vector<std::string>>(heuristic_params_arg))
("f,apply-tsp-fix",
"apply experimental TSPFix local search operator",
cxxopts::value<bool>(cl_args.apply_TSPFix)->default_value("false"));
cxxopts::value<bool>(cl_args.apply_TSPFix)->default_value("false"))
("d,depth",
"search depth",
cxxopts::value<std::optional<unsigned>>(debug_depth))
("s,nb-searches",
"number of searches to perform in parallel",
cxxopts::value<std::optional<unsigned>>(debug_nb_searches));

// clang-format on
try {
Expand Down Expand Up @@ -153,8 +163,14 @@ int main(int argc, char** argv) {
for (const auto& port : port_args) {
vroom::io::update_port(cl_args.servers, port);
}
cl_args.exploration_level =
std::min(cl_args.exploration_level, vroom::MAX_EXPLORATION_LEVEL);
exploration_level = std::min(exploration_level, vroom::MAX_EXPLORATION_LEVEL);
vroom::io::set_exploration_level(cl_args, exploration_level);
if (debug_depth) {
cl_args.depth = debug_depth.value();
}
if (debug_nb_searches) {
cl_args.nb_searches = debug_nb_searches.value();
}

// Determine routing engine (defaults to ROUTER::OSRM).
if (router_arg == "libosrm") {
Expand Down Expand Up @@ -220,7 +236,8 @@ int main(int argc, char** argv) {

vroom::Solution sol = (cl_args.check)
? problem_instance.check(cl_args.nb_threads)
: problem_instance.solve(cl_args.exploration_level,
: problem_instance.solve(cl_args.nb_searches,
cl_args.depth,
cl_args.nb_threads,
cl_args.timeout,
cl_args.h_params);
Expand Down
2 changes: 1 addition & 1 deletion src/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Variables.
CXX ?= g++
USE_ROUTING ?= true
CXXFLAGS = -MMD -MP -I. -std=c++20 -Wextra -Wpedantic -Wall -O3 -DASIO_STANDALONE -DUSE_ROUTING=$(USE_ROUTING)
CXXFLAGS = -MMD -MP -I. -std=c++20 -Wextra -Wpedantic -Wall -O3 -DASIO_STANDALONE -DUSE_ROUTING=$(USE_ROUTING) -DLOG_LS
LDLIBS = -lpthread

# Using all cpp files in current directory.
Expand Down
6 changes: 4 additions & 2 deletions src/problems/cvrp/cvrp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ const std::vector<HeuristicParameters> CVRP::heterogeneous_parameters =
CVRP::CVRP(const Input& input) : VRP(input) {
}

Solution CVRP::solve(unsigned exploration_level,
Solution CVRP::solve(unsigned nb_searches,
unsigned depth,
unsigned nb_threads,
const Timeout& timeout,
const std::vector<HeuristicParameters>& h_param) const {
Expand All @@ -164,7 +165,8 @@ Solution CVRP::solve(unsigned exploration_level,
return utils::format_solution(_input, {r});
}

return VRP::solve<RawRoute, cvrp::LocalSearch>(exploration_level,
return VRP::solve<RawRoute, cvrp::LocalSearch>(nb_searches,
depth,
nb_threads,
timeout,
h_param,
Expand Down
3 changes: 2 additions & 1 deletion src/problems/cvrp/cvrp.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class CVRP : public VRP {
explicit CVRP(const Input& input);

Solution
solve(unsigned exploration_level,
solve(unsigned nb_searches,
unsigned depth,
unsigned nb_threads,
const Timeout& timeout,
const std::vector<HeuristicParameters>& h_param) const override;
Expand Down
Loading