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

Add internal 2 opt #707

Merged
merged 13 commits into from
May 12, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Added

- `IntraTwoOpt` local search operator (#706)
- `description` key for unassigned tasks in output, if provided (#403)
- `location_index` key for unassigned tasks and each step, if provided (#625)
- Shared target to makefile, for creating Position Independent Code (#617)
Expand Down
56 changes: 56 additions & 0 deletions src/algorithms/local_search/local_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All rights reserved (see LICENSE).
#include "problems/vrptw/operators/intra_mixed_exchange.h"
#include "problems/vrptw/operators/intra_or_opt.h"
#include "problems/vrptw/operators/intra_relocate.h"
#include "problems/vrptw/operators/intra_two_opt.h"
#include "problems/vrptw/operators/mixed_exchange.h"
#include "problems/vrptw/operators/or_opt.h"
#include "problems/vrptw/operators/pd_shift.h"
Expand Down Expand Up @@ -43,6 +44,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
LocalSearch<Route,
Expand All @@ -59,6 +61,7 @@ LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::LocalSearch(const Input& input,
std::vector<Route>& sol,
Expand Down Expand Up @@ -145,6 +148,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
void LocalSearch<Route,
Expand All @@ -161,6 +165,7 @@ void LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::try_job_additions(const std::vector<Index>&
routes,
Expand Down Expand Up @@ -320,6 +325,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
void LocalSearch<Route,
Expand All @@ -336,6 +342,7 @@ void LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::run_ls_step() {
// Store best move involving a pair of routes.
Expand Down Expand Up @@ -1318,6 +1325,39 @@ void LocalSearch<Route,
}
}

// IntraTwoOpt stuff
for (const auto& s_t : s_t_pairs) {
if (s_t.first != s_t.second or best_priorities[s_t.first] > 0 or
_sol[s_t.first].size() < 4) {
continue;
}
for (unsigned s_rank = 0; s_rank < _sol[s_t.first].size() - 2; ++s_rank) {
const auto s_job_rank = _sol[s_t.first].route[s_rank];
const auto end_s =
_sol_state.weak_insertion_ranks_end[s_t.first][s_job_rank];
assert(end_s != 0);
auto end_t_rank = std::min(static_cast<Index>(_sol[s_t.first].size()),
static_cast<Index>(end_s - 1));

for (unsigned t_rank = s_rank + 2; t_rank < end_t_rank; ++t_rank) {
#ifdef LOG_LS_OPERATORS
++tried_moves[OperatorName::IntraTwoOpt];
#endif
IntraTwoOpt r(_input,
_sol_state,
_sol[s_t.first],
s_t.first,
s_rank,
t_rank);
auto& current_best = best_gains[s_t.first][s_t.second];
if (r.gain() > current_best and r.is_valid()) {
current_best = r.gain();
best_ops[s_t.first][s_t.first] = std::make_unique<IntraTwoOpt>(r);
}
}
}
}

if (_input.has_shipments()) {
// Move(s) that don't make sense for job-only instances.

Expand Down Expand Up @@ -1590,6 +1630,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
void LocalSearch<Route,
Expand All @@ -1606,6 +1647,7 @@ void LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::run() {
bool try_ls_step = true;
Expand Down Expand Up @@ -1707,6 +1749,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
std::array<OperatorStats, OperatorName::MAX>
Expand All @@ -1724,6 +1767,7 @@ LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::get_stats() const {
std::array<OperatorStats, OperatorName::MAX> stats;
Expand All @@ -1749,6 +1793,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
Gain LocalSearch<Route,
Expand All @@ -1765,6 +1810,7 @@ Gain LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::job_route_cost(Index v_target,
Index v,
Expand Down Expand Up @@ -1818,6 +1864,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
Gain LocalSearch<Route,
Expand All @@ -1834,6 +1881,7 @@ Gain LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::relocate_cost_lower_bound(Index v, Index r) {
Gain best_bound = static_cast<Gain>(INFINITE_COST);
Expand Down Expand Up @@ -1864,6 +1912,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
Gain LocalSearch<Route,
Expand All @@ -1880,6 +1929,7 @@ Gain LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::relocate_cost_lower_bound(Index v,
Index r1,
Expand Down Expand Up @@ -1914,6 +1964,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
void LocalSearch<Route,
Expand All @@ -1930,6 +1981,7 @@ void LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::remove_from_routes() {
// Store nearest job from and to any job in any route for constant
Expand Down Expand Up @@ -2053,6 +2105,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
utils::SolutionIndicators LocalSearch<Route,
Expand All @@ -2069,6 +2122,7 @@ utils::SolutionIndicators LocalSearch<Route,
IntraMixedExchange,
IntraRelocate,
IntraOrOpt,
IntraTwoOpt,
PDShift,
RouteExchange>::indicators() const {
return _best_sol_indicators;
Expand All @@ -2088,6 +2142,7 @@ template class LocalSearch<TWRoute,
vrptw::IntraMixedExchange,
vrptw::IntraRelocate,
vrptw::IntraOrOpt,
vrptw::IntraTwoOpt,
vrptw::PDShift,
vrptw::RouteExchange>;

Expand All @@ -2105,6 +2160,7 @@ template class LocalSearch<RawRoute,
cvrp::IntraMixedExchange,
cvrp::IntraRelocate,
cvrp::IntraOrOpt,
cvrp::IntraTwoOpt,
cvrp::PDShift,
cvrp::RouteExchange>;

Expand Down
1 change: 1 addition & 0 deletions src/algorithms/local_search/local_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ template <class Route,
class IntraMixedExchange,
class IntraRelocate,
class IntraOrOpt,
class IntraTwoOpt,
class PDShift,
class RouteExchange>
class LocalSearch {
Expand Down
2 changes: 2 additions & 0 deletions src/problems/cvrp/cvrp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All rights reserved (see LICENSE).
#include "problems/cvrp/operators/intra_mixed_exchange.h"
#include "problems/cvrp/operators/intra_or_opt.h"
#include "problems/cvrp/operators/intra_relocate.h"
#include "problems/cvrp/operators/intra_two_opt.h"
#include "problems/cvrp/operators/mixed_exchange.h"
#include "problems/cvrp/operators/or_opt.h"
#include "problems/cvrp/operators/pd_shift.h"
Expand Down Expand Up @@ -51,6 +52,7 @@ using LocalSearch = ls::LocalSearch<RawRoute,
cvrp::IntraMixedExchange,
cvrp::IntraRelocate,
cvrp::IntraOrOpt,
cvrp::IntraTwoOpt,
cvrp::PDShift,
cvrp::RouteExchange>;
} // namespace cvrp
Expand Down
135 changes: 135 additions & 0 deletions src/problems/cvrp/operators/intra_two_opt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*

This file is part of VROOM.

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

*/

#include <algorithm>

#include "problems/cvrp/operators/intra_two_opt.h"

namespace vroom {
namespace cvrp {

IntraTwoOpt::IntraTwoOpt(const Input& input,
const utils::SolutionState& sol_state,
RawRoute& s_route,
Index s_vehicle,
Index s_rank,
Index t_rank)
: Operator(OperatorName::IntraTwoOpt,
input,
sol_state,
s_route,
s_vehicle,
s_rank,
s_route,
s_vehicle,
t_rank) {
// Assume s_rank < t_rank for symmetry reasons. Set aside cases
// where t_rank = s_rank + 1, as the move is also an intra_relocate.
assert(s_route.size() >= 3);
assert(s_rank < t_rank - 1);
assert(t_rank < s_route.size());
}

void IntraTwoOpt::compute_gain() {
const auto& s_v = _input.vehicles[s_vehicle];

Index s_index = _input.jobs[s_route[s_rank]].index();
Index t_index = _input.jobs[t_route[t_rank]].index();
stored_gain = 0;

// Cost of reversing vehicle route between s_rank and t_rank
// included.
stored_gain += _sol_state.fwd_costs[s_vehicle][s_vehicle][t_rank];
stored_gain -= _sol_state.fwd_costs[s_vehicle][s_vehicle][s_rank];
stored_gain += _sol_state.bwd_costs[s_vehicle][s_vehicle][s_rank];
stored_gain -= _sol_state.bwd_costs[s_vehicle][s_vehicle][t_rank];

// Cost of going to t_rank first instead of s_rank.
if (s_rank > 0) {
Index previous_index = _input.jobs[s_route[s_rank - 1]].index();
stored_gain += s_v.cost(previous_index, s_index);
stored_gain -= s_v.cost(previous_index, t_index);
} else {
if (s_v.has_start()) {
Index start_index = s_v.start.value().index();
stored_gain += s_v.cost(start_index, s_index);
stored_gain -= s_v.cost(start_index, t_index);
}
}

// Cost of going from s_rank after instead of t_rank.
if (t_rank < s_route.size() - 1) {
Index next_index = _input.jobs[s_route[t_rank + 1]].index();
stored_gain += s_v.cost(t_index, next_index);
stored_gain -= s_v.cost(s_index, next_index);
} else {
if (s_v.has_end()) {
Index end_index = s_v.end.value().index();
stored_gain += s_v.cost(t_index, end_index);
stored_gain -= s_v.cost(s_index, end_index);
}
}

gain_computed = true;
}

bool IntraTwoOpt::reversal_ok_for_shipments() const {
bool valid = true;
Index current = s_rank;

while (valid and current < t_rank) {
const auto& job = _input.jobs[s_route[current]];
valid = (job.type != JOB_TYPE::PICKUP) or
(_sol_state.matching_delivery_rank[s_vehicle][current] > t_rank);

++current;
}

return valid;
}

bool IntraTwoOpt::is_valid() {
bool valid = !_input.has_shipments() or reversal_ok_for_shipments();

if (valid) {
auto rev_t = s_route.rbegin() + (s_route.size() - t_rank - 1);
auto rev_s_next = s_route.rbegin() + (s_route.size() - s_rank);

valid =
source
.is_valid_addition_for_capacity_inclusion(_input,
source
.delivery_in_range(s_rank,
t_rank +
1),
rev_t,
rev_s_next,
s_rank,
t_rank + 1);
}

return valid;
}

void IntraTwoOpt::apply() {
std::reverse(s_route.begin() + s_rank, s_route.begin() + t_rank + 1);

source.update_amounts(_input);
}

std::vector<Index> IntraTwoOpt::addition_candidates() const {
return {};
}

std::vector<Index> IntraTwoOpt::update_candidates() const {
return {s_vehicle};
}

} // namespace cvrp
} // namespace vroom
Loading