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}