vrp_core/construction/features/
minimize_unassigned.rs1#[cfg(test)]
4#[path = "../../../tests/unit/construction/features/minimize_unassigned_test.rs"]
5mod minimize_unassigned_test;
6
7use super::*;
8use crate::utils::Either;
9use std::iter::empty;
10
11pub struct MinimizeUnassignedBuilder {
13 name: String,
14 job_estimator: Option<UnassignedJobEstimator>,
15}
16
17impl MinimizeUnassignedBuilder {
18 pub fn new(name: &str) -> Self {
20 Self { name: name.to_string(), job_estimator: None }
21 }
22
23 pub fn set_job_estimator<F>(mut self, func: F) -> Self
26 where
27 F: Fn(&SolutionContext, &Job) -> Float + Send + Sync + 'static,
28 {
29 self.job_estimator = Some(Arc::new(func));
30 self
31 }
32
33 pub fn build(mut self) -> GenericResult<Feature> {
35 let unassigned_job_estimator = self.job_estimator.take().unwrap_or_else(|| Arc::new(|_, _| 1.));
36
37 FeatureBuilder::default()
38 .with_name(self.name.as_str())
39 .with_objective(MinimizeUnassignedObjective { unassigned_job_estimator })
40 .build()
41 }
42}
43
44type UnassignedJobEstimator = Arc<dyn Fn(&SolutionContext, &Job) -> Float + Send + Sync>;
46
47struct MinimizeUnassignedObjective {
48 unassigned_job_estimator: UnassignedJobEstimator,
49}
50
51impl FeatureObjective for MinimizeUnassignedObjective {
52 fn fitness(&self, solution: &InsertionContext) -> Cost {
53 if solution.solution.routes.is_empty() {
54 Either::Left(solution.solution.ignored.iter())
57 } else {
58 Either::Right(empty())
59 }
60 .chain(solution.solution.unassigned.keys())
61 .map(|job| (self.unassigned_job_estimator)(&solution.solution, job))
62 .sum::<Float>()
63 }
64
65 fn estimate(&self, move_ctx: &MoveContext<'_>) -> Cost {
66 match move_ctx {
67 MoveContext::Route { solution_ctx, job, .. } => -1. * (self.unassigned_job_estimator)(solution_ctx, job),
68 MoveContext::Activity { .. } => Cost::default(),
69 }
70 }
71}