From af325f5a4ff967314fb484b77b33406d21133393 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 May 2020 14:49:32 -0400 Subject: kd: Implement flat k-d trees --- src/kd.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) (limited to 'src') diff --git a/src/kd.rs b/src/kd.rs index 97616e7..4f591f9 100644 --- a/src/kd.rs +++ b/src/kd.rs @@ -333,6 +333,156 @@ where V: Coordinates, {} +/// A node in a flat k-d tree. +#[derive(Debug)] +struct FlatKdNode { + /// The vantage point itself. + item: T, + /// The size of the left subtree. + left_len: usize, +} + +impl FlatKdNode { + /// Create a new FlatKdNode. + fn new(item: T) -> Self { + Self { + item, + left_len: 0, + } + } + + /// Create a balanced tree. + fn balanced>(items: I) -> Vec { + let mut nodes: Vec<_> = items + .into_iter() + .map(Self::new) + .collect(); + + Self::balance_recursive(&mut nodes, 0); + + nodes + } + + /// Create a balanced subtree. + fn balance_recursive(nodes: &mut [Self], level: usize) { + if !nodes.is_empty() { + nodes.sort_by_cached_key(|x| Ordered::new(x.item.coord(level))); + + let mid = nodes.len() / 2; + nodes.swap(0, mid); + + let (node, children) = nodes.split_first_mut().unwrap(); + let (left, right) = children.split_at_mut(mid); + node.left_len = left.len(); + + let next = (level + 1) % node.item.dims(); + Self::balance_recursive(left, next); + Self::balance_recursive(right, next); + } + } +} + +impl<'a, K, V, N> KdSearch for &'a [FlatKdNode] +where + K: KdProximity<&'a V>, + V: Coordinates, + N: Neighborhood, +{ + fn item(self) -> &'a V { + &self[0].item + } + + fn left(self) -> Option { + let end = self[0].left_len + 1; + if end > 1 { + Some(&self[1..end]) + } else { + None + } + } + + fn right(self) -> Option { + let start = self[0].left_len + 1; + if start < self.len() { + Some(&self[start..]) + } else { + None + } + } +} + +/// A [k-d tree] stored as a flat array. +/// +/// A FlatKdTree is always balanced and usually more efficient than a [KdTree], but doesn't support +/// dynamic updates. +/// +/// [k-d tree]: https://en.wikipedia.org/wiki/K-d_tree +#[derive(Debug)] +pub struct FlatKdTree { + nodes: Vec>, +} + +impl FlatKdTree { + /// Create a balanced tree out of a sequence of items. + pub fn balanced>(items: I) -> Self { + Self { + nodes: FlatKdNode::balanced(items), + } + } +} + +impl FromIterator for FlatKdTree { + fn from_iter>(items: I) -> Self { + Self::balanced(items) + } +} + +/// An iterator that moves values out of a flat k-d tree. +#[derive(Debug)] +pub struct FlatIntoIter(std::vec::IntoIter>); + +impl Iterator for FlatIntoIter { + type Item = T; + + fn next(&mut self) -> Option { + self.0.next().map(|n| n.item) + } +} + +impl IntoIterator for FlatKdTree { + type Item = T; + type IntoIter = FlatIntoIter; + + fn into_iter(self) -> Self::IntoIter { + FlatIntoIter(self.nodes.into_iter()) + } +} + +impl NearestNeighbors for FlatKdTree +where + K: KdProximity, + V: Coordinates, +{ + fn search<'k, 'v, N>(&'v self, mut neighborhood: N) -> N + where + K: 'k, + V: 'v, + N: Neighborhood<&'k K, &'v V>, + { + if !self.nodes.is_empty() { + let mut closest = neighborhood.target().as_vec(); + self.nodes.as_slice().search(0, &mut closest, &mut neighborhood); + } + neighborhood + } +} + +impl ExactNeighbors for FlatKdTree +where + K: KdMetric, + V: Coordinates, +{} + #[cfg(test)] mod tests { use super::*; @@ -354,4 +504,9 @@ mod tests { tree }); } + + #[test] + fn test_flat_kd_tree() { + test_nearest_neighbors(FlatKdTree::from_iter); + } } -- cgit v1.2.3