Skip to content

Commit

Permalink
Refactor route proximity groups
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Oct 3, 2024
1 parent 210fc9b commit f193fc9
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 88 deletions.
8 changes: 4 additions & 4 deletions vrp-core/src/construction/heuristics/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ pub fn get_last_distance_customer_mean(insertion_ctx: &InsertionContext) -> Floa

/// Estimates distances between all routes by sampling locations from routes and measuring
/// average distance between them.
pub fn group_routes_by_proximity(insertion_ctx: &InsertionContext) -> Option<Vec<Vec<usize>>> {
const LOCATION_SAMPLE_SIZE: usize = 8;
pub fn group_routes_by_proximity(insertion_ctx: &InsertionContext) -> Vec<Vec<usize>> {
const LOCATION_SAMPLE_SIZE: usize = 4;

let routes = &insertion_ctx.solution.routes;
let transport = insertion_ctx.problem.transport.as_ref();
Expand All @@ -200,7 +200,7 @@ pub fn group_routes_by_proximity(insertion_ctx: &InsertionContext) -> Option<Vec
.enumerate()
.collect::<Vec<_>>();

Some(parallel_collect(&indexed_route_clusters, |(outer_idx, outer_clusters)| {
parallel_collect(&indexed_route_clusters, |(outer_idx, outer_clusters)| {
let mut route_distances = indexed_route_clusters
.iter()
.filter(move |(inner_idx, _)| *outer_idx != *inner_idx)
Expand Down Expand Up @@ -240,7 +240,7 @@ pub fn group_routes_by_proximity(insertion_ctx: &InsertionContext) -> Option<Vec
let (indices, _): (Vec<_>, Vec<_>) = route_distances.into_iter().unzip();

indices
}))
})
}

fn get_values_from_route_state<'a>(
Expand Down
24 changes: 8 additions & 16 deletions vrp-core/src/solver/search/decompose_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::construction::heuristics::*;
use crate::models::GoalContext;
use crate::solver::search::create_environment_with_custom_quota;
use crate::solver::*;
use rand::prelude::SliceRandom;
use rosomaxa::utils::parallel_into_collect;
use std::cell::RefCell;
use std::cmp::Ordering;
Expand Down Expand Up @@ -116,31 +115,24 @@ fn create_multiple_insertion_contexts(
environment: Arc<Environment>,
max_routes_range: (i32, i32),
) -> Option<Vec<(InsertionContext, HashSet<usize>)>> {
let mut route_groups_distances = group_routes_by_proximity(insertion_ctx)?;
route_groups_distances.iter_mut().for_each(|route_group_distance| {
let random = &environment.random;
let shuffle_count = random.uniform_int(2, (route_group_distance.len() as i32 / 5).max(2)) as usize;
route_group_distance.partial_shuffle(&mut random.get_rng(), shuffle_count);
});
if insertion_ctx.solution.routes.is_empty() {
return None;
}

let route_groups = group_routes_by_proximity(insertion_ctx);
let (min, max) = max_routes_range;
let max = if insertion_ctx.solution.routes.len() < 4 { 2 } else { max };

// identify route groups and create contexts from them
let used_indices = RefCell::new(HashSet::new());
let insertion_ctxs = route_groups_distances
.iter()
let insertion_ctxs = route_groups
.into_iter()
.enumerate()
.filter(|(outer_idx, _)| !used_indices.borrow().contains(outer_idx))
.map(|(outer_idx, route_group_distance)| {
.map(|(outer_idx, route_group)| {
let group_size = environment.random.uniform_int(min, max) as usize;
let route_group = once(outer_idx)
.chain(
route_group_distance
.iter()
.filter(|&inner_idx| !used_indices.borrow().contains(inner_idx))
.cloned(),
)
.chain(route_group.into_iter().filter(|inner_idx| !used_indices.borrow().contains(inner_idx)))
.take(group_size)
.collect::<HashSet<_>>();

Expand Down
80 changes: 38 additions & 42 deletions vrp-core/src/solver/search/local/exchange_swap_star.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use super::*;
use crate::models::problem::Job;
use crate::solver::search::create_environment_with_custom_quota;
use crate::utils::Either;
use rand::seq::SliceRandom;
use rosomaxa::utils::*;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -97,50 +96,47 @@ fn get_evaluation_context<'a>(search_ctx: &'a SearchContext, job: &'a Job) -> Ev
}

/// Creates route pairs to exchange jobs.
#[allow(clippy::needless_collect)] // NOTE enforce size hint to be non-zero
fn create_route_pairs(insertion_ctx: &InsertionContext, route_pairs_threshold: usize) -> Vec<(usize, usize)> {
let random = insertion_ctx.environment.random.clone();

if random.is_hit(0.1) { None } else { group_routes_by_proximity(insertion_ctx) }
.map(|route_groups_distances| {
let used_indices = RefCell::new(HashSet::<(usize, usize)>::new());
let distances = route_groups_distances
.into_iter()
.enumerate()
.flat_map(|(outer_idx, mut route_group_distance)| {
let shuffle_amount = (route_group_distance.len() as Float * 0.1) as usize;
route_group_distance.partial_shuffle(&mut random.get_rng(), shuffle_amount);
route_group_distance
.iter()
.cloned()
.filter(|inner_idx| {
let used_indices = used_indices.borrow();
!used_indices.contains(&(outer_idx, *inner_idx))
&& !used_indices.contains(&(*inner_idx, outer_idx))
})
.inspect(|&inner_idx| {
let mut used_indices = used_indices.borrow_mut();
used_indices.insert((outer_idx, inner_idx));
used_indices.insert((inner_idx, outer_idx));
})
.next()
.map(|inner_idx| (outer_idx, inner_idx))
})
.collect::<Vec<_>>();
SelectionSamplingIterator::new(distances.into_iter(), route_pairs_threshold, random.clone()).collect()
})
.unwrap_or_else(|| {
let route_count = insertion_ctx.solution.routes.len();
// NOTE this is needed to have size hint properly set
let all_route_pairs = (0..route_count)
.flat_map(move |outer_idx| {
(0..route_count)
.filter(move |&inner_idx| outer_idx > inner_idx)
.map(move |inner_idx| (outer_idx, inner_idx))
})
.collect::<Vec<_>>();
SelectionSamplingIterator::new(all_route_pairs.into_iter(), route_pairs_threshold, random.clone()).collect()
})
if random.is_hit(0.1) {
let route_count = insertion_ctx.solution.routes.len();
// NOTE this is needed to have size hint properly set
let all_route_pairs = (0..route_count)
.flat_map(move |outer_idx| {
(0..route_count)
.filter(move |&inner_idx| outer_idx > inner_idx)
.map(move |inner_idx| (outer_idx, inner_idx))
})
.collect::<Vec<_>>();

SelectionSamplingIterator::new(all_route_pairs.into_iter(), route_pairs_threshold, random).collect()
} else {
let route_groups = group_routes_by_proximity(insertion_ctx);
let used_indices = RefCell::new(HashSet::<(usize, usize)>::new());
let distances = route_groups
.into_iter()
.enumerate()
.flat_map(|(outer_idx, route_group)| {
route_group
.into_iter()
.filter(|inner_idx| {
let used_indices = used_indices.borrow();
!used_indices.contains(&(outer_idx, *inner_idx))
&& !used_indices.contains(&(*inner_idx, outer_idx))
})
.inspect(|inner_idx| {
let mut used_indices = used_indices.borrow_mut();
used_indices.insert((outer_idx, *inner_idx));
used_indices.insert((*inner_idx, outer_idx));
})
.next()
.map(|inner_idx| (outer_idx, inner_idx))
})
.collect::<Vec<_>>();

SelectionSamplingIterator::new(distances.into_iter(), route_pairs_threshold, random).collect()
}
}

/// Finds insertion cost of the existing job in the route.
Expand Down
50 changes: 24 additions & 26 deletions vrp-core/src/solver/search/ruin/route_removal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,32 +58,30 @@ impl Ruin for CloseRouteRemoval {
return insertion_ctx;
}

if let Some(route_groups_distances) = group_routes_by_proximity(&insertion_ctx) {
let random = insertion_ctx.environment.random.clone();

let stale_routes = insertion_ctx
.solution
.routes
.iter()
.enumerate()
.filter_map(|(idx, route)| if route.is_stale() { Some(idx) } else { None })
.collect::<Vec<_>>();

let route_index = if !stale_routes.is_empty() && random.is_head_not_tails() {
stale_routes[random.uniform_int(0, (stale_routes.len() - 1) as i32) as usize]
} else {
random.uniform_int(0, (route_groups_distances.len() - 1) as i32) as usize
};

#[allow(clippy::needless_collect)]
let routes = route_groups_distances[route_index]
.iter()
.filter_map(|idx| insertion_ctx.solution.routes.get(*idx))
.map(|route_ctx| route_ctx.route().actor.clone())
.collect::<Vec<_>>();

remove_routes_with_actors(&mut insertion_ctx.solution, &self.limits, random.as_ref(), routes.into_iter());
}
let random = insertion_ctx.environment.random.clone();
let route_groups = group_routes_by_proximity(&insertion_ctx);

let stale_routes = insertion_ctx
.solution
.routes
.iter()
.enumerate()
.filter_map(|(idx, route)| if route.is_stale() { Some(idx) } else { None })
.collect::<Vec<_>>();

let route_index = if !stale_routes.is_empty() && random.is_head_not_tails() {
stale_routes[random.uniform_int(0, (stale_routes.len() - 1) as i32) as usize]
} else {
random.uniform_int(0, (route_groups.len() - 1) as i32) as usize
};

let routes = route_groups[route_index]
.iter()
.filter_map(|idx| insertion_ctx.solution.routes.get(*idx))
.map(|route_ctx| route_ctx.route().actor.clone())
.collect::<Vec<_>>();

remove_routes_with_actors(&mut insertion_ctx.solution, &self.limits, random.as_ref(), routes.into_iter());

insertion_ctx
}
Expand Down

0 comments on commit f193fc9

Please sign in to comment.