tsp/
tsp.rs

1use std::{
2    collections::{HashMap, HashSet},
3    hash::Hash,
4    time::{Duration, SystemTime},
5};
6
7use netaheuristics::{
8    algorithms::{
9        lns::LargeNeighborhoodSearch,
10        sa::{FactorSchedule, SimulatedAnnealing},
11        vns::VariableNeighborhoodSearch,
12    },
13    selectors::{AdaptiveSelector, RandomSelector, SequentialSelector},
14    termination::{Terminator, TimeTerminator},
15    Evaluate, ImprovingHeuristic, Operator, Outcome,
16};
17use rand::{Rng, RngCore, SeedableRng};
18
19fn main() {
20    // init
21    let n = 100;
22    let width = 100.;
23    let height = 100.;
24    let computation_time_max = Duration::new(2, 0);
25
26    // create random cities
27    let seed = 0;
28    let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
29    let cities: Vec<City> = (0..n)
30        .map(|id| create_random_city(id, width, height, &mut rng))
31        .collect();
32    let cities = Box::new(cities);
33
34    let now = SystemTime::now();
35    let random_tour = construct_random_tour(&mut cities.clone().into_iter(), &mut rng);
36    let duration_random = now.elapsed().unwrap();
37    let random_outcome = Outcome::new(random_tour, duration_random);
38    let now = SystemTime::now();
39    let greedy_tour = construct_greedy_tour(&mut cities.clone().into_iter(), &mut rng);
40    let duration_greedy = now.elapsed().unwrap();
41    let greedy_outcome = Outcome::new(greedy_tour, duration_greedy);
42
43    // optimize with VNS
44    let operator1 = TwoOpt::new(cities.as_slice());
45    let operator2 = Insertion::new(cities.as_slice());
46    let vns = VariableNeighborhoodSearch::builder()
47        .selector(
48            SequentialSelector::new()
49                .option(operator1)
50                .option(operator2),
51        )
52        .terminator(TimeTerminator::new(computation_time_max))
53        .build();
54    let vns_outcome = vns.optimize_timed(random_outcome.solution().clone());
55
56    // optimize with Simulated Annealing
57    let temperature = 100.;
58    let cooling_factor = 0.05;
59    let minimum_acceptance_probability = 0.05;
60    let schedule = FactorSchedule::new(temperature, cooling_factor);
61    let operator = TwoOpt::new(cities.as_slice());
62    let sa = SimulatedAnnealing::builder()
63        .selector(RandomSelector::new(rng.clone()).option(operator))
64        .operator(TwoOptRandom)
65        .cooling_schedule(schedule)
66        .terminator(
67            Terminator::builder()
68                .computation_time(computation_time_max)
69                .build(),
70        )
71        .minimum_acceptance_probability(minimum_acceptance_probability)
72        .rng(rng.clone())
73        .build();
74    let sa_outcome = sa.optimize_timed(random_outcome.solution().clone());
75
76    // optimize with Large Neighborhood Search
77    let n_destroyed_cities = 2;
78    let destroyer = TSPDestroyer::new(n_destroyed_cities);
79    let repairer = TSPRepairer::new(*cities.clone());
80    let lns = LargeNeighborhoodSearch::builder()
81        .selector_destroyer(SequentialSelector::new().option(destroyer))
82        .selector_repairer(SequentialSelector::new().option(repairer))
83        .terminator(
84            Terminator::builder()
85                .computation_time(computation_time_max)
86                .build(),
87        )
88        .rng(rng.clone())
89        .build();
90    let lns_outcome = lns.optimize_timed(random_outcome.solution().clone());
91
92    // optimize with adaptive VNS
93    let decay = 0.5;
94    let operator1 = TwoOpt::new(cities.as_slice());
95    let operator2 = Insertion::new(cities.as_slice());
96    let adaptive_vns = VariableNeighborhoodSearch::builder()
97        .selector(
98            AdaptiveSelector::default_weights(decay, rng)
99                .operator(operator1)
100                .operator(operator2),
101        )
102        .terminator(TimeTerminator::new(computation_time_max))
103        .build();
104    let adaptive_vns_outcome = adaptive_vns.optimize_timed(random_outcome.solution().clone());
105
106    // display results
107    show_solution(random_outcome, "random");
108    show_solution(greedy_outcome, "greedy");
109    show_solution(vns_outcome, "vns");
110    show_solution(adaptive_vns_outcome, "adaptive vns");
111    show_solution(sa_outcome, "sa");
112    show_solution(lns_outcome, "lns");
113}
114
115#[derive(Clone, Debug)]
116struct City {
117    id: usize,
118    x: f32,
119    y: f32,
120}
121
122#[derive(Clone, Debug)]
123struct Tour {
124    cities: Vec<City>,
125}
126
127#[derive(Clone)]
128struct TwoOpt {
129    tour: Option<Tour>,
130    cities: Box<Vec<City>>,
131    index1: usize,
132    index2: usize,
133}
134
135struct TwoOptRandom;
136
137struct Insertion {
138    tour: Option<Tour>,
139    cities: Box<Vec<City>>,
140    index1: usize,
141    index2: usize,
142}
143
144struct TSPDestroyer {
145    n: usize,
146}
147
148struct TSPRepairer {
149    cities: Vec<City>,
150}
151
152fn show_solution<Solution: Evaluate>(outcome: Outcome<Solution>, method: &str) {
153    println!(
154        "{} tour length: {}, computation time: {}",
155        method,
156        outcome.solution().evaluate(),
157        outcome.duration().as_nanos() as f32 * 1e-9
158    );
159}
160
161impl TSPRepairer {
162    fn new(cities: Vec<City>) -> Self {
163        Self { cities }
164    }
165}
166
167impl TSPDestroyer {
168    pub fn new(n: usize) -> Self {
169        Self { n }
170    }
171}
172
173impl Operator for TSPRepairer {
174    type Solution = Tour;
175    fn shake(&self, mut solution: Self::Solution, _rng: &mut dyn rand::RngCore) -> Self::Solution {
176        let map: HashMap<City, usize> = self
177            .cities
178            .iter()
179            .enumerate()
180            .map(|(index, city)| (city.clone(), index))
181            .collect();
182        let cities: HashSet<City> = self.cities.iter().map(|x| x.to_owned()).collect();
183        let cities_tour: HashSet<City> = solution.cities.clone().into_iter().collect();
184        let cities_missing: HashSet<City> = &cities - &cities_tour;
185        let mut cities_missing: Vec<City> = cities_missing.into_iter().collect();
186        cities_missing.sort_by(|x, y| {
187            let index_x = map[x];
188            let index_y = map[y];
189            index_x.cmp(&index_y)
190        });
191
192        for city in cities_missing {
193            let index_to_place = closest_city_to(&city, &solution.cities);
194            solution.cities.insert(index_to_place, city);
195        }
196
197        solution
198    }
199}
200
201fn closest_city_to<'a>(city: &'a City, city_pool: &'a Vec<City>) -> usize {
202    let mut city_closest_index = 0;
203    let mut distance_minimum = distance(city, &city_pool[0]);
204    for i in 1..city_pool.len() {
205        let distance = distance(city, &city_pool[i]);
206        if distance < distance_minimum {
207            distance_minimum = distance;
208            city_closest_index = i;
209        }
210    }
211    city_closest_index
212}
213
214impl Operator for TSPDestroyer {
215    type Solution = Tour;
216    fn shake(&self, mut solution: Self::Solution, rng: &mut dyn rand::RngCore) -> Self::Solution {
217        for _ in 0..self.n {
218            let r = rng.gen_range(0..solution.cities.len());
219            solution.cities.remove(r);
220        }
221        solution
222    }
223}
224
225impl Operator for TwoOptRandom {
226    type Solution = Tour;
227    fn shake(&self, solution: Tour, rng: &mut dyn rand::RngCore) -> Self::Solution {
228        let n = solution.cities.len();
229        let index1 = rng.gen_range(0..n);
230        let index2 = rng.gen_range(0..n);
231
232        let mut neighbor = solution.clone();
233        neighbor.cities.swap(index1, index2);
234        neighbor
235    }
236}
237
238impl<'a> TwoOpt {
239    fn new(cities: &[City]) -> Self {
240        Self {
241            tour: None,
242            cities: Box::new(cities.to_owned()),
243            index1: 0,
244            index2: 0,
245        }
246    }
247
248    // advance index2 before index1
249    fn advance(&mut self) -> bool {
250        let n = self.cities.len();
251        // index1 free
252        if self.index2 + 1 < n {
253            self.index2 += 1;
254        }
255        // index1 blocked, index2 blocked
256        else if self.index1 + 1 == n {
257            return false;
258        }
259        // index1 free, index2 blocked
260        else if self.index1 + 1 < n {
261            self.index2 = 0;
262            self.index1 += 1;
263        } else {
264            panic!("unknown state");
265        }
266
267        if self.index1 == self.index2 {
268            self.advance()
269        } else {
270            true
271        }
272    }
273}
274
275impl<'a> Iterator for TwoOpt {
276    type Item = Tour;
277
278    fn next(&mut self) -> Option<Self::Item> {
279        // advance by one
280        if !self.advance() {
281            return None;
282        }
283
284        // return tour with the two cities swapped
285        if let Some(tour) = &self.tour {
286            Some(tour.swap(self.index1, self.index2))
287        } else {
288            None
289        }
290    }
291}
292
293impl Operator for TwoOpt {
294    type Solution = Tour;
295    fn construct_neighborhood(&self, solution: Tour) -> Box<dyn Iterator<Item = Tour>> {
296        let mut neighborhood = Self::new(self.cities.as_ref());
297        neighborhood.tour = Some(solution.clone());
298        Box::new(neighborhood)
299    }
300
301    fn shake(&self, solution: Self::Solution, rng: &mut dyn rand::RngCore) -> Self::Solution {
302        let n = solution.cities.len();
303        let index1 = rng.gen_range(0..n);
304        let index2 = rng.gen_range(0..n);
305
306        let mut neighbor = solution.clone();
307        neighbor.cities.swap(index1, index2);
308        neighbor
309    }
310}
311
312impl Insertion {
313    fn new(cities: &[City]) -> Self {
314        Self {
315            tour: None,
316            cities: Box::new(cities.to_owned()),
317            index1: 0,
318            index2: 1,
319        }
320    }
321
322    // advance index3, index2, index1
323    fn advance(&mut self) -> bool {
324        // init
325        let number_cities = self.cities.len();
326
327        // index1 locked, index2 locked
328        if self.index1 == number_cities - 1 && self.index2 == number_cities - 1 {
329            return false;
330        }
331        // index1 unlocked, index2 locked
332        else if self.index1 < number_cities - 1 && self.index2 == number_cities - 1 {
333            self.index1 += 1;
334            self.index2 = 0;
335        }
336        // index 1 ?, index2 unlocked
337        else {
338            self.index2 += 1;
339        }
340
341        if self.index1 == self.index2 {
342            self.advance()
343        } else {
344            true
345        }
346    }
347}
348
349impl Iterator for Insertion {
350    type Item = Tour;
351
352    fn next(&mut self) -> Option<Self::Item> {
353        if !self.advance() {
354            return None;
355        }
356
357        if let Some(tour) = &self.tour {
358            let tour = tour.reinsert(self.index1, self.index2);
359            return Some(tour);
360        } else {
361            None
362        }
363    }
364}
365
366impl Operator for Insertion {
367    type Solution = Tour;
368    fn construct_neighborhood(&self, solution: Tour) -> Box<dyn Iterator<Item = Tour>> {
369        let mut neighborhood = Self::new(self.cities.as_ref());
370        neighborhood.tour = Some(solution.clone());
371        Box::new(neighborhood)
372    }
373}
374
375impl City {
376    fn new(id: usize, x: f32, y: f32) -> Self {
377        Self { id, x, y }
378    }
379
380    fn x(&self) -> f32 {
381        self.x
382    }
383
384    fn y(&self) -> f32 {
385        self.y
386    }
387}
388
389impl Eq for City {}
390
391impl PartialEq for City {
392    fn eq(&self, other: &Self) -> bool {
393        self.id == other.id
394    }
395}
396
397impl Hash for City {
398    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
399        self.id.hash(state)
400    }
401}
402
403impl Tour {
404    fn new(cities: Vec<City>) -> Self {
405        Self { cities }
406    }
407
408    // fn len(&self) -> usize {
409    //     self.cities.len()
410    // }
411
412    // fn get(&self, index: usize) -> Option<&City> {
413    //     self.cities.get(index)
414    // }
415
416    fn swap(&self, index1: usize, index2: usize) -> Tour {
417        let mut solution = self.clone();
418        solution.cities.swap(index1, index2);
419        solution
420    }
421
422    fn reinsert(&self, from: usize, to: usize) -> Tour {
423        let mut solution = self.clone();
424        let city = solution.cities.remove(from);
425        solution.cities.insert(to, city);
426        solution
427    }
428}
429
430impl Evaluate for Tour {
431    fn evaluate(&self) -> f32 {
432        if self.cities.is_empty() {
433            return 0.;
434        }
435
436        let mut sum = 0.;
437
438        for i in 0..self.cities.len() - 1 {
439            let ref city1 = self.cities[i];
440            let ref city2 = self.cities[i + 1];
441            sum += distance(city1, city2);
442        }
443
444        sum
445    }
446}
447
448fn construct_greedy_tour(cities: &mut dyn Iterator<Item = City>, rng: &mut dyn RngCore) -> Tour {
449    let mut cities: Vec<City> = cities.collect();
450    let index_initial_city = rng.gen_range(0..cities.len());
451    let city = cities.remove(index_initial_city);
452    let mut cities_tour = vec![city.clone()];
453
454    while !cities.is_empty() {
455        let city = remove_closest_city(&city, &mut cities);
456        cities_tour.push(city);
457    }
458
459    Tour::new(cities_tour)
460}
461
462fn construct_random_tour(cities: &mut dyn Iterator<Item = City>, rng: &mut dyn RngCore) -> Tour {
463    let mut cities: Vec<City> = cities.collect();
464    let mut cities_tour = vec![];
465
466    while !cities.is_empty() {
467        let index_city = rng.gen_range(0..cities.len());
468        let city = cities.remove(index_city);
469        cities_tour.push(city.clone());
470    }
471
472    Tour::new(cities_tour)
473}
474
475fn remove_closest_city(reference_city: &City, cities: &mut Vec<City>) -> City {
476    let distances = cities.iter().map(|city| distance(city, reference_city));
477
478    let mut iter = distances.enumerate();
479    let init = iter.next().unwrap();
480
481    let (index_closest, _) = iter.fold(init, |(index_accum, distance_accum), (index, distance)| {
482        if distance < distance_accum {
483            (index, distance)
484        } else {
485            (index_accum, distance_accum)
486        }
487    });
488
489    cities.remove(index_closest)
490}
491
492fn create_random_city(id: usize, width: f32, height: f32, rng: &mut dyn rand::RngCore) -> City {
493    let w = rng.gen::<f32>() * width;
494    let h = rng.gen::<f32>() * height;
495    City::new(id, w, h)
496}
497
498fn distance(city1: &City, city2: &City) -> f32 {
499    let delta_x = (city1.x() - city2.x()).abs();
500    let delta_y = (city1.y() - city2.y()).abs();
501
502    (delta_x.powf(2.) + delta_y.powf(2.)).sqrt()
503}