Skip to content

Commit 210fc9b

Browse files
Change oen of the metrics to use selection sampling
1 parent cf9d1ec commit 210fc9b

File tree

2 files changed

+44
-63
lines changed

2 files changed

+44
-63
lines changed

vrp-core/src/construction/heuristics/metrics.rs

Lines changed: 43 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ mod metrics_test;
44

55
use crate::construction::enablers::{TotalDistanceTourState, TotalDurationTourState, WaitingTimeActivityState};
66
use crate::construction::features::MaxVehicleLoadTourState;
7-
use crate::construction::heuristics::{InsertionContext, RouteContext, RouteState};
7+
use crate::construction::heuristics::{InsertionContext, RouteState};
88
use crate::models::common::Distance;
9-
use crate::models::problem::{TransportCost, TravelTime};
9+
use crate::models::problem::TravelTime;
1010
use rosomaxa::algorithms::math::*;
1111
use rosomaxa::prelude::*;
12-
use rosomaxa::utils::parallel_collect;
12+
use rosomaxa::utils::{parallel_collect, SelectionSamplingIterator};
1313
use std::cmp::Ordering;
1414

1515
/// Gets max load variance in tours.
@@ -176,45 +176,55 @@ pub fn get_last_distance_customer_mean(insertion_ctx: &InsertionContext) -> Floa
176176
get_mean_iter(distances)
177177
}
178178

179-
/// Estimates distances between all routes using their medoids and returns the sorted groups.
179+
/// Estimates distances between all routes by sampling locations from routes and measuring
180+
/// average distance between them.
180181
pub fn group_routes_by_proximity(insertion_ctx: &InsertionContext) -> Option<Vec<Vec<usize>>> {
181-
let solution = &insertion_ctx.solution;
182-
let profile = &solution.routes.first().map(|route_ctx| &route_ctx.route().actor.vehicle.profile)?;
182+
const LOCATION_SAMPLE_SIZE: usize = 8;
183+
184+
let routes = &insertion_ctx.solution.routes;
183185
let transport = insertion_ctx.problem.transport.as_ref();
186+
let random = &insertion_ctx.environment.random;
184187

185-
let indexed_route_clusters =
186-
parallel_collect(&solution.routes, |route_ctx| get_approx_clusters(route_ctx, transport))
187-
.into_iter()
188-
.enumerate()
189-
.collect::<Vec<_>>();
188+
// get routes with sampled locations and index them
189+
let indexed_route_clusters = routes
190+
.iter()
191+
.map(|route_ctx| {
192+
SelectionSamplingIterator::new(
193+
route_ctx.route().tour.all_activities(),
194+
LOCATION_SAMPLE_SIZE,
195+
random.clone(),
196+
)
197+
.map(|activity| activity.place.location)
198+
.collect::<Vec<_>>()
199+
})
200+
.enumerate()
201+
.collect::<Vec<_>>();
190202

191203
Some(parallel_collect(&indexed_route_clusters, |(outer_idx, outer_clusters)| {
192204
let mut route_distances = indexed_route_clusters
193205
.iter()
194206
.filter(move |(inner_idx, _)| *outer_idx != *inner_idx)
195207
.map(move |(inner_idx, inner_clusters)| {
196-
let distance = match (outer_clusters, inner_clusters) {
197-
(Some(outer_clusters), Some(inner_clusters)) => {
198-
// get a sum of distances between all pairs of clusters
199-
let pair_distance = outer_clusters
200-
.iter()
201-
.flat_map(|outer| inner_clusters.iter().map(move |inner| (inner, outer)))
202-
.map(|(&o, &i)| {
203-
transport.distance_approx(profile, o, i).max(0.)
204-
+ transport.distance_approx(profile, i, o).max(0.)
205-
})
206-
.sum::<Distance>()
207-
/ 2.;
208-
209-
let total_pairs = outer_clusters.len() * inner_clusters.len();
210-
if total_pairs == 0 {
211-
None
212-
} else {
213-
// get average distance between clusters
214-
Some(pair_distance / total_pairs as Float)
215-
}
216-
}
217-
_ => None,
208+
// get a sum of distances between all pairs of sampled locations
209+
let pair_distance = outer_clusters
210+
.iter()
211+
.flat_map(|outer| inner_clusters.iter().map(move |inner| (inner, outer)))
212+
.map(|(&o, &i)| {
213+
// NOTE use outer and inner route profiles to estimate distance
214+
let inner_profile = &routes[*inner_idx].route().actor.vehicle.profile;
215+
let outer_profile = &routes[*outer_idx].route().actor.vehicle.profile;
216+
transport.distance_approx(inner_profile, o, i).max(0.)
217+
+ transport.distance_approx(outer_profile, o, i).max(0.)
218+
})
219+
.sum::<Distance>()
220+
/ 2.;
221+
222+
let total_pairs = outer_clusters.len() * inner_clusters.len();
223+
let distance = if total_pairs == 0 {
224+
None
225+
} else {
226+
// get average distance between clusters
227+
Some(pair_distance / total_pairs as Float)
218228
};
219229

220230
(*inner_idx, distance)
@@ -243,32 +253,3 @@ fn get_values_from_route_state<'a>(
243253
.iter()
244254
.map(move |route_ctx| state_value_fn(route_ctx.state()).copied().unwrap_or_default())
245255
}
246-
247-
fn get_approx_clusters(route_ctx: &RouteContext, transport: &(dyn TransportCost)) -> Option<Vec<usize>> {
248-
const CLUSTER_SIZE_RATIO: Float = 0.2;
249-
250-
let distance_threshold = route_ctx.state().get_total_distance().copied()? * CLUSTER_SIZE_RATIO;
251-
let profile = &route_ctx.route().actor.vehicle.profile;
252-
253-
Some(
254-
route_ctx
255-
.route()
256-
.tour
257-
.all_activities()
258-
.map(|activity| activity.place.location)
259-
.fold((Distance::default(), None, Vec::default()), |(mut acc_distance, prev, mut clusters), current| {
260-
if let Some(prev) = prev {
261-
acc_distance += transport.distance_approx(profile, prev, current).max(0.);
262-
if acc_distance > distance_threshold {
263-
clusters.push(current);
264-
acc_distance = Distance::default();
265-
}
266-
} else {
267-
clusters.push(current)
268-
}
269-
270-
(acc_distance, Some(current), clusters)
271-
})
272-
.2,
273-
)
274-
}

vrp-core/tests/unit/solver/search/local/exchange_swap_star_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ can_create_route_pairs! {
269269
}
270270

271271
fn can_create_route_pairs_impl(route_pairs_threshold: usize, is_proximity: bool, expected_length: usize) {
272-
let reals = once(i32::from(is_proximity)).chain([0; 9]).map(|value| value as Float).collect();
272+
let reals = once(i32::from(is_proximity)).chain([0; 32]).map(|value| value as Float).collect();
273273
let matrix = (3, 3);
274274
let environment = create_test_environment_with_random(Arc::new(FakeRandom::new(vec![], reals)));
275275
let (problem, solution) = generate_matrix_routes_with_defaults(matrix.0, matrix.1, true);

0 commit comments

Comments
 (0)