flannrust
A Rust port of nanoflann (the C++ kd-tree library), targeting bit-exact result parity with the C++ reference and equal-or-better speed. Static and dynamic indexes, with Python bindings.
Features
- Bit-exact parity with nanoflann 1.12.1 — every result index, distance, and internal tree permutation is cross-validated in-process against the vendored C++ reference on every commit (docs/testing.md)
- Static kd-tree (
KdTree) and dynamic Bentley–Saxe forest (DynamicKdTree) with point add/remove after construction - Matches or beats the C++ on most benchmarked workloads (table below)
- L1 / L2 / L2-simple / SO2 / SO3 metrics;
f32/f64; compile-time (ConstDim) or runtime (DynDim) dimension;u32/u64/usizeindices - Optional parallel build via rayon (default feature
parallel) - Python bindings:
flannrust.KDTree/DynamicKDTree, NumPy in/out, GIL released during build and query - Exactly two
unsafeblocks, both miri-verified in CI
Installation
Not yet published to crates.io / PyPI — until then, use git / build from source.
Rust:
[]
= { = "https://github.com/sitzikbs/flannrust" }
Python (from a clone, inside a virtualenv):
MSRV: Rust 1.98.0 (pinned in rust-toolchain.toml).
Quick start
Rust
use ;
let pts: & = &;
let tree = new.build;
let mut indices = ;
let mut dists = ;
let found = tree.knn_search;
assert_eq!;
assert_eq!; // nearest point is [0.0, 0.0, 0.0]
Python
=
=
=
, = # dists are SQUARED l2
, = # r is SQUARED too, strict `<`
=
# lazy tombstone
, =
Distances and radii are SQUARED for
l2/l2_simple— unlikescipy.spatial.cKDTree. Square your radius before calling; expect squared values back (l1is an unsquared sum of absolute differences). This is the single most common mistake porting code fromcKDTree.
Performance
Six gated workloads vs. the vendored C++ oracle, ratio = Rust time / C++ time (lower is better for Rust). Latest idle-host re-measurement (M2.6, 3 sessions, statistical harness):
| Workload | Ratio (Rust / C++) |
|---|---|
| build 100k, dim 3, f32, sequential | 0.99–1.01 |
| knn, fixed dim 3, f32, k=10 | 1.01–1.04 |
| knn, runtime dim 8, f64, k=10 | 0.93–0.94 |
| radius, dim 3, f32 | 0.83–0.87 |
| dynamic add 20k, dim 3, f32 | 1.03–1.04 |
| dynamic knn after churn, dim 3, f32 | 0.93–0.96 |
Measured on WSL2, AMD Ryzen 7 9800X3D, rustc 1.98.0, -C target-cpu=native
vs. C++ -O3 -march=native -ffp-contract=off. Full methodology, history,
honest residuals, and a portable repro kit:
docs/benchmarks.md,
docs/EXPERIMENTS.md,
docs/benchkit.md.
Documentation
- API docs:
cargo doc -p flannrust --open(docs.rs after publish); Python docstrings on every class/method - docs/semantics.md — exact behavioral contracts, deliberate deviations from C++, input domain, feature flags, dynamic adaptor and Python API details
- docs/testing.md — how bit-exact parity is verified (cross-validation matrix, dynamic op-sequence suite, miri, canaries)
- docs/benchmarks.md / docs/benchkit.md — the numbers and how to reproduce them on your hardware
- docs/ROADMAP.md — what's next (M3 incremental adaptor, M4 multithreaded wrapper, serialization)
- CONTRIBUTING.md — dev setup; note that
cross-validation against the C++ oracle needs a C++17 compiler
(
cargo test --workspace)
How this was built
Every line of Rust, C++ FFI, and Python-binding code here was written by an AI agent (Claude Code), directed and reviewed by Itzik Ben-Shabat. Correctness does not rest on human code review — it rests on the bit-exact cross-validation suite run against the real C++ library on every change, and every performance figure traces to a pasted, reproducible run. The full process record — plans, specs, and per-task reports: docs/agentic-development/.
License
BSD-2-Clause — see LICENSE. flannrust is a derivative work of
nanoflann by Jose Luis
Blanco-Claraco et al., which builds on FLANN by Marius Muja and David G.
Lowe; the upstream copyright notices are retained in LICENSE. The vendored
nanoflann.hpp (used only as a test/benchmark oracle, not part of the Rust
library) keeps its original license header verbatim.