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.4.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.4.0-cp314-cp314-win_amd64.whl (271.0 kB view details)

Uploaded CPython 3.14Windows x86-64

torann-0.4.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (393.8 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

torann-0.4.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.8 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ ARM64

torann-0.4.0-cp314-cp314-macosx_11_0_arm64.whl (351.0 kB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

torann-0.4.0-cp314-cp314-macosx_10_12_x86_64.whl (363.3 kB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

torann-0.4.0-cp313-cp313-win_amd64.whl (271.4 kB view details)

Uploaded CPython 3.13Windows x86-64

torann-0.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (393.5 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

torann-0.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.6 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

torann-0.4.0-cp313-cp313-macosx_11_0_arm64.whl (350.9 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

torann-0.4.0-cp313-cp313-macosx_10_12_x86_64.whl (363.1 kB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

torann-0.4.0-cp312-cp312-win_amd64.whl (271.2 kB view details)

Uploaded CPython 3.12Windows x86-64

torann-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (393.3 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

torann-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.3 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

torann-0.4.0-cp312-cp312-macosx_11_0_arm64.whl (350.8 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

torann-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl (362.9 kB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

File details

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

File metadata

  • Download URL: torann-0.4.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.4.0.tar.gz
Algorithm Hash digest
SHA256 2386066a4edba1038e07104b80bea942d2b5140f38cce8326aaa38d5868bb34e
MD5 d6d7e14aa8d9bbcae22b90d6e1d4cc5d
BLAKE2b-256 554c4f6955c1cd0af7b8f2403a9f415f96d36098cf07de0d76c2c116086d188f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: torann-0.4.0-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 271.0 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.4.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 fc99a3faeb393e917a683fe9f75b826e96cd82f10a1c69cb5ab522bd4f9b4f03
MD5 8248505d6005584d4617ce5136aebd5c
BLAKE2b-256 2f4c997b6904fd8a12e0684054d057940a2f528d458cc701c080f90ac22bf803

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 37f2bef6474870cbdc109ec533d6d378c358ca2debca446236ca5042bc37e735
MD5 418bf0704dc51659833ea4ac11fa6549
BLAKE2b-256 09aaed833dd245369dd9bd4f4b64512881c42f87f77ed9e200568275727b0f56

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 98d138c0442f3c6f8fcf863fd73d0b3196c5b923ff92d734e81707532f8aa32f
MD5 e7a7e8fdd04897d2ea7a3be06811cf4f
BLAKE2b-256 ebee9f54ea193aa317e25e341d22ea7bbc19725a941365a471521606151436bb

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0bb050b1bbb6e73674aaec16c79802a67aded33c967d2e4c3bf9bd6ebefcee22
MD5 08a251f66959e1c50b46948675267b26
BLAKE2b-256 50510939fb95dee7abfc55586f31039b86b12f3b00343961d366254aeb3f8719

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 4a0a4476e4fc3bd5fbd99f92038286886e9c1b1d34383beb342b59a6de047c79
MD5 b1aacb946895e3f9d0249a34cdd0630a
BLAKE2b-256 3b090f532209d4c2eb91e2639419f94f09371d7c52797758c59d15a557504ec5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: torann-0.4.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 271.4 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.4.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 25235328841bfd849f9cf328ac60eb8fac11da259c7c43f2e85bd33d9a588505
MD5 fb26ce76677dd0cd76ca85b21e112024
BLAKE2b-256 679b3c906f8937e7ba2b52f8e14aaf03a7e5be20e87dcf2d82458685cc2f4518

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dbe6e4f989d21c092282a8ed3abb3dc331febafac2b09b1da34ce66055c4a717
MD5 fa99c133dcaa86a37db39485ea1d1a53
BLAKE2b-256 87853799b53ecdf39ea5d485803644a5c1a67dffe586dd2cd1cb41564c0717da

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a080386f5efb7bdb8c11d6c486c49b91611c37938de775ba90d54eb28ac6129d
MD5 7d2ece6462946cc9982f58fc0ae2bb3c
BLAKE2b-256 b17220c5c8f83d4672f4bf90b999e978fb0f42f7f330a86c1201f25f00c85112

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c0ca75f9224826ad289b63864fa46f290e89d63062d436c87708680dceab373a
MD5 c05139e3718fd71b62d4d743c5d9c588
BLAKE2b-256 8bcb4c9375bf6807f8fe408776fe32127aab00500a55183e6905e6bb26b81aa4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 ae2278a5b7746965a51de6acb3948f3a1fe8ca41af48b3f37bdd1ef7eefa9c39
MD5 2dcfcf10069eec1fd3143472f7e34eea
BLAKE2b-256 21c5134df423487ec19e281f30d85e32e47c4a160710e5443cb516dfa5d84ef8

See more details on using hashes here.

File details

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

File metadata

  • Download URL: torann-0.4.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 271.2 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.4.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 97e7c63dc883738667c36f198591517182138c548b22546cdc5d66716e302b4d
MD5 483dfa045e822a15b5e7d0514832867c
BLAKE2b-256 8e99ee3a78e2b7677e813c456100f13c3bf53418c2e6281f5019989488e7b306

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 90b7344173d959022983ab5965ce4dc00183eca2064c3602d93b24957e5e4491
MD5 7d10c403a02fdfa2906b771f3b287b41
BLAKE2b-256 2078a6901dc2b076655055e25328cd71df2257a4a08f28a8eabd6a8df5b54e54

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b4d474af6eb720f1d8f8af1425b2e46ec7da4a438785d5321833f1b7ccfd89f6
MD5 8c521aef2aafa51956fd0cbfab3843da
BLAKE2b-256 6b952b53f07c3804f6714b29827c209cbf6d1ea3440c044b0c14875291de079a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7b3f3e80e39dff9a57122cf147330834d1d641174a4395c3d49841c7b4205b0e
MD5 79c85b55157ea6d2b940bf2d14db58ed
BLAKE2b-256 0f6b9ae255a28db1c5fbd0e7b77ecb0be61868565159ea7d346652668d6219b6

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for torann-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 303beb94d59ded4bf4319e4e076184ad9e59b4bfbd68a67f6843832edc27af5e
MD5 a0e88067e42fa37859a9a5e33e83f517
BLAKE2b-256 a06838d35ef8fa26f78b06195f81744965ba6b72d77cd43d89af0fda1faf82f0

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.1

6 files

0.5.0

6 files

This release

0.4.0 This release

16 files

0.3.0

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