diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2020-06-24 15:20:02 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2020-06-24 15:44:14 -0400 |
commit | 39c0348c9f98b4dd29bd112a0a2a42faa67c92d4 (patch) | |
tree | 6c8ed80bd8cbbb0af79c9ac57bdb39634fa178fd /src/metric/kd.rs | |
parent | adaafdd7043507cbceae65e78c38954e47103b5c (diff) | |
download | kd-forest-39c0348c9f98b4dd29bd112a0a2a42faa67c92d4.tar.xz |
Diffstat (limited to 'src/metric/kd.rs')
-rw-r--r-- | src/metric/kd.rs | 224 |
1 files changed, 0 insertions, 224 deletions
diff --git a/src/metric/kd.rs b/src/metric/kd.rs deleted file mode 100644 index 6ea3809..0000000 --- a/src/metric/kd.rs +++ /dev/null @@ -1,224 +0,0 @@ -//! [k-d trees](https://en.wikipedia.org/wiki/K-d_tree). - -use super::{Metric, NearestNeighbors, Neighborhood, Ordered}; - -use std::iter::FromIterator; - -/// A point in Cartesian space. -pub trait Cartesian: Metric<[f64]> { - /// Returns the number of dimensions necessary to describe this point. - fn dimensions(&self) -> usize; - - /// Returns the value of the `i`th coordinate of this point (`i < self.dimensions()`). - fn coordinate(&self, i: usize) -> f64; -} - -/// Blanket [Cartesian] implementation for references. -impl<'a, T: Cartesian> Cartesian for &'a T { - fn dimensions(&self) -> usize { - (*self).dimensions() - } - - fn coordinate(&self, i: usize) -> f64 { - (*self).coordinate(i) - } -} - -/// Blanket [Metric<[f64]>](Metric) implementation for [Cartesian] references. -impl<'a, T: Cartesian> Metric<[f64]> for &'a T { - type Distance = T::Distance; - - fn distance(&self, other: &[f64]) -> Self::Distance { - (*self).distance(other) - } -} - -/// Standard cartesian space. -impl Cartesian for [f64] { - fn dimensions(&self) -> usize { - self.len() - } - - fn coordinate(&self, i: usize) -> f64 { - self[i] - } -} - -/// Marker trait for cartesian metric spaces. -pub trait CartesianMetric<T: ?Sized = Self>: - Cartesian + Metric<T, Distance = <Self as Metric<[f64]>>::Distance> -{ -} - -/// Blanket [CartesianMetric] implementation for cartesian spaces with compatible metric distance -/// types. -impl<T, U> CartesianMetric<T> for U -where - T: ?Sized, - U: ?Sized + Cartesian + Metric<T, Distance = <U as Metric<[f64]>>::Distance>, -{ -} - -/// A node in a k-d tree. -#[derive(Debug)] -struct KdNode<T> { - /// The value stored in this node. - item: T, - /// The size of the left subtree. - left_len: usize, -} - -impl<T: Cartesian> KdNode<T> { - /// Create a new KdNode. - fn new(item: T) -> Self { - Self { item, left_len: 0 } - } - - /// Build a k-d tree recursively. - fn build(slice: &mut [KdNode<T>], i: usize) { - if slice.is_empty() { - return; - } - - slice.sort_unstable_by_key(|n| Ordered(n.item.coordinate(i))); - - let mid = slice.len() / 2; - slice.swap(0, mid); - - let (node, children) = slice.split_first_mut().unwrap(); - let (left, right) = children.split_at_mut(mid); - node.left_len = left.len(); - - let j = (i + 1) % node.item.dimensions(); - Self::build(left, j); - Self::build(right, j); - } - - /// Recursively search for nearest neighbors. - fn recurse<'a, U, N>( - slice: &'a [KdNode<T>], - i: usize, - closest: &mut [f64], - neighborhood: &mut N, - ) where - T: 'a, - U: CartesianMetric<&'a T>, - N: Neighborhood<&'a T, U>, - { - let (node, children) = slice.split_first().unwrap(); - neighborhood.consider(&node.item); - - let target = neighborhood.target(); - let ti = target.coordinate(i); - let ni = node.item.coordinate(i); - let j = (i + 1) % node.item.dimensions(); - - let (left, right) = children.split_at(node.left_len); - let (near, far) = if ti <= ni { - (left, right) - } else { - (right, left) - }; - - if !near.is_empty() { - Self::recurse(near, j, closest, neighborhood); - } - - if !far.is_empty() { - let saved = closest[i]; - closest[i] = ni; - if neighborhood.contains_distance(target.distance(closest)) { - Self::recurse(far, j, closest, neighborhood); - } - closest[i] = saved; - } - } -} - -/// A [k-d tree](https://en.wikipedia.org/wiki/K-d_tree). -#[derive(Debug)] -pub struct KdTree<T>(Vec<KdNode<T>>); - -impl<T: Cartesian> FromIterator<T> for KdTree<T> { - /// Create a new k-d tree from a set of points. - fn from_iter<I: IntoIterator<Item = T>>(items: I) -> Self { - let mut nodes: Vec<_> = items.into_iter().map(KdNode::new).collect(); - KdNode::build(nodes.as_mut_slice(), 0); - Self(nodes) - } -} - -impl<T, U> NearestNeighbors<T, U> for KdTree<T> -where - T: Cartesian, - U: CartesianMetric<T>, -{ - fn search<'a, 'b, N>(&'a self, mut neighborhood: N) -> N - where - T: 'a, - U: 'b, - N: Neighborhood<&'a T, &'b U>, - { - if !self.0.is_empty() { - let target = neighborhood.target(); - let dims = target.dimensions(); - let mut closest: Vec<_> = (0..dims).map(|i| target.coordinate(i)).collect(); - - KdNode::recurse(&self.0, 0, &mut closest, &mut neighborhood); - } - - neighborhood - } -} - -/// An iterator that the moves values out of a k-d tree. -#[derive(Debug)] -pub struct IntoIter<T>(std::vec::IntoIter<KdNode<T>>); - -impl<T> Iterator for IntoIter<T> { - type Item = T; - - fn next(&mut self) -> Option<T> { - self.0.next().map(|n| n.item) - } -} - -impl<T> IntoIterator for KdTree<T> { - type Item = T; - type IntoIter = IntoIter<T>; - - fn into_iter(self) -> Self::IntoIter { - IntoIter(self.0.into_iter()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::metric::tests::{test_nearest_neighbors, Point}; - use crate::metric::SquaredDistance; - - impl Metric<[f64]> for Point { - type Distance = SquaredDistance; - - fn distance(&self, other: &[f64]) -> Self::Distance { - self.0.distance(other) - } - } - - impl Cartesian for Point { - fn dimensions(&self) -> usize { - self.0.dimensions() - } - - fn coordinate(&self, i: usize) -> f64 { - self.0.coordinate(i) - } - } - - #[test] - fn test_kd_tree() { - test_nearest_neighbors(KdTree::from_iter); - } -} |