rml/math/
distance.rs

1// Copyright 2021 Jonathan Manly.
2
3// This file is part of rml.
4
5// rml is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// rml is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU Lesser General Public License for more details.
14
15// You should have received a copy of the GNU Lesser General Public License
16// along with rml.  If not, see <https://www.gnu.org/licenses/>.
17
18//! Computes distances needed for KNN.
19
20//! Supports both euclidean and manhattan distances.
21
22/// An enum which describes the two available types of distance calculations.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Distance {
25    Euclidean,
26    Manhattan,
27}
28
29/// Calculate the euclidean distance between two points.
30/// # Example
31/// ```rust
32/// let x: Vec<f64> = vec![5.0, 6.0];
33/// let y: Vec<f64> = vec![-7.0, 11.0];
34/// println!("{}", euclidean_distance(&x, &y))
35/// ```
36pub fn euclidean_distance(p: &[f64], q: &[f64]) -> f64 {
37    let distance: f64 = q.iter().zip(p).map(|(&q, &p)| (f64::powi(q - p, 2))).sum();
38
39    if distance == 0.0 {
40        0.0
41    } else {
42        distance.sqrt()
43    }
44}
45
46/// Calculate the Manhattan distance between two points.
47/// # Example
48/// ```rust
49/// let x: Vec<f64> = vec![5.0, 6.0];
50/// let y: Vec<f64> = vec![-7.0, 11.0];
51/// println!("{}", manhattan_distance(&x, &y))
52/// ```
53pub fn manhattan_distance(p: &[f64], q: &[f64]) -> f64 {
54    let distance: f64 = p.iter().zip(q).map(|(&p, &q)| (p - q).abs()).sum();
55
56    distance
57}
58
59#[cfg(test)]
60mod tests {
61
62    use super::*;
63
64    #[test]
65    fn euclidean_distance_test() {
66        let x: Vec<f64> = vec![5.0, 6.0];
67        let y: Vec<f64> = vec![-7.0, 11.0];
68
69        assert_eq!(euclidean_distance(&x, &y), 13.0);
70
71        let x: Vec<f64> = vec![0.0, 0.0, 0.0];
72        let y: Vec<f64> = vec![1.0, 1.0, 1.0];
73
74        assert_eq!(euclidean_distance(&x, &y), f64::from(3).sqrt());
75    }
76
77    #[test]
78    fn manhattan_distance_test() {
79        let x: Vec<f64> = vec![0.0, 0.0];
80        let y: Vec<f64> = vec![1.0, 1.0];
81
82        assert_eq!(manhattan_distance(&x, &y), 2.0);
83
84        let x: Vec<f64> = vec![0.0, 0.0];
85        let y: Vec<f64> = vec![-1.0, 1.0];
86
87        assert_eq!(manhattan_distance(&x, &y), 2.0);
88
89        let x: Vec<f64> = vec![0.0, 0.0, 0.0];
90        let y: Vec<f64> = vec![1.0, 1.0, 1.0];
91
92        assert_eq!(manhattan_distance(&x, &y), 3.0);
93    }
94}