@@ -4,12 +4,12 @@ mod metrics_test;
4
4
5
5
use crate :: construction:: enablers:: { TotalDistanceTourState , TotalDurationTourState , WaitingTimeActivityState } ;
6
6
use crate :: construction:: features:: MaxVehicleLoadTourState ;
7
- use crate :: construction:: heuristics:: { InsertionContext , RouteContext , RouteState } ;
7
+ use crate :: construction:: heuristics:: { InsertionContext , RouteState } ;
8
8
use crate :: models:: common:: Distance ;
9
- use crate :: models:: problem:: { TransportCost , TravelTime } ;
9
+ use crate :: models:: problem:: TravelTime ;
10
10
use rosomaxa:: algorithms:: math:: * ;
11
11
use rosomaxa:: prelude:: * ;
12
- use rosomaxa:: utils:: parallel_collect;
12
+ use rosomaxa:: utils:: { parallel_collect, SelectionSamplingIterator } ;
13
13
use std:: cmp:: Ordering ;
14
14
15
15
/// Gets max load variance in tours.
@@ -176,45 +176,55 @@ pub fn get_last_distance_customer_mean(insertion_ctx: &InsertionContext) -> Floa
176
176
get_mean_iter ( distances)
177
177
}
178
178
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.
180
181
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 ;
183
185
let transport = insertion_ctx. problem . transport . as_ref ( ) ;
186
+ let random = & insertion_ctx. environment . random ;
184
187
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 < _ > > ( ) ;
190
202
191
203
Some ( parallel_collect ( & indexed_route_clusters, |( outer_idx, outer_clusters) | {
192
204
let mut route_distances = indexed_route_clusters
193
205
. iter ( )
194
206
. filter ( move |( inner_idx, _) | * outer_idx != * inner_idx)
195
207
. 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 )
218
228
} ;
219
229
220
230
( * inner_idx, distance)
@@ -243,32 +253,3 @@ fn get_values_from_route_state<'a>(
243
253
. iter ( )
244
254
. map ( move |route_ctx| state_value_fn ( route_ctx. state ( ) ) . copied ( ) . unwrap_or_default ( ) )
245
255
}
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
- }
0 commit comments