Skip to main content

logo torann

TORoidal Approximate Nearest Neighbours

PyPI - Version PyPI - Python Version GitHub License GitHub Actions Workflow Status GitHub last commit

torann is exact + approximate k-NN and range search on the unit torus $[0,1)^d$ under toroidal L1 — a metric mainstream ANN libraries do not offer, chosen deliberately: L1 degrades more gracefully than L2/cosine in high dimensions, and on the torus the LSH guarantee is exact. Built for ESS-style epoch workloads: static anchors, a moving candidate tier, selective updates, batch promotion.

Features

  • The metric is the contract: toroidal L1, exact distances everywhere — the LSH only filters candidates, never approximates a distance.
  • An LSH family that is exactly L1-sensitive on the torus: randomly rotated integer grids with a closed-form, seam-free collision law (see How it works).
  • ESS-shaped lifecycle: two tiers (anchors + candidates), selective update() that re-places only points whose hash cell changed, promote() as a linear merge — never a re-sort, exact after every step.
  • No brute-force fallback: under-filled queries widen buckets by prefix relaxation (contiguous sorted-key ranges), so k results are structurally guaranteed.
  • Self-tuning: fit(..., k=...) or radius=... derives the hash parameters (B, K, L) from the workload; explicit arguments always win.
  • Three interchangeable implementations of one interface (torann/base.py): exact NumPy brute force, a pure-Python LSH reference, and a Rust core (PyO3 + rayon) that produces byte-identical hash tables at 60–120× the speed. Without the compiled module — or on a CPU below the AVX2 floor — the package still runs, on the reference implementation.
  • No unsafe: the SIMD kernel is wide, a safe stable-Rust wrapper. Hand-written core::arch intrinsics were measured at 21% faster and declined; that trade is deliberate and stays open.

Installation

From PyPI

pip install torann

From source

The project is a maturin mixed Rust/Python package — a Rust toolchain is required to build the native core:

python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install .

Requirements: Python ≥ 3.12, numpy.

Rust ≥ 1.98 is required to build the native core: the distance kernel uses the algebraic floating-point methods stabilized in that release.

CPU: published x86-64 wheels are built with an AVX2 + FMA floor — Intel Haswell (2013) and AMD Excavator (2015) onward. That is not gratuitous: the distance kernel is a plain scalar loop that the compiler auto-vectorizes, and without AVX it has no 256-bit register to lower to, costing 17% (340 ms → 408 ms on the reference shape). AVX-512 is not used: it is a further 4% at d = 32 but nearly 2× worse at d = 8. On a CPU without AVX2 the compiled backend is not loaded and the pure-Python implementation is used instead, with a warning; installing from the sdist builds a native module for whatever the machine has. arm64 needs no floor — NEON is baseline there, and the kernel names no vector type, so it follows the target rather than pinning a width.

Quick Start

import numpy as np
from torann import ToroidalNN

d = 16
static = np.random.rand(15_000, d)      # anchors: never move
batch = np.random.rand(3_000, d)        # candidates: move each epoch

nn = ToroidalNN(seed=42)
nn.fit(static, batch, k=2*d)            # build + tune from zero

for epoch in range(32):
    idx, dist = nn.query()              # each candidate vs everything
    new = force_step(nn.candidates, idx, dist)   # your physics here
    nn.update(new)                      # selective refresh

nn.promote(next_batch)                  # candidates freeze into anchors

nn.query_radius(0.25)                   # range query as a post-filter
nn.query(k=8, queries=Q)                # arbitrary external queries

Knobs (all optional — tuning fills them in): num_tables, resolution, dims_per_table, target_bucket_size, probes, brute_threshold, backend ("auto" prefers the fastest installed of rust, python).

How it works

The metric

On the torus, distance wraps: per dimension it is $\min(|a_i-b_i|,, 1-|a_i-b_i|)$, and the metric is the sum. Near an edge the nearest region of a query is not where a seam-blind index looks — it wraps around every boundary it touches. The teal points are the true 12-NN of the star:

the toroidal nearest region

The region follows the query around the torus:

the nearest region wraps

At d=16, distance concentration makes wrapping the common case: almost every true neighbour pair wraps in at least one dimension, which is why a seam-blind exact search misses ~74 % of the true toroidal neighbours (examples/compare_faiss.py).

The hash

One hashed dimension, with integer resolution $B \ge 2$ and a random offset $u \sim U[0,1)$:

$$c(x) = \lfloor B,((x+u) \bmod 1) \rfloor \qquad P[c(x){=}c(y)] = \max(0,, 1 - B\delta)$$

Because $B$ is an integer, the $B$ arcs tile the circle exactly — the grid has no seam, and the collision law is exact, not approximate (dots are measured frequencies):

the offset integer grid the collision law is exact

A table concatenates $K$ sampled dimensions into a base-$B$ key, so collisions decay as $\prod_j \max(0, 1-B\delta_j) \approx e^{-B \cdot L1}$ — inherently an L1 guarantee, which is why L1 is the public contract and no other metric is offered. A uniformly random pair collides per dimension with probability exactly $1/B$ (closed-form bucket load $n/B^K$), and a point that moves by $s$ changes its cell with probability $B s$ — churn is proportional to movement, which is what makes selective updates cheap. The rejected alternatives (p-stable projections cannot wrap; integer projections alias far points onto near ones) are measured in exploration/.

The index

index representation

Sorted key arrays make a bucket a contiguous range (an $O(1)$ direct-address offset table serves the static tier), keys are digit concatenations so prefix relaxation — dropping low-order digits — widens a bucket into a wider contiguous range without any distance scan, and every gathered candidate is refined with the exact toroidal L1 before the top-k. Full details: torann/lsh.py — the reference implementation, normative for the L1 hash.

Benchmarks

Measured on an AMD Ryzen AI 7 PRO 350 (16 threads), d=16, k=32, on a build carrying the AVX2 floor described under Installation — which is what the published wheels now ship, so these are numbers you get rather than numbers only the developer got. Regenerate the full grids, crossover and complexity tables with python examples/benchmark.py:

These figures predate the Rust 1.98 distance kernel and are now conservative. That rewrite is worth 8–50% on the query path depending on dimensionality, and d = 16 — the dimension charted here — is near the top of that range at ~27%. The plots are regenerated by the command above; the numbers below have not been re-measured since.

queries vs n

On the workload this library exists for — the ESS main loop, simulated end to end (examples/ess_sim.py) — torann is 1.7× faster than FAISS Flat rebuilt per epoch and correct, where FAISS's exact seam-blind L1 delivers 0.25–0.28 recall against the true toroidal neighbours:

the ESS main loop

Queries run 11–148 µs at 16 threads across n ∈ [20k, 1M] on torus data — 60–120× the NumPy pipeline — with 0.9–2.1 ms selective updates and ~1 s builds at n = 1M (HNSW: 24–32 s). On wrap-free data (FAISS's best case) torann sits within 1.06–1.56× of FAISS's exact SIMD Flat scan at equal ≈ 1.0 recall. Python and Rust implementations produce byte-identical hash tables, so their recall is identical by construction.

Running Unit Tests

The conformance suite runs once per installed backend and checks byte-identical tables plus equivalent query results across the whole lifecycle, using the standard unittest framework:

python -m unittest discover -s test

Documentation

The library uses Google-style docstrings; the API documentation is generated with pdoc by a GitHub Action and published here. To preview locally:

pip install pdoc
pdoc --math -d google torann torann.wrapper torann.base torann.brute torann.lsh torann.rust

Development

The checks in CI also run at commit time:

pip install pre-commit
pre-commit install

That gates each commit on ruff, basedpyright, vulture, the unit tests, and -- because the native core is held to the same standard -- cargo fmt --check and cargo clippy --release -- -D warnings. pre-commit run --all-files checks the tree without committing.

python examples/ess_sim.py             # the ESS main loop end to end, vs FAISS
python examples/bench_backends.py      # per-op grid over (n, d, backend)
python examples/crossover.py           # brute vs LSH crossover n*(d, backend)
python examples/compare_faiss_flat.py  # non-toroidal throughput vs FAISS
python examples/figures.py             # regenerate the README method figures
python examples/plot_benchmarks.py     # regenerate the benchmark figures
python exploration/exp_1d.py           # regenerate the concept experiments

maturin build --release produces the complete wheel (Cargo.toml + src/lib.rs are the native core; torann/ is the Python package). The C contender from the phase-6 bake-off is preserved at tag archive/backend-c.

Authors

License

This project is licensed under the MIT License - see the LICENSE file for details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

torann-0.3.0.tar.gz (2.8 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

torann-0.3.0-cp314-cp314-win_amd64.whl (267.7 kB view details)

Uploaded CPython 3.14Windows x86-64

torann-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (390.4 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

torann-0.3.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (382.7 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ ARM64

torann-0.3.0-cp314-cp314-macosx_11_0_arm64.whl (347.8 kB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

torann-0.3.0-cp314-cp314-macosx_10_12_x86_64.whl (359.9 kB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

torann-0.3.0-cp313-cp313-win_amd64.whl (268.0 kB view details)

Uploaded CPython 3.13Windows x86-64

torann-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (390.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

torann-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (382.3 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

torann-0.3.0-cp313-cp313-macosx_11_0_arm64.whl (347.8 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

torann-0.3.0-cp313-cp313-macosx_10_12_x86_64.whl (359.8 kB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

torann-0.3.0-cp312-cp312-win_amd64.whl (267.7 kB view details)

Uploaded CPython 3.12Windows x86-64

torann-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (389.9 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

torann-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (382.0 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

torann-0.3.0-cp312-cp312-macosx_11_0_arm64.whl (347.6 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

torann-0.3.0-cp312-cp312-macosx_10_12_x86_64.whl (359.6 kB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

File details

Details for the file torann-0.3.0.tar.gz.

File metadata

  • Download URL: torann-0.3.0.tar.gz
  • Upload date:
  • Size: 2.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for torann-0.3.0.tar.gz
Algorithm Hash digest
SHA256 f64929ed71632e59258bd9683d3dac5bf01c934621e50f2096f6e192719ba2ae
MD5 d4249815957a684d88131c5f75a92c04
BLAKE2b-256 2c387fe46b9a4f8de870c94ee4957c544f4f09facf4d0af6dc774b3299eaac60

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: torann-0.3.0-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 267.7 kB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for torann-0.3.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 d4afe65dfb98e78a7e3a840c1c52583958c46c68557e6afb34db815476e096ab
MD5 19965ce8d6d326ca4ba5dc964dcc66dc
BLAKE2b-256 ac8de8ab4329a67d366716bdfe14c1e09ef4daa1663b4e0d80251b1194cab220

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 54eea8a931f37e7749bf7e3307746ddbde2a20044b88ba221f8b5d48702721d8
MD5 003789dde58c56006305c05a1cb85814
BLAKE2b-256 155887c80fa3a6cdbad87263fd34a8384690ec42906585c7da3785f50725af3b

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 c4c0710faf53407cea88e3c79a43c7290fd6c92e9221e084473fd215c33e449a
MD5 e048ef47cdd6ccf11e5f57fb3ded540d
BLAKE2b-256 9174b38edc9713f6b3f5a2bd9f73f24c609ca2c6c1771419f9d5994e631cbafd

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7d89aec9a9a7034d09ff2a41b8601aa613caab896f61e3e2fa5ab793479af155
MD5 de61e9cd804a9108baaf646a5774af01
BLAKE2b-256 3b33563c8f539587f47ae2f65e5dc5cc013f0d0c0ecdd960a9d3b44e85c1169d

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp314-cp314-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 489f186705fcf23328e054933ee846188147b271cb8180e97a6520d09c71f0e5
MD5 f8d4dc51f259b4295540b6ef3f380a1b
BLAKE2b-256 acbd9641c0ef4ee9d79a388ce1d508f2b9bdc98535f2b6db929f519177a61148

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: torann-0.3.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 268.0 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for torann-0.3.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 d97639aa4e207ffa19536991893f778cfd1cc741ae468dcb8bf6d8d5770fb544
MD5 a8bb26856fbf48648029974b97d372e7
BLAKE2b-256 217895ab48ade322c9d38c8fb99af47702a6fbe46126e54055b0310c65930aca

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4276c43e03f485d0d5695bc6018fffe4f8d52fedf2d541ed78e4593009560e16
MD5 12132764b1e42428f73a4f7c435bcb8b
BLAKE2b-256 45cb7a7fd4eaf1d1eb1154427a6b22d584b576dfb3582f4ce640677e67b9e986

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ec5d6315d67269ab33b654a67831ab022eb81353b7deb44866feab1d437beb04
MD5 f70519edf5271d0b34a0c931ed9a4833
BLAKE2b-256 cc740956e98461bbb7dbc212f8760ac879112956cb03b7257e02208de86fe806

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 755467e7a10d39d792c667a60a288e29a08767b39eb7b20dcf0ac569948f2464
MD5 45e51f3136c880d4dd2cfb442b9f6313
BLAKE2b-256 ee9ec071dd5a363d51343f6ebda346ef018851f7499d7dff5e3a01d30153f30e

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 3d8d7746ed863685cc16952cb252523932d6bbab7c88f59fb73ed26246a033b2
MD5 04a8922776a48ab1cd38fa9126668466
BLAKE2b-256 0efd9c7f891204fe0c03a418accfc6dc978b28dcca3d1ea2f15492dc1adc7da5

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: torann-0.3.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 267.7 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for torann-0.3.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 dae4c16613e664564dd6273eff7d73f7e1ef050bfe9884130a590af413800a80
MD5 7792ba24b1e016251047f5b91892b626
BLAKE2b-256 dbf95c7c65c4f4a5bd6e9d2473c2236d489294ecbe05b6c2661cbef6c890c6fa

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 374c515ae899f564c075c0aad2d30dc4cac59b09a885901e2c3b9567439a7363
MD5 e53e23b41af177b46021c75247ebf665
BLAKE2b-256 52db4e918c7427db5db5a3d731d7ca3506a75aefebceeacc380bccf4548ea9e5

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 d4dda78189fd4892a072751ac5279ab2e0afc3e35766a8d89421311ffadfe7a2
MD5 0928676fa1d3871cd334a068209be98f
BLAKE2b-256 6d80f95e73f6a58bcf9187103ae12d2d7100ddc881e278a3b730fe32d36db675

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 34fd8c6d16b565d566f078f5f9fe3b6f68068e8e973588ce0d19cf144ec8a647
MD5 9a1034d08ffcfec5226096f6b849cf36
BLAKE2b-256 c79824ad2b28d72666d1972414397753f2b6ca2fb29d929d4a22115a8377205d

See more details on using hashes here.

File details

Details for the file torann-0.3.0-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for torann-0.3.0-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 8bbbe388cb12312b6a3b14a008f105021a97ad4707dc25df2ccdf47e4cffc30a
MD5 53bb7fa1738feeb1d914d22d45efc8b8
BLAKE2b-256 580e7ee76f07c60fe10962873308b54384b7a0b93bdf633705053de25c646c32

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.1

6 files

0.5.0

6 files

0.4.0

16 files

This release

0.3.0 This release

16 files

0.2.2

26 files

0.2.1

26 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page