kiddo 6.0.2

A high-performance, flexible, ergonomic k-d tree library. Ideal for geo- and astro- nearest-neighbour and k-nearest-neighbor queries
Documentation
use std::num::NonZeroUsize;

use kiddo::dist::SquaredEuclidean;
use kiddo::leaf_view::LeafView;
use kiddo::traits::leaf_strategy::{
    BucketLimitType, ConstructibleLeafStrategy, Immutable, LeafProjection,
};
use kiddo::{Axis, Content, Eytzinger, KdTree, LeafStrategy, StemStrategy};

struct ExternalFlatLeaf<A, T, const K: usize, const B: usize> {
    leaf_points: [Vec<A>; K],
    leaf_items: Vec<T>,
    leaf_extents: Vec<(u32, u32)>,
    size: usize,
}

impl<A, T, SS, const K: usize, const B: usize> LeafStrategy<A, T, SS, K, B>
    for ExternalFlatLeaf<A, T, K, B>
where
    A: Axis<Coord = A>,
    T: Content,
    SS: StemStrategy,
{
    type Num = A;
    type Mutability = Immutable;

    const BUCKET_LIMIT_TYPE: BucketLimitType = BucketLimitType::Soft;
    const LEAF_PROJECTION: LeafProjection = LeafProjection::LeafView;

    fn size(&self) -> usize {
        self.size
    }

    fn leaf_count(&self) -> usize {
        self.leaf_extents.len()
    }

    fn leaf_len(&self, leaf_idx: usize) -> usize {
        let (start, end) = self.leaf_extents[leaf_idx];
        (end - start) as usize
    }

    fn leaf_view(&self, leaf_idx: usize) -> LeafView<'_, A, T, K, B> {
        let (start, end) = self.leaf_extents[leaf_idx];
        let start = start as usize;
        let end = end as usize;

        let points = array_init::array_init(|dim| &self.leaf_points[dim][start..end]);
        let items = &self.leaf_items[start..end];

        LeafView::new(points, items)
    }
}

impl<A, T, SS, const K: usize, const B: usize> ConstructibleLeafStrategy<A, T, SS, K, B>
    for ExternalFlatLeaf<A, T, K, B>
where
    A: Axis<Coord = A>,
    T: Content,
    SS: StemStrategy,
{
    fn new_with_capacity(capacity: usize) -> Self {
        Self {
            leaf_points: array_init::array_init(|_| Vec::with_capacity(capacity)),
            leaf_items: Vec::with_capacity(capacity),
            leaf_extents: Vec::new(),
            size: 0,
        }
    }

    fn append_leaf(&mut self, leaf_points: &[&[A]; K], leaf_items: &[T]) {
        let start = self.leaf_items.len() as u32;
        let len = leaf_items.len();
        let end = start + len as u32;

        for (dim, item) in leaf_points.iter().enumerate() {
            self.leaf_points[dim].extend_from_slice(item);
        }
        self.leaf_items.extend_from_slice(leaf_items);
        self.leaf_extents.push((start, end));
        self.size += len;
    }
}

type TestTree = KdTree<f64, u32, Eytzinger, ExternalFlatLeaf<f64, u32, 2, 32>, 2, 32>;

fn query_items<const K: usize>(
    tree: &KdTree<f64, u32, Eytzinger, ExternalFlatLeaf<f64, u32, K, 32>, K, 32>,
    point: &[f64; K],
    n: NonZeroUsize,
) -> Vec<u32> {
    tree.query(point)
        .nearest_n::<SquaredEuclidean<f64>>(n)
        .execute()
        .into_iter()
        .map(|result| result.item)
        .collect()
}

#[test]
fn external_leaf_strategy_supports_build_and_execute_query() {
    let points = [
        (10u32, [0.0f64, 0.0]),
        (20u32, [1.0, 1.0]),
        (30u32, [2.0, 2.0]),
    ];
    let tree: TestTree = KdTree::new_from_entries(&points).unwrap();

    assert_eq!(tree.size(), 3);
    assert_eq!(tree.leaf_count(), 1);

    let items = query_items::<2>(&tree, &[0.1, 0.1], NonZeroUsize::new(2).unwrap());

    assert_eq!(items, vec![10, 20]);
}