1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#[cfg(test)]
#[path = "../../../tests/unit/solver/objectives/total_transport_test.rs"]
mod total_transport_test;

use super::*;
use crate::algorithms::nsga2::Objective;
use crate::construction::constraints::{TOTAL_DISTANCE_KEY, TOTAL_DURATION_KEY};
use crate::models::common::Cost;
use crate::models::problem::TargetObjective;
use crate::utils::compare_floats;
use std::ops::Deref;
use std::sync::Arc;

/// An objective function for total cost minimization as a target.
pub struct TotalCost;

impl TotalCost {
    /// Creates an objective to minimize total cost.
    pub fn minimize() -> TargetObjective {
        Box::new(TotalTransport { fitness: Arc::new(|insertion_ctx| insertion_ctx.solution.get_total_cost()) })
    }
}

/// An objective function for total distance minimization as a target.
pub struct TotalDistance;

impl TotalDistance {
    /// Creates an objective to minimize total distance.
    pub fn minimize() -> TargetObjective {
        new_with_route_state_key(TOTAL_DISTANCE_KEY)
    }
}

/// An objective function for total duration minimization as a target.
pub struct TotalDuration;

impl TotalDuration {
    /// Creates an objective to minimize total duration.
    pub fn minimize() -> TargetObjective {
        new_with_route_state_key(TOTAL_DURATION_KEY)
    }
}

struct TotalTransport {
    fitness: Arc<dyn Fn(&InsertionContext) -> f64 + Send + Sync>,
}

impl Objective for TotalTransport {
    type Solution = InsertionContext;

    fn total_order(&self, a: &Self::Solution, b: &Self::Solution) -> Ordering {
        compare_floats(self.fitness(a), self.fitness(b))
    }

    fn distance(&self, a: &Self::Solution, b: &Self::Solution) -> f64 {
        self.fitness(a) - self.fitness(b)
    }

    fn fitness(&self, solution: &Self::Solution) -> f64 {
        self.fitness.deref()(solution)
    }
}

fn new_with_route_state_key(key: i32) -> TargetObjective {
    Box::new(TotalTransport {
        fitness: Arc::new(move |insertion_ctx| {
            insertion_ctx
                .solution
                .routes
                .iter()
                .fold(Cost::default(), move |acc, rc| acc + rc.state.get_route_state::<f64>(key).cloned().unwrap_or(0.))
        }),
    })
}