genetic_algorithm_tsp/distance_mat.rs
1use crate::routes;
2/// A representation of a f64 based distance matrix.
3#[derive(Debug)]
4pub struct DistanceMat {
5 distances: Vec<Vec<f64>>,
6}
7
8impl DistanceMat {
9 /// Create a new distance mat based on exising
10 /// distances.
11 ///
12 /// # Arguments
13 ///
14 /// * `distances` - The distances between all indexes 0..n. The matrix
15 /// is assumed to be symmetrical and the distance between an object and itself
16 /// (the diagonal) should be only 0.
17 ///
18 /// # Examples
19 ///
20 /// ```
21 /// use genetic_algorithm_tsp::distance_mat::DistanceMat;
22 ///
23 /// let distance_matrix = DistanceMat::new(vec![vec![0.0,1.0,2.0], vec![1.0,0.0,3.0], vec![2.0,3.0,0.0]]);
24 /// ```
25 pub fn new(distances: Vec<Vec<f64>>) -> Self {
26 DistanceMat { distances }
27 }
28 /// Get the number of nodes in the distance matrix, e.g. one of its dimensions.
29 ///
30 /// # Examples
31 ///
32 /// ```
33 /// use genetic_algorithm_tsp::distance_mat::DistanceMat;
34 ///
35 /// let distance_matrix = DistanceMat::new(vec![vec![0.0,1.0,2.0], vec![1.0,0.0,3.0], vec![2.0,3.0,0.0]]);
36 /// println!("{}", distance_matrix.n_units());
37 /// ```
38 pub fn n_units(&self) -> usize {
39 self.distances.len()
40 }
41 /// Given a sequence of nodes (in a `Route`-object) compute the distance for the round-
42 /// trip between node 0..0
43 ///
44 /// # Arguments
45 ///
46 /// * `route` - The sequence of nodes that is visited and for which the round-trip-lenght
47 /// should be computed.
48 ///
49 /// # Examples
50 ///
51 /// ```
52 /// use genetic_algorithm_tsp::distance_mat::DistanceMat;
53 /// use genetic_algorithm_tsp::route::Route;
54 ///
55 /// let distance_matrix = DistanceMat::new(vec![vec![0.0,1.0,2.0], vec![1.0,0.0,3.0], vec![2.0,3.0,0.0]]);
56 /// println!("{}", distance_matrix.get_distance(&vec![1,0,2]));
57 /// ```
58 pub fn get_distance(&self, route: &[usize]) -> f64 {
59 route
60 .iter()
61 .fold(
62 // By folding the indexes we get the distances between 1-2, 2-3, ... , (n-1)-n.
63 // Then we are missing n-0, therefore that's the initial value we choose in the `fold`-
64 // operator.
65 (self.distances[route[route.len() - 1]][route[0]], None),
66 |(mut loss, last_point): (f64, Option<usize>), current_point| {
67 if let Some(last_point) = last_point {
68 loss += &self.distances[last_point][*current_point];
69 }
70 (loss, Some(*current_point))
71 },
72 )
73 .0
74 }
75
76 /// Generate a random population suiting your distance mat.
77 ///
78 /// # Arguments
79 ///
80 /// * `n_routes` - How many routes should be generated?
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use genetic_algorithm_tsp::distance_mat::DistanceMat;
86 ///
87 /// let distance_matrix = DistanceMat::new(vec![vec![0.0,1.0,2.0], vec![1.0,0.0,3.0], vec![2.0,3.0,0.0]]);
88 /// println!("{}", distance_matrix.get_random_population(5));
89 /// ```
90 pub fn get_random_population(&self, n_routes: usize) -> routes::Routes {
91 routes::Routes::random(n_routes, self.n_units())
92 }
93}
94
95#[cfg(test)]
96mod test_distance_mat {
97 use super::*;
98 use crate::test_utils::test_dist_mat;
99 #[test]
100 fn test_constructor() {
101 let dist_mat = DistanceMat::new(vec![vec![0.0, 1.0], vec![1.0, 0.0]]);
102 assert_eq!(dist_mat.distances, vec![vec![0.0, 1.0], vec![1.0, 0.0]]);
103 }
104 #[test]
105 fn test_dist_same_node() {
106 assert_eq!(test_dist_mat().get_distance(&vec![0, 0]), 0.0);
107 }
108 #[test]
109 fn test_dist_two_nodes() {
110 assert_eq!(test_dist_mat().get_distance(&vec![0, 1]), 2.0);
111 assert_eq!(test_dist_mat().get_distance(&vec![0, 2]), 4.0);
112 assert_eq!(test_dist_mat().get_distance(&vec![1, 2]), 6.0);
113 }
114 #[test]
115 fn test_dist_three_nodes() {
116 assert_eq!(test_dist_mat().get_distance(&vec![0, 1, 2]), 6.0);
117 assert_eq!(test_dist_mat().get_distance(&vec![0, 2, 1]), 6.0);
118 }
119 #[test]
120 fn test_dist_repeat_visit() {
121 assert_eq!(test_dist_mat().get_distance(&[0, 2, 1, 2]), 10.0);
122 }
123 #[test]
124 fn test_get_random_population() {
125 let distance_matrix = DistanceMat::new(vec![
126 vec![0.0, 1.0, 2.0],
127 vec![1.0, 0.0, 3.0],
128 vec![2.0, 3.0, 0.0],
129 ]);
130 distance_matrix.get_random_population(5);
131 }
132}