tsp_rs/
lib.rs

1//! Crate of algorithms for solving the traveling salesman problem.
2//!
3//! # Example
4//! ```
5//! use std::time;
6//!
7//! use tsp_rs::Tour;
8//! use tsp_rs::point::Point;
9//!
10//! let tour: Vec<Point> = vec![
11//!     Point::new(0., 0.),
12//!     Point::new(0., 1.),
13//!     Point::new(1., 0.),
14//!     Point::new(1., 1.),
15//! ];
16//!
17//! let mut tour = Tour::from(&tour);
18//!
19//! tour.optimize_kopt(std::time::Duration::from_secs(1));
20//! ```
21//!
22//! _Disclaimer:_
23//!
24//! This is not a work of art, nor is it perfect (or even good?) Rust.
25//! This was written alongside my first reading of the Rust book (https://doc.rust-lang.org/book/)
26//! while trying to learn the language.
27mod kopt;
28mod nn;
29pub mod point;
30
31use std::borrow::Borrow;
32use std::collections::HashSet;
33use std::time;
34
35use rand::Rng;
36
37/// Trait used by all algorithms to calculate the cost of moving along an edge
38///
39/// # Examples
40/// An example implementation is found on `tsp::point::Point`, that implements
41/// standard euclidean distance as its metric.
42pub trait Metrizable {
43    fn cost(&self, other: &Self) -> f64;
44}
45
46/// Represents a solution to the tsp for the items T
47#[derive(Debug, Clone, PartialEq)]
48pub struct Tour<T: Metrizable> {
49    pub path: Vec<T>,
50}
51
52impl<T: Metrizable + Clone + Borrow<T>> Tour<T> {
53    /// Returns a new, empty Tour<T>
54    ///
55    /// # Example
56    /// ```
57    /// use tsp_rs::Tour;
58    /// use tsp_rs::point::Point;
59    ///
60    /// let tour: Tour<Point> = Tour::new();
61    /// ```
62    pub fn new() -> Tour<T> {
63        Tour {
64            path: Vec::new() as Vec<T>,
65        }
66    }
67
68    /// Returns a tour from `nodes: Vec<T>` passed in where the tour
69    /// is nodes[0] -> nodes[1] -> ... nodes[nodes.len() - 1] -> nodes[0]
70    ///
71    /// # Example
72    /// ```
73    /// use tsp_rs::Tour;
74    /// use tsp_rs::point::Point;
75    ///
76    /// let nodes = vec![
77    ///     Point::new(0., 0.),
78    ///     Point::new(1., 0.),
79    ///     Point::new(1., 1.),
80    ///     Point::new(0., 1.),
81    /// ];
82    ///
83    /// let tour = Tour::from(&nodes);
84    /// ```
85    pub fn from(nodes: &Vec<T>) -> Tour<T>
86    where
87        T: Clone,
88    {
89        Tour {
90            path: (*nodes).clone(),
91        }
92    }
93
94    /// Returns the length of a tour.
95    ///
96    /// # Example
97    /// let tour = Tour::from(&some_points);
98    /// let total_cost = tour.tour_len();
99    pub fn tour_len(&self) -> f64 {
100        if self.path.len() <= 0 {
101            return 0.;
102        }
103
104        let mut sum = 0.;
105        let mut prev = self.path.last().unwrap();
106        for curr in &self.path {
107            sum += prev.cost(&curr);
108            prev = &curr;
109        }
110        sum
111    }
112
113    /// Improves the tour in place using the 2opt heuristic (with 3opt kicks if it gets stuck)
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use std::time;
119    ///
120    /// use tsp_rs::Tour;
121    /// use tsp_rs::point::Point;
122    ///
123    /// let nodes = vec![
124    ///     Point::new(0., 0.),
125    ///     Point::new(1., 0.),
126    ///     Point::new(1., 1.),
127    ///     Point::new(0., 1.),
128    /// ];
129    ///
130    /// let mut tour = Tour::from(&nodes);
131    ///
132    /// tour.optimize_kopt(time::Duration::from_secs(1));
133    /// ```
134    pub fn optimize_kopt(&mut self, timeout: time::Duration) {
135        self.optimize_nn();
136        let start_time = time::Instant::now();
137        let max_iter_withouth_impr = self.path.len() ^ 2;
138        let mut iter_without_impr = 0;
139        let mut best_tour_length = std::f64::MAX;
140        let mut best_tour: Vec<T> = Vec::new();
141        loop {
142            match kopt::k_opt(2, self) {
143                Some(_) => {
144                    iter_without_impr = 0;
145                }
146                None => {
147                    iter_without_impr += 1;
148                    if iter_without_impr > max_iter_withouth_impr {
149                        let current_tour_length = self.tour_len();
150                        if current_tour_length < best_tour_length {
151                            best_tour = self.path.clone();
152                            best_tour_length = current_tour_length;
153                        }
154                        kopt::k_opt(4, self); // kick
155                        iter_without_impr = 0;
156                    }
157                }
158            }
159            if start_time.elapsed() > timeout {
160                break;
161            }
162        }
163        let current_tour_length = self.tour_len();
164        if current_tour_length < best_tour_length {
165            best_tour = self.path.clone();
166        }
167        self.path = best_tour;
168    }
169
170    /// Constructs a tour inplace using the nearest neighbor heuristic
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use tsp_rs::Tour;
176    /// use tsp_rs::point::Point;
177    ///
178    /// let nodes = vec![
179    ///     Point::new(0., 0.),
180    ///     Point::new(1., 0.),
181    ///     Point::new(1., 1.),
182    ///     Point::new(0., 1.),
183    /// ];
184    ///
185    /// let mut tour = Tour::from(&nodes);
186    ///
187    /// tour.optimize_nn();
188    /// ```
189    pub fn optimize_nn(&mut self)
190    where
191        T: Metrizable + Clone,
192    {
193        let mut path = Vec::new();
194        let nodes = index_path(&self.path);
195        let mut visited = HashSet::new();
196
197        let start_index: usize = rand::thread_rng().gen_range(0, nodes.len());
198        let mut curr = &nodes[start_index].value.clone();
199        path.push(curr.clone());
200        visited.insert(nodes[start_index].index);
201
202        loop {
203            match nn::nearest_neighbor(curr, &nodes, &mut visited) {
204                Some(next) => {
205                    path.push(next.clone());
206                    curr = &next;
207                }
208                None => {
209                    if visited.len() == nodes.len() {
210                        break;
211                    }
212                }
213            };
214        }
215
216        self.path = path;
217    }
218}
219
220#[derive(Clone)]
221pub(crate) struct IndexedT<T> {
222    pub index: usize,
223    pub value: T,
224}
225
226#[inline]
227pub(crate) fn index_path<T>(path: &Vec<T>) -> Vec<IndexedT<&T>> {
228    path.iter()
229        .enumerate()
230        .map(|(index, value)| IndexedT { index, value })
231        .collect()
232}