genetic_algorithm_tsp/
route.rs

1use crate::distance_mat::DistanceMat;
2use crate::subsequence::Subsequence;
3use crate::utils::{change_order, get_random_elem_from_range, ordered_crossover, remove_elem};
4use genetic_algorithm_traits::Individual;
5use rand::seq::SliceRandom;
6use std::cmp::max;
7use std::fmt;
8
9/// The `Route` is an invidiual in the traveling salemens problem that is a valid route.
10#[derive(Debug, PartialEq, Clone, Eq, Hash)]
11pub struct Route {
12    /// The order in which the nodes should be visited.
13    pub indexes: Vec<usize>,
14}
15/// Make Route formattable.
16impl fmt::Display for Route {
17    /// As a string representation of the Route, just display the inidividual
18    /// nodes that are visited.
19    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
20        write!(formatter, "Route({:?})", self.indexes)
21    }
22}
23impl Route {
24    /// Create a new route based on a vector of indexes.
25    ///
26    /// # Arguments
27    ///
28    /// * `indexes` - The order in which the nodes are visited in the Traveling Salesman Problem.
29    ///
30    /// # Examples
31    ///
32    /// ```
33    /// use genetic_algorithm_tsp::route::Route;
34    ///
35    /// let my_individual = Route::from(Route::new(vec![0,1,2]));
36    /// ```
37    pub fn new(indexes: Vec<usize>) -> Self {
38        Self { indexes }
39    }
40    /// Get the number of nodes for this route.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use genetic_algorithm_tsp::route::Route;
46    ///
47    /// let three_node_route = Route::from(Route::new(vec![0,1,2]));
48    /// println!("This route has {} nodes", three_node_route.get_n_nodes());
49    /// ```
50    pub fn get_n_nodes(&self) -> usize {
51        self.indexes.len()
52    }
53}
54impl<'a> Individual<'a> for Route {
55    // The Distance matrix is needed by the individuals to compute their fitness on.
56    type IndividualCost = DistanceMat;
57    /// Randomly changes the order of two nodes in the route
58    ///
59    /// # Arguments
60    ///
61    /// * `prob` - The probability with which the indexes will be changed
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use genetic_algorithm_tsp::route::Route;
67    /// use  genetic_algorithm_traits::Individual;
68    ///
69    /// let my_individual = Route::from(Route::new(vec![0,1,2]));
70    /// let my_mutated_indiviual =  my_individual.mutate(1.0);
71    /// ```
72    fn mutate(self, prob: f32) -> Self {
73        Route {
74            indexes: if get_random_elem_from_range(0.0..1.0) > prob {
75                // With probabilty (1-prop) don't do any mutation.
76                self.indexes
77            } else {
78                // else mutation is applied.
79                // To do so first sample an element to put another element in front of.
80                let put_before_idx: usize = get_random_elem_from_range(0..(self.indexes.len() - 1));
81                change_order(
82                    &self.indexes,
83                    put_before_idx,
84                    // Sample the element that should be put before `put_before_idx`. Should not be
85                    // the `put_before_idx` itself.
86                    *remove_elem(
87                        remove_elem(
88                            (0..(self.indexes.len() - 1)).collect::<Vec<usize>>(),
89                            put_before_idx,
90                        ),
91                        max(put_before_idx, 1) - 1,
92                    )
93                    .choose(&mut rand::thread_rng())
94                    .unwrap_or(&((put_before_idx + 1) % self.indexes.len())),
95                )
96            },
97        }
98    }
99    /// Crossover this invidual with another individual to create a new individual. Currently
100    /// uses the `ordered_crossover` algorithm.
101    ///
102    /// # Arguments
103    ///
104    /// * `other` - The other individual you would like to crossover with this individual.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use genetic_algorithm_tsp::route::Route;
110    /// use  genetic_algorithm_traits::Individual;
111    ///
112    /// let my_individual = Route::from(Route::new(vec![0,1,2]));
113    /// let my_individual = my_individual.crossover(
114    ///     &Route::from(Route::new(vec![1,0,2]))
115    /// );
116    /// ```
117    fn crossover(&self, other: &Route) -> Self {
118        ordered_crossover(
119            self,
120            other,
121            Subsequence::random_subsequence(self.indexes.len()),
122        )
123    }
124    /// Compute how much distance the individual implies with its order of nodes
125    /// and the distance matrix.
126    ///
127    /// # Arguments
128    ///
129    /// * `distance_matrix` - Distance Matrix that determines the length of the proposed
130    /// route
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use genetic_algorithm_tsp::route::Route;
136    /// use genetic_algorithm_tsp::distance_mat::DistanceMat;
137    /// use  genetic_algorithm_traits::Individual;
138    ///
139    /// let my_individual = Route::from(Route::new(vec![0,1,2]));
140    /// println!("Fitness of your individual: {}", my_individual.fitness(
141    ///     &DistanceMat::new(vec![vec![0.0,1.0,2.0], vec![1.0,0.0,3.0], vec![2.0,3.0,0.0]]))
142    /// )
143    /// ```
144    ///
145    fn fitness(&self, distance_mat: &DistanceMat) -> f64 {
146        -distance_mat.get_distance(&self.indexes[..])
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153    mod test_route {
154        use super::*;
155        use crate::test_utils::valid_permutation;
156        #[test]
157        fn test_format() {
158            let route_to_print = Route::new(vec![1, 2, 3, 4]);
159            assert_eq!(format!("{}", route_to_print), "Route([1, 2, 3, 4])");
160        }
161
162        #[test]
163        fn test_constructor() {
164            let route = Route::new(vec![1, 2, 3, 4]);
165            assert_eq!(route.indexes, vec![1, 2, 3, 4])
166        }
167        #[test]
168        fn test_n_nodes() {
169            let three_node_route = Route::from(Route::new(vec![0, 1, 2]));
170            assert_eq!(three_node_route.get_n_nodes(), 3);
171        }
172        #[test]
173        fn test_mutuate_no_prob() {
174            assert_eq!(
175                Route::new(vec![1, 2, 3, 4]).mutate(0.0).indexes,
176                vec![1, 2, 3, 4]
177            )
178        }
179        // Run the following test five times.
180        #[test]
181        #[test]
182        #[test]
183        #[test]
184        #[test]
185        fn test_mutuate_100_prob() {
186            assert_ne!(
187                Route::new(vec![1, 2, 3, 4]).mutate(1.0).indexes,
188                vec![1, 2, 3, 4]
189            )
190        }
191        #[test]
192        fn test_mutuate_100_prob_3_elems() {
193            assert_ne!(Route::new(vec![1, 2, 3]).mutate(1.0).indexes, vec![1, 2, 3])
194        }
195        #[test]
196        fn test_mutate_simple_run() {
197            let test_route = Route::new(vec![1, 2, 0]);
198            valid_permutation(&test_route.indexes, &test_route.clone().mutate(0.5).indexes);
199        }
200    }
201    mod test_crossover {
202        use super::*;
203        use crate::test_utils::valid_permutation;
204
205        #[test]
206        fn random_test_10() {
207            let n_tests = 1000;
208            let route_a = Route {
209                indexes: vec![0, 12, 7, 3, 9, 8, 11, 5, 13, 1, 4, 6, 10, 15, 2, 14],
210            };
211            let route_b = Route {
212                indexes: vec![7, 10, 15, 12, 2, 9, 5, 3, 1, 6, 4, 13, 14, 11, 8, 0],
213            };
214            let mut n_no_crossover = 0;
215            for _ in 1..n_tests {
216                let result = route_a.crossover(&route_b);
217                if result.indexes == route_a.indexes || result.indexes == route_b.indexes {
218                    n_no_crossover += 1;
219                }
220                valid_permutation(&result.indexes, &route_a.indexes);
221                valid_permutation(&result.indexes, &route_a.indexes);
222            }
223            assert!(n_no_crossover <= n_tests / 5);
224        }
225    }
226    mod test_fitness {
227        use super::*;
228        use crate::test_utils::test_dist_mat;
229        #[test]
230        fn simple_functionality_test() {
231            let distance_mat = test_dist_mat();
232            let route = Route::new(vec![1, 2, 0]);
233            assert_eq!(route.fitness(&distance_mat), -6.0);
234        }
235    }
236}