Submitted vehicle_routing/simple_ls_zero

This commit is contained in:
FiveMovesAhead 2025-10-17 12:10:46 +01:00
parent a2fbd8c435
commit 0cf783ead5
3 changed files with 1013 additions and 1 deletions

View File

@ -152,7 +152,8 @@
// c002_a077
// c002_a078
pub mod simple_ls_zero;
pub use simple_ls_zero as c002_a078;
// c002_a079

View File

@ -0,0 +1,23 @@
# TIG Code Submission
## Submission Details
* **Challenge Name:** vehicle_routing
* **Submission Name:** simple_ls_zero
* **Copyright:** 2025 Thibaut Vidal, Rafael Martinelli
* **Identity of Submitter:** Thibaut Vidal
* **Identity of Creator of Algorithmic Method:** null
* **Unique Algorithm Identifier (UAI):** null
## License
The files in this folder are under the following licenses:
* TIG Benchmarker Outbound License
* TIG Commercial License
* TIG Inbound Game License
* TIG Innovator Outbound Game License
* TIG Open Data License
* TIG THV Game License
Copies of the licenses can be obtained at:
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses

View File

@ -0,0 +1,988 @@
/*
REFERENCES AND ACKNOWLEDGMENTS
This implementation is based on or inspired by existing work.
This is a Rust re-implementation of the local search component of the HGS algorithm,
adapted to the VRP with time windows:
Vidal, T., Crainic, T. G., Gendreau, M., Lahrichi, N., & Rei, W. (2012).
A hybrid genetic algorithm for multidepot and periodic vehicle routing problems.
Operations Research, 60(3), 611624.
https://doi.org/10.1287/opre.1120.1048
Vidal, T. (2022).
Hybrid genetic search for the CVRP: Open-source implementation and SWAP* neighborhood.
Computers and Operations Research, 140, 105643.
https://doi.org/10.1016/j.cor.2021.105643
*/
use serde_json::{Map, Value};
use std::cmp::{max, min};
use std::collections::BTreeSet;
use std::mem::replace;
use std::mem::take;
use tig_challenges::vehicle_routing::*;
pub struct MoveArgs {
pub route1: usize,
pub pos1: usize,
pub route2: usize,
pub pos2: usize,
pub profit: isize,
}
impl MoveArgs {
pub fn new(route1: usize, pos1: usize, route2: usize, pos2: usize) -> Self {
Self {
route1,
pos1,
route2,
pos2,
profit: 0,
}
}
}
pub struct Parameters {
pub file_name: String,
pub seed: [u8; 32],
pub initial_penalty: isize,
pub granularity: usize,
}
pub struct Data {
pub seed: [u8; 32],
pub nb_nodes: usize,
pub nb_routes: usize,
pub demands: Vec<isize>,
pub max_capacity: isize,
pub distance_matrix: Vec<Vec<isize>>,
pub service_times: Vec<isize>,
pub start_tw: Vec<isize>,
pub end_tw: Vec<isize>,
pub penalty_capacity: isize,
pub penalty_tw: isize,
pub granularity: usize,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Sequence {
pub tau_minus: isize,
pub tau_plus: isize,
pub tmin: isize,
pub tw: isize,
pub total_service_duration: isize,
pub load: isize,
pub distance: isize,
pub first_node: usize,
pub last_node: usize,
}
#[inline(always)]
fn penalty(x: isize, coef: isize) -> isize {
if x > 0 {
x * coef
} else {
0
}
}
#[inline(always)]
fn dm(dm: &[Vec<isize>], i: usize, j: usize) -> isize {
dm[i][j]
}
impl Sequence {
#[inline(always)]
pub fn initialize(&mut self, data: &Data, node: usize) {
let st = data.start_tw[node];
let et = data.end_tw[node];
let svc = data.service_times[node];
let ld = data.demands[node];
self.tau_minus = st;
self.tau_plus = et;
self.tmin = svc;
self.tw = 0;
self.total_service_duration = svc;
self.load = ld;
self.distance = 0;
self.first_node = node;
self.last_node = node;
}
#[inline(always)]
pub fn join2(data: &Data, s1: &Sequence, s2: &Sequence) -> Sequence {
let travel = dm(&data.distance_matrix, s1.last_node, s2.first_node);
let distance = s1.distance + s2.distance + travel;
let temp = travel + s1.tmin - s1.tw;
let wtij = max(s2.tau_minus - temp - s1.tau_plus, 0);
let twij = max(temp + s1.tau_minus - s2.tau_plus, 0);
let tw = s1.tw + s2.tw + twij;
let tmin = temp + s1.tw + s2.tmin + wtij;
let tau_minus = max(s2.tau_minus - temp - wtij, s1.tau_minus);
let tau_plus = min(s2.tau_plus - temp + twij, s1.tau_plus);
let load = s1.load + s2.load;
Sequence {
tau_minus,
tau_plus,
tmin,
tw,
total_service_duration: s1.total_service_duration + s2.total_service_duration,
load,
distance,
first_node: s1.first_node,
last_node: s2.last_node,
}
}
#[inline(always)]
pub fn singleton(data: &Data, node: usize) -> Sequence {
let mut s = Sequence::default();
s.initialize(data, node);
s
}
#[inline(always)]
pub fn eval(&self, data: &Data) -> isize {
self.distance
+ penalty(self.load - data.max_capacity, data.penalty_capacity)
+ penalty(self.tw, data.penalty_tw)
}
#[inline(always)]
pub fn eval2(data: &Data, s1: &Sequence, s2: &Sequence) -> isize {
let travel = dm(&data.distance_matrix, s1.last_node, s2.first_node);
let distance = s1.distance + s2.distance + travel;
let temp = s1.tmin - s1.tw + travel;
let tw_viol = s1.tw + s2.tw + max(s1.tau_minus - s2.tau_plus + temp, 0);
let load = s1.load + s2.load;
distance
+ penalty(load - data.max_capacity, data.penalty_capacity)
+ penalty(tw_viol, data.penalty_tw)
}
#[inline(always)]
pub fn eval3(data: &Data, s1: &Sequence, s2: &Sequence, s3: &Sequence) -> isize {
let travel12 = dm(&data.distance_matrix, s1.last_node, s2.first_node);
let distance12 = s1.distance + s2.distance + travel12;
let mut temp = travel12 + s1.tmin - s1.tw;
let wtij = max(s2.tau_minus - temp - s1.tau_plus, 0);
let twij = max(temp + s1.tau_minus - s2.tau_plus, 0);
let tw_viol12 = s1.tw + s2.tw + twij;
let tmin12 = temp + s1.tw + s2.tmin + wtij;
let tau_m12 = max(s2.tau_minus - temp - wtij, s1.tau_minus);
let tau_p12 = min(s2.tau_plus - temp + twij, s1.tau_plus);
let travel23 = dm(&data.distance_matrix, s2.last_node, s3.first_node);
let distance = distance12 + s3.distance + travel23;
temp = travel23 + tmin12 - tw_viol12;
let tw_viol = tw_viol12 + s3.tw + max(tau_m12 - s3.tau_plus + temp, 0);
let _tmin123 = tw_viol12 + s3.tmin + max(s3.tau_minus - tau_p12, temp);
let load = s1.load + s2.load + s3.load;
distance
+ penalty(load - data.max_capacity, data.penalty_capacity)
+ penalty(tw_viol, data.penalty_tw)
}
#[inline(always)]
pub fn eval_n(data: &Data, chain: &[Sequence]) -> isize {
debug_assert!(chain.len() >= 3);
let mut agg = chain[0];
for s in &chain[1..chain.len() - 1] {
agg = Sequence::join2(data, &agg, s);
}
let last = &chain[chain.len() - 1];
Sequence::eval2(data, &agg, last)
}
}
pub struct TigLoader;
impl TigLoader {
pub fn load(params: Parameters, challenge: &Challenge) -> Data {
Data {
seed: challenge.seed,
nb_nodes: challenge.difficulty.num_nodes,
nb_routes: challenge.difficulty.num_nodes,
demands: challenge.demands.iter().map(|&d| d as isize).collect(),
max_capacity: challenge.max_capacity as isize,
distance_matrix: challenge
.distance_matrix
.iter()
.map(|row| row.iter().map(|&x| x as isize).collect())
.collect(),
service_times: std::iter::once(0)
.chain(
std::iter::repeat(challenge.service_time as isize)
.take(challenge.difficulty.num_nodes),
)
.collect(),
start_tw: challenge.ready_times.iter().map(|&d| d as isize).collect(),
end_tw: challenge.due_times.iter().map(|&d| d as isize).collect(),
penalty_capacity: params.initial_penalty,
penalty_tw: params.initial_penalty,
granularity: params.granularity,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Node {
pub id: usize,
pub seq0_i: Sequence,
pub seqi_n: Sequence,
pub seq1: Sequence,
pub seq12: Sequence,
pub seq21: Sequence,
}
impl Node {
pub fn new(id: usize) -> Self {
Self {
id,
seq0_i: Sequence::default(),
seqi_n: Sequence::default(),
seq1: Sequence::default(),
seq12: Sequence::default(),
seq21: Sequence::default(),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Route {
pub cost: isize,
pub load: isize,
pub tw: isize,
pub nodes: Vec<Node>,
}
impl Route {
pub fn new(data: &Data, node_ids: &Vec<usize>) -> Self {
let mut route = Route {
cost: 0,
load: 0,
tw: 0,
nodes: Vec::new(),
};
for &node_id in node_ids {
route.nodes.push(Node::new(node_id));
}
route.update_metrics(data);
route
}
pub fn update_metrics(&mut self, data: &Data) {
self.cost = 0;
self.load = 0;
self.tw = 0;
let mut current_time = 0;
for i in 0..self.nodes.len() - 1 {
let from = self.nodes[i].id;
let to = self.nodes[i + 1].id;
self.cost += data.distance_matrix[from][to];
if to != 0 {
self.load += data.demands[to];
}
current_time += data.distance_matrix[from][to];
if current_time < data.start_tw[to] {
current_time = data.start_tw[to];
}
if current_time > data.end_tw[to] {
self.tw += current_time - data.end_tw[to];
current_time = data.end_tw[to];
}
current_time += data.service_times[to];
}
}
pub fn preprocess(&mut self, data: &Data) {
let len = self.nodes.len();
let mut acc_fwd = Sequence::singleton(data, self.nodes[0].id);
self.nodes[0].seq0_i = acc_fwd;
for pos in 1..len {
let id = self.nodes[pos].id;
acc_fwd = Sequence::join2(data, &acc_fwd, &Sequence::singleton(data, id));
self.nodes[pos].seq0_i = acc_fwd;
}
let mut acc_bwd = Sequence::singleton(data, self.nodes[len - 1].id);
self.nodes[len - 1].seqi_n = acc_bwd;
for pos in (0..len - 1).rev() {
let id = self.nodes[pos].id;
acc_bwd = Sequence::join2(data, &Sequence::singleton(data, id), &acc_bwd);
self.nodes[pos].seqi_n = acc_bwd;
}
for pos in 0..len {
let id = self.nodes[pos].id;
self.nodes[pos].seq1 = Sequence::singleton(data, id);
if pos + 1 < len {
let id_next = self.nodes[pos + 1].id;
self.nodes[pos].seq12 = Sequence::join2(
data,
&Sequence::singleton(data, id),
&Sequence::singleton(data, id_next),
);
self.nodes[pos].seq21 = Sequence::join2(
data,
&Sequence::singleton(data, id_next),
&Sequence::singleton(data, id),
);
}
}
let end = self.nodes[len - 1].seq0_i;
self.load = end.load;
self.tw = end.tw;
self.cost = end.eval(data);
}
pub fn to_string(&self) -> String {
let mut result = String::from("[ ");
for node in &self.nodes {
result.push_str(&format!("{} ", node.id));
}
result.push_str(&format!(
"] (Cost: {}, Load: {}, TW: {})",
self.cost, self.load, self.tw
));
result
}
}
#[derive(Debug, Clone)]
pub struct Individual {
pub cost: isize,
pub routes: Vec<Route>,
pub node_route: Vec<usize>,
pub node_pos: Vec<usize>,
}
impl Individual {
pub fn new(data: &Data, routes: Vec<Vec<usize>>) -> Self {
let mut ind = Individual {
cost: 0,
routes: routes.iter().map(|r| Route::new(data, r)).collect(),
node_route: vec![0; data.nb_nodes],
node_pos: vec![0; data.nb_nodes],
};
ind.cost = ind.routes.iter().map(|r| r.cost).sum();
ind.update_node_mappings();
ind
}
pub fn update_route_node_mappings(&mut self, route_id: usize) {
for (pos, node) in self.routes[route_id].nodes.iter().enumerate() {
self.node_route[node.id] = route_id;
self.node_pos[node.id] = pos;
}
}
pub fn update_node_mappings(&mut self) {
for route_id in 0..self.routes.len() {
self.update_route_node_mappings(route_id);
}
}
}
pub struct Swap;
impl Swap {
#[inline(always)]
pub fn evaluate(data: &Data, ind: &Individual, args: &mut MoveArgs) {
let r1 = args.route1;
let r2 = args.route2;
let p1 = args.pos1;
let p2 = args.pos2;
let new1 = Sequence::eval3(
data,
&ind.routes[r1].nodes[p1 - 1].seq0_i,
&ind.routes[r2].nodes[p2].seq1,
&ind.routes[r1].nodes[p1 + 1].seqi_n,
);
let new2 = Sequence::eval3(
data,
&ind.routes[r2].nodes[p2 - 1].seq0_i,
&ind.routes[r1].nodes[p1].seq1,
&ind.routes[r2].nodes[p2 + 1].seqi_n,
);
let old_cost = ind.routes[r1].cost + ind.routes[r2].cost;
let new_cost = new1 + new2;
args.profit = old_cost - new_cost;
}
pub fn perform(data: &Data, ind: &mut Individual, args: &MoveArgs) {
let temp = take(&mut ind.routes[args.route1].nodes[args.pos1]);
ind.routes[args.route1].nodes[args.pos1] =
replace(&mut ind.routes[args.route2].nodes[args.pos2], temp);
ind.routes[args.route1].preprocess(&data);
ind.update_route_node_mappings(args.route1);
ind.routes[args.route2].preprocess(&data);
ind.update_route_node_mappings(args.route2);
ind.cost -= args.profit;
}
}
pub struct Constructive;
impl Constructive {
pub fn build_individual(data: &Data) -> Individual {
let mut routes = Vec::new();
let mut nodes: Vec<usize> = (1..data.nb_nodes).collect();
nodes.sort_by(|&a, &b| data.distance_matrix[0][a].cmp(&data.distance_matrix[0][b]));
let mut remaining: BTreeSet<usize> = nodes.iter().cloned().collect();
while let Some(node) = nodes.pop() {
if !remaining.remove(&node) {
continue;
}
let mut route = vec![0, node, 0];
let mut route_demand = data.demands[node];
while let Some((best_node, best_pos)) = Self::find_best_insertion(
&route,
remaining
.iter()
.cloned()
.filter(|&n| route_demand + data.demands[n] <= data.max_capacity)
.collect(),
data,
) {
remaining.remove(&best_node);
route_demand += data.demands[best_node];
route.insert(best_pos, best_node);
}
routes.push(route);
}
Individual::new(data, routes)
}
fn find_best_insertion(
route: &Vec<usize>,
remaining_nodes: Vec<usize>,
data: &Data,
) -> Option<(usize, usize)> {
let alpha1 = 1;
let alpha2 = 0;
let lambda = 1;
let mut best_c2 = None;
let mut best = None;
for insert_node in remaining_nodes {
let mut best_c1 = None;
let mut curr_time = 0;
let mut curr_node = 0;
for pos in 1..route.len() {
let next_node = route[pos];
let new_arrival_time = data.start_tw[insert_node]
.max(curr_time + data.distance_matrix[curr_node][insert_node]);
if new_arrival_time > data.end_tw[insert_node] {
continue;
}
let old_arrival_time = data.start_tw[next_node]
.max(curr_time + data.distance_matrix[curr_node][next_node]);
let c11 = data.distance_matrix[curr_node][insert_node]
+ data.distance_matrix[insert_node][next_node]
- data.distance_matrix[curr_node][next_node];
let c12 = new_arrival_time - old_arrival_time;
let c1 = -(alpha1 * c11 + alpha2 * c12);
let c2 = lambda * data.distance_matrix[0][insert_node] + c1;
let c1_is_better = match best_c1 {
None => true,
Some(x) => c1 > x,
};
let c2_is_better = match best_c2 {
None => true,
Some(x) => c2 > x,
};
if c1_is_better
&& c2_is_better
&& Self::is_feasible(
route,
insert_node,
new_arrival_time + data.service_times[next_node],
pos,
data,
)
{
best_c1 = Some(c1);
best_c2 = Some(c2);
best = Some((insert_node, pos));
}
curr_time = data.start_tw[next_node]
.max(curr_time + data.distance_matrix[curr_node][next_node])
+ data.service_times[next_node];
curr_node = next_node;
}
}
best
}
fn is_feasible(
route: &Vec<usize>,
mut curr_node: usize,
mut curr_time: isize,
start_pos: usize,
data: &Data,
) -> bool {
let mut valid = true;
for pos in start_pos..route.len() {
let next_node = route[pos];
curr_time += data.distance_matrix[curr_node][next_node];
if curr_time > data.end_tw[route[pos]] {
valid = false;
break;
}
curr_time = curr_time.max(data.start_tw[next_node]) + data.service_times[next_node];
curr_node = next_node;
}
valid
}
}
pub struct Relocate;
impl Relocate {
#[inline(always)]
pub fn evaluate(data: &Data, ind: &Individual, args: &mut MoveArgs) {
let r1 = args.route1;
let r2 = args.route2;
let p1 = args.pos1;
let p2 = args.pos2;
let new1 = Sequence::eval2(
data,
&ind.routes[r1].nodes[p1 - 1].seq0_i,
&ind.routes[r1].nodes[p1 + 1].seqi_n,
);
let new2 = Sequence::eval3(
data,
&ind.routes[r2].nodes[p2 - 1].seq0_i,
&ind.routes[r1].nodes[p1].seq1,
&ind.routes[r2].nodes[p2].seqi_n,
);
let old_cost = ind.routes[r1].cost + ind.routes[r2].cost;
let new_cost = new1 + new2;
args.profit = old_cost - new_cost;
}
pub fn perform(data: &Data, ind: &mut Individual, args: &MoveArgs) {
let element = ind.routes[args.route1].nodes.remove(args.pos1);
ind.routes[args.route2].nodes.insert(args.pos2, element);
ind.routes[args.route1].preprocess(&data);
ind.update_route_node_mappings(args.route1);
ind.routes[args.route2].preprocess(&data);
ind.update_route_node_mappings(args.route2);
ind.cost -= args.profit;
}
}
pub struct TwoOptStar;
impl TwoOptStar {
#[inline(always)]
pub fn evaluate(data: &Data, ind: &Individual, args: &mut MoveArgs) {
let r1 = args.route1;
let r2 = args.route2;
let p1 = args.pos1;
let p2 = args.pos2;
let new1 = Sequence::eval2(
data,
&ind.routes[r1].nodes[p1 - 1].seq0_i,
&ind.routes[r2].nodes[p2].seqi_n,
);
let new2 = Sequence::eval2(
data,
&ind.routes[r2].nodes[p2 - 1].seq0_i,
&ind.routes[r1].nodes[p1].seqi_n,
);
let old_cost = ind.routes[r1].cost + ind.routes[r2].cost;
let new_cost = new1 + new2;
args.profit = old_cost - new_cost;
}
pub fn perform(data: &Data, ind: &mut Individual, args: &MoveArgs) {
let mut suffix1 = ind.routes[args.route1].nodes.split_off(args.pos1);
let mut suffix2 = ind.routes[args.route2].nodes.split_off(args.pos2);
ind.routes[args.route1].nodes.append(&mut suffix2);
ind.routes[args.route2].nodes.append(&mut suffix1);
ind.routes[args.route1].preprocess(&data);
ind.update_route_node_mappings(args.route1);
ind.routes[args.route2].preprocess(&data);
ind.update_route_node_mappings(args.route2);
ind.cost -= args.profit;
}
}
pub struct LocalSearch<'a> {
pub data: &'a Data,
pub neighbors_before: Vec<Vec<usize>>,
}
impl<'a> LocalSearch<'a> {
pub fn new(data: &'a Data) -> Self {
let mut neighbors_before: Vec<Vec<usize>> = vec![Vec::new(); data.nb_nodes];
for i in 1..data.nb_nodes {
let mut prox: Vec<(isize, usize)> = Vec::with_capacity(data.nb_nodes - 2);
for j in 1..data.nb_nodes {
if j == i {
continue;
}
let tji = data.distance_matrix[j][i];
let wait = (data.start_tw[i] - tji - data.service_times[j] - data.end_tw[j]).max(0);
let late = (data.start_tw[j] + data.service_times[j] + tji - data.end_tw[i]).max(0);
let proxy10 = 10 * tji + 2 * wait + 10 * late;
prox.push((proxy10, j));
}
prox.sort_by_key(|&(p, _)| p);
let keep = min(data.granularity, data.nb_nodes - 2);
neighbors_before[i] = prox[..keep].iter().map(|&(_, j)| j).collect();
}
Self {
data,
neighbors_before,
}
}
pub fn run_intra_route_relocate(
&mut self,
ind: &mut Individual,
r1: usize,
pos1: usize,
) -> bool {
let route = &ind.routes[r1];
let len = route.nodes.len();
if len < pos1 + 4 {
return false;
}
debug_assert!(pos1 > 0);
debug_assert!(ind.routes[r1].nodes[pos1].id != 0);
let mut left_excl: Vec<Sequence> = vec![Sequence::default(); len];
let mut acc_left = route.nodes[0].seq0_i;
for p in 1..len {
left_excl[p] = acc_left;
if p != pos1 {
acc_left = Sequence::join2(self.data, &acc_left, &route.nodes[p].seq1);
}
}
let mut right_excl: Vec<Sequence> = vec![Sequence::default(); len];
let mut acc_right = route.nodes[len - 1].seq1;
right_excl[len - 1] = acc_right;
for p in (1..len - 1).rev() {
if p != pos1 {
acc_right = Sequence::join2(self.data, &route.nodes[p].seq1, &acc_right);
}
right_excl[p] = acc_right;
}
let old_cost = route.cost;
let mut best_cost = old_cost;
let mut best_pos: Option<usize> = None;
for t in 1..len {
if t == pos1 || t == pos1 + 1 {
continue;
}
let new_cost = Sequence::eval3(
self.data,
&left_excl[t],
&route.nodes[pos1].seq1,
&right_excl[t],
);
if new_cost < best_cost {
best_cost = new_cost;
best_pos = Some(t);
}
}
if let Some(mypos) = best_pos {
let insert_pos = if mypos > pos1 { mypos - 1 } else { mypos };
let elem = ind.routes[r1].nodes.remove(pos1);
ind.routes[r1].nodes.insert(insert_pos, elem);
ind.routes[r1].preprocess(self.data);
ind.update_route_node_mappings(r1);
ind.cost += ind.routes[r1].cost - old_cost;
return true;
} else {
return false;
};
}
pub fn run_intra_route_swap_right(
&mut self,
ind: &mut Individual,
r1: usize,
pos1: usize,
) -> bool {
let route = &ind.routes[r1];
let len = route.nodes.len();
if len < pos1 + 4 {
return false;
}
debug_assert!(pos1 > 0);
debug_assert!(ind.routes[r1].nodes[pos1].id != 0);
let old_cost = route.cost;
let mut best_cost = old_cost;
let mut best_pos: Option<usize> = None;
let mut acc_mid = route.nodes[pos1 + 1].seq1;
for pos2 in (pos1 + 2)..(len - 1) {
let new_cost = Sequence::eval_n(
self.data,
&[
route.nodes[pos1 - 1].seq0_i,
route.nodes[pos2].seq1,
acc_mid,
route.nodes[pos1].seq1,
route.nodes[pos2 + 1].seqi_n,
],
);
if new_cost < best_cost {
best_cost = new_cost;
best_pos = Some(pos2);
}
acc_mid = Sequence::join2(self.data, &acc_mid, &route.nodes[pos2].seq1);
}
if let Some(mypos) = best_pos {
ind.routes[r1].nodes.swap(pos1, mypos);
ind.routes[r1].preprocess(self.data);
ind.update_route_node_mappings(r1);
ind.cost += ind.routes[r1].cost - old_cost;
return true;
} else {
return false;
};
}
pub fn run_2opt(&mut self, ind: &mut Individual, r1: usize, pos1: usize) -> bool {
let route = &ind.routes[r1];
let len = route.nodes.len();
if len < pos1 + 3 {
return false;
}
debug_assert!(pos1 > 0);
debug_assert!(ind.routes[r1].nodes[pos1].id != 0);
let old_cost = route.cost;
let mut best_cost = old_cost;
let mut best_pos: Option<usize> = None;
let mut mid_rev = route.nodes[pos1].seq21;
for pos2 in (pos1 + 1)..(len - 1) {
let new_cost = Sequence::eval3(
self.data,
&route.nodes[pos1 - 1].seq0_i,
&mid_rev,
&route.nodes[pos2 + 1].seqi_n,
);
if new_cost < best_cost {
best_cost = new_cost;
best_pos = Some(pos2);
}
if pos2 + 1 < len - 1 {
mid_rev = Sequence::join2(self.data, &route.nodes[pos2 + 1].seq1, &mid_rev);
}
}
if let Some(mypos) = best_pos {
ind.routes[r1].nodes[pos1..=mypos].reverse();
ind.routes[r1].preprocess(self.data);
ind.update_route_node_mappings(r1);
ind.cost += ind.routes[r1].cost - old_cost;
return true;
} else {
return false;
};
}
pub fn runls(&mut self, ind: &mut Individual) {
for route in &mut ind.routes {
route.preprocess(self.data);
}
let mut improved = true;
let mut loop_id = 0;
while improved {
improved = false;
loop_id += 1;
let c1_order: Vec<usize> = (1..self.data.nb_nodes).collect();
for c1 in c1_order {
for &c2 in &self.neighbors_before[c1] {
let r1 = ind.node_route[c1];
let pos1 = ind.node_pos[c1];
let r2 = ind.node_route[c2];
let pos2 = ind.node_pos[c2] + 1;
if r1 == r2 {
continue;
}
let mut args = MoveArgs::new(r1, pos1, r2, pos2);
if ind.routes[r2].nodes[pos2].id != 0 {
Swap::evaluate(self.data, ind, &mut args);
if args.profit > 0 {
Swap::perform(self.data, ind, &args);
improved = true;
continue;
}
}
Relocate::evaluate(self.data, ind, &mut args);
if args.profit > 0 {
Relocate::perform(self.data, ind, &args);
improved = true;
continue;
}
TwoOptStar::evaluate(self.data, ind, &mut args);
if args.profit > 0 {
TwoOptStar::perform(self.data, ind, &args);
improved = true;
continue;
}
}
let mut tested_empty_route = false;
for r2 in 0..ind.routes.len() {
let r1 = ind.node_route[c1];
let pos1 = ind.node_pos[c1];
let pos2 = 1;
if r1 == r2 {
continue;
}
if ind.routes[r2].nodes.len() == 2 {
if loop_id == 1 || tested_empty_route {
continue;
};
tested_empty_route = true;
}
let mut args = MoveArgs::new(r1, pos1, r2, pos2);
if ind.routes[r2].nodes[pos2].id != 0 {
Swap::evaluate(self.data, ind, &mut args);
if args.profit > 0 {
Swap::perform(self.data, ind, &args);
improved = true;
continue;
}
}
Relocate::evaluate(self.data, ind, &mut args);
if args.profit > 0 {
Relocate::perform(self.data, ind, &args);
improved = true;
continue;
}
TwoOptStar::evaluate(self.data, ind, &mut args);
if args.profit > 0 {
TwoOptStar::perform(self.data, ind, &args);
improved = true;
continue;
}
}
improved |= self.run_2opt(ind, ind.node_route[c1], ind.node_pos[c1]);
improved |=
self.run_intra_route_relocate(ind, ind.node_route[c1], ind.node_pos[c1]);
improved |=
self.run_intra_route_swap_right(ind, ind.node_route[c1], ind.node_pos[c1]);
}
}
}
}
fn solve(data: Data) -> anyhow::Result<Option<Solution>> {
let mut individual = Constructive::build_individual(&data);
let mut my_local_search = LocalSearch::new(&data);
my_local_search.runls(&mut individual);
let usize_routes = individual
.routes
.iter()
.map(|r| r.nodes.iter().map(|n| n.id).collect::<Vec<usize>>())
.filter(|route| route.len() > 2)
.collect::<Vec<Vec<usize>>>();
Ok(Some(Solution {
routes: usize_routes,
}))
}
pub fn solve_challenge(
challenge: &Challenge,
save_solution: &dyn Fn(&Solution) -> anyhow::Result<()>,
hyperparameters: &Option<Map<String, Value>>,
) -> anyhow::Result<()> {
let data = TigLoader::load(
Parameters {
file_name: String::new(),
seed: challenge.seed,
initial_penalty: 1000,
granularity: 30,
},
&challenge,
);
match solve(data) {
Ok(Some(solution)) => {
let _ = save_solution(&solution);
}
Ok(None) => {
eprintln!("No solution found for the sub-instance.");
}
Err(e) => {
eprintln!("Error solving sub-instance: {}", e);
}
}
Ok(())
}