vrp_core/construction/clustering/vicinity/
mod.rs1#[cfg(test)]
4#[path = "../../../../tests/unit/construction/clustering/vicinity/vicinity_test.rs"]
5mod vicinity_test;
6
7use crate::construction::heuristics::*;
8use crate::models::common::Dimensions;
9use crate::models::common::*;
10use crate::models::problem::{Actor, Job};
11use crate::models::Problem;
12use rosomaxa::prelude::*;
13use std::cmp::Ordering;
14use std::collections::HashSet;
15use std::ops::ControlFlow;
16use std::sync::Arc;
17
18mod estimations;
19use self::estimations::*;
20use crate::models::solution::Commute;
21use crate::prelude::ViolationCode;
22
23custom_dimension!(ClusterInfo typeof Vec<ClusterInfo>);
24
25pub type ClusterCandidate<'a> = (&'a Job, &'a HashSet<Job>);
27
28type CheckInsertionFn = (dyn Fn(&Job) -> Result<(), ViolationCode> + Send + Sync);
29
30#[derive(Clone)]
32pub struct ClusterConfig {
33 pub profile: Profile,
35 pub threshold: ThresholdPolicy,
37 pub visiting: VisitPolicy,
39 pub serving: ServingPolicy,
41 pub filtering: FilterPolicy,
43 pub building: BuilderPolicy,
45}
46
47#[derive(Clone)]
49pub struct ThresholdPolicy {
50 pub moving_duration: Duration,
52 pub moving_distance: Distance,
54 pub min_shared_time: Option<Duration>,
56 pub smallest_time_window: Option<Duration>,
58 pub max_jobs_per_cluster: Option<usize>,
60}
61
62#[derive(Clone)]
64pub enum VisitPolicy {
65 Return,
67 ClosedContinuation,
70 OpenContinuation,
75}
76
77#[derive(Clone)]
79pub struct FilterPolicy {
80 pub job_filter: Arc<dyn Fn(&Job) -> bool + Send + Sync>,
82 pub actor_filter: Arc<dyn Fn(&Actor) -> bool + Send + Sync>,
84}
85
86#[derive(Clone)]
88pub enum ServingPolicy {
89 Original {
91 parking: Duration,
93 },
94 Multiplier {
96 multiplier: Float,
98 parking: Duration,
100 },
101 Fixed {
103 value: Duration,
105 parking: Duration,
107 },
108}
109
110pub type OrderingGlobalFn = Arc<dyn Fn(ClusterCandidate, ClusterCandidate) -> Ordering + Send + Sync>;
112pub type OrderingLocalFn = Arc<dyn Fn(&ClusterInfo, &ClusterInfo) -> Ordering + Send + Sync>;
114
115#[derive(Clone)]
117pub struct BuilderPolicy {
118 pub ordering_global_fn: OrderingGlobalFn,
120 pub ordering_local_fn: OrderingLocalFn,
122}
123
124#[derive(Clone)]
126pub struct ClusterInfo {
127 pub job: Job,
129 pub service_time: Duration,
131 pub place_idx: usize,
133 pub commute: Commute,
135}
136
137pub fn create_job_clusters(
142 problem: Arc<Problem>,
143 environment: Arc<Environment>,
144 config: &ClusterConfig,
145) -> Vec<(Job, Vec<Job>)> {
146 let insertion_ctx = InsertionContext::new_empty(problem.clone(), environment);
147 let constraint = insertion_ctx.problem.goal.clone();
148 let check_insertion = get_check_insertion_fn(insertion_ctx, config.filtering.actor_filter.clone());
149 let transport = problem.transport.as_ref();
150 let jobs = problem
151 .jobs
152 .all()
153 .iter()
154 .filter(|job| (config.filtering.job_filter)(job))
155 .filter(|job| job.as_single().is_some())
157 .cloned()
158 .collect::<Vec<_>>();
159
160 let estimates = get_jobs_dissimilarities(jobs.as_slice(), transport, config);
161
162 get_clusters(&constraint, estimates, config, &check_insertion)
163}
164
165fn get_check_insertion_fn(
167 insertion_ctx: InsertionContext,
168 actor_filter: Arc<dyn Fn(&Actor) -> bool + Send + Sync>,
169) -> impl Fn(&Job) -> Result<(), ViolationCode> {
170 move |job: &Job| -> Result<(), ViolationCode> {
171 let eval_ctx = EvaluationContext {
172 goal: &insertion_ctx.problem.goal,
173 job,
174 leg_selection: &LegSelection::Exhaustive,
175 result_selector: &BestResultSelector::default(),
176 };
177
178 insertion_ctx
179 .solution
180 .registry
181 .next_route()
182 .filter(|route_ctx| (actor_filter)(&route_ctx.route().actor))
183 .try_fold(Err(ViolationCode::unknown()), |_, route_ctx| {
184 let result = eval_job_insertion_in_route(
185 &insertion_ctx,
186 &eval_ctx,
187 route_ctx,
188 InsertionPosition::Any,
189 InsertionResult::make_failure(),
190 );
191
192 match result {
193 InsertionResult::Success(_) => ControlFlow::Break(Ok(())),
194 InsertionResult::Failure(failure) => ControlFlow::Continue(Err(failure.constraint)),
195 }
196 })
197 .unwrap_value()
198 }
199}
200
201impl ServingPolicy {
202 pub fn get_parking(&self) -> Duration {
204 match &self {
205 Self::Original { parking } => *parking,
206 Self::Multiplier { parking, .. } => *parking,
207 Self::Fixed { parking, .. } => *parking,
208 }
209 }
210}