use ndarray::{ArrayView, Dimension, Zip};
#[derive(Clone, Copy)]
pub enum KMeansDist {
Euclidean,
Manhattan,
Chebyshev,
Minkowski(f64),
Cosine,
}
impl KMeansDist {
pub fn run<D>(&self, a: ArrayView<f64, D>, b: ArrayView<f64, D>) -> f64
where
D: Dimension,
{
match self {
KMeansDist::Euclidean => {
if let (Ok(a), Ok(b)) = (a.view().into_shape(a.len()), b.view().into_shape(b.len()))
{
(a.dot(&a) - 2.0 * a.dot(&b) + b.dot(&b)).sqrt()
} else {
Zip::from(a)
.and(b)
.fold(0.0, |acc, x, y| acc + (x - y).powi(2))
.sqrt()
}
}
KMeansDist::Manhattan => Zip::from(a)
.and(b)
.fold(0.0, |acc, x, y| acc + (x - y).abs()),
KMeansDist::Chebyshev => Zip::from(a)
.and(b)
.fold(0.0, |acc, x, y| acc.max((x - y).abs())),
KMeansDist::Minkowski(p) => Zip::from(a)
.and(b)
.fold(0.0, |acc, x, y| acc + (x - y).abs().powf(*p))
.powf(1.0 / *p),
KMeansDist::Cosine => {
let dot = Zip::from(&a).and(&b).fold(0.0, |acc, x, y| acc + x * y);
let norm_a = Zip::from(a).fold(0.0, |acc, x| acc + x.powi(2)).sqrt();
let norm_b = Zip::from(b).fold(0.0, |acc, y| acc + y.powi(2)).sqrt();
1.0 - (dot / (norm_a * norm_b))
}
}
}
}