genetic_algorithm_tsp_api/
tsp_solver.rs

1use genetic_algorithm_traits::Population;
2use genetic_algorithm_tsp::{distance_mat, route};
3use std::time;
4
5/// From a `std::time::Duration` object compute the elapsed microseconds.
6///
7/// # Arguments
8///
9/// * `duration` - The time that has gone by.
10///
11/// # Examples
12///
13/// ```
14/// use std::time;
15/// use std::thread;
16/// use genetic_algorithm_tsp_api::tsp_solver;
17///
18/// let before = time::Instant::now();
19/// thread::sleep(time::Duration::from_millis(10));
20/// println!("Sleeping for 10 ms took {} ms", tsp_solver::duration_to_ms(before.elapsed()));
21/// ```
22pub fn duration_to_ms(duration: time::Duration) -> u64 {
23    let nano_seconds = duration.subsec_nanos() as u64;
24    (1000 * 1000 * 1000 * duration.as_secs() + nano_seconds) / (1000 * 1000)
25}
26
27/// Compute an route that for the traveling-salesman-problem defined by
28/// the distance matrix.
29///
30/// # Arguments
31///
32/// * `distance_matrix` - These distances define the fitness of an invidual.
33/// * `n_generation` - How many generations should the algorithm run for?
34/// * `n_routes` - How many routes should be kept in the population.
35/// * `n_random_route_per_generation` - How many random routes should be
36///     ingested in every generation to allow?
37pub fn solve_tsp(
38    distance_matrix: &distance_mat::DistanceMat,
39    n_generations: usize,
40    n_routes: usize,
41    n_random_individuals_per_generation: usize,
42    top_n: usize,
43) -> Vec<route::Route> {
44    let initial_population = distance_matrix.get_random_population(n_routes);
45    // Decay mutation probability.
46    (0..10000)
47        .step_by(10000 / n_generations)
48        .fold(
49            initial_population,
50            |population, mutation_probability_int| {
51                population
52                    .evolve(1.0 - (f64::from(mutation_probability_int) / 10000.0) as f32)
53                    // Add a few random inidividuals each round.
54                    .add_n_random_nodes(n_random_individuals_per_generation)
55                    .get_fittest_population(n_routes, distance_matrix)
56            },
57        )
58        .get_n_fittest(top_n, distance_matrix)
59}
60
61mod tests {
62    #[test]
63    fn test_duration() {
64        use super::duration_to_ms;
65        use std::thread;
66        use std::time;
67
68        // Test that after waiting for 12 ms, `duration_to_ms`
69        // reports correctly that 12 ms have gone by.
70        let before = time::Instant::now();
71        thread::sleep(time::Duration::from_millis(12));
72        assert_eq!(duration_to_ms(before.elapsed()), 12);
73    }
74    #[test]
75    fn test_solve_tsp() {
76        use super::solve_tsp;
77        use genetic_algorithm_tsp::distance_mat;
78        use std::fs;
79        // Just run `solve_tsp` for a simple distance matrix.
80        // Load in the test matrix.
81        let distances = distance_mat::DistanceMat::new(
82            fs::read_to_string("tests/test-data/6_cities.txt")
83                .unwrap()
84                .lines()
85                .collect::<Vec<&str>>()
86                .iter()
87                .map(|line| {
88                    line.split(";")
89                        .map(|float_string| float_string.parse::<f64>().unwrap())
90                        .collect::<Vec<f64>>()
91                })
92                .collect(),
93        );
94        // Get a solution
95        let _ = solve_tsp(&distances, 20, 10, 10, 3);
96    }
97}