# ordvec
[](https://github.com/Project-Navi/ordvec/actions/workflows/ci.yml)
[](#license)
[](#minimum-supported-rust-version)
[](https://scorecard.dev/viewer/?uri=github.com/Project-Navi/ordvec)
[](https://www.bestpractices.dev/en/projects/12977/silver)
[](https://codecov.io/gh/Project-Navi/ordvec)
[](https://crates.io/crates/ordvec)
[](https://docs.rs/ordvec)
Training-free ordinal & sign quantization for vector retrieval.
`ordvec` is a small, pure-Rust crate for compressed nearest-neighbour search
that quantizes the **ordinal (rank) and sign structure** of an embedding —
no codebook, no learned rotation, no graph to build.
## What is ordinal retrieval?
Ordinal retrieval is a retrieval family where the index operates on order/sign
quotients of embedding coordinates rather than metric magnitudes, with
training-free compression, quotient-aware candidate generation, and asymmetric
reranking against the original query signal.
Dense retrieval assumes magnitude geometry is the primitive. Ordinal retrieval
asks what survives when embeddings are quotiented down to coordinate order,
signs, and relative salience. Surprisingly, enough retrieval signal survives to
match dense qrel quality at much smaller storage, and those quotients support
quotient-aware candidate-generation rules that are training-free,
append-friendly, and graph-optional.
## Benchmark at a glance
> **On the public Harrier-Q8 BEIR harness, ordvec matches exact dense `nDCG@10`
> within bootstrap noise at 8–16× smaller vector storage — with no training and
> no graph build. The committed evidence is the reproducible scifact +
> trec-covid run below; the harness also supports nfcorpus and fiqa. ordvec wins
> single-query latency against exact `flat` on the committed 171K-doc run and on
> operability (no build, no tuning, append-only); in the committed default-method
> threaded view, HNSW still wins highly-parallel batched serving. Larger-corpus
> and alternate-encoder studies are active research, not public release claims
> until their artifacts land in this repository.**
**Public evidence snapshot.** The load-bearing result in this README is narrower
than the research backlog: Harrier-Q8 embeddings on public BEIR data, scored
against official qrels. The default `make benchmark-beir` reproduces scifact
(quality) and trec-covid (quality + latency/scaling); the harness also supports
nfcorpus and fiqa via `QUALITY_DATASETS=...`. Do not read this page as a public
larger-corpus or alternate-encoder claim; those need checked-in run artifacts
before they become release evidence.
On **trec-covid** (171,332 documents, the public [BEIR](https://github.com/beir-cellar/beir)
benchmark) with **Harrier-Q8** 1024-d embeddings, ordvec's two-stage retrieval has a
far smaller per-query cost than exact brute-force (`flat`, identical math to FAISS
`IndexFlatIP`). Both are O(n) scans, but the public ordvec rows stream compressed
codes instead of the 4096-byte float vectors, so their lines sit well below `flat`
and the gap widens over the committed subsampling sweep:

- **~100× faster than exact `flat`, single query, at 171K docs.** Single-query
latency: exact `flat` 56 ms vs ordvec `Sign→rq2` **0.53 ms** — the gap over `flat`
grows with the corpus (it is ~5× at 1K docs).
- **8–16× smaller for the reported qrel rows.** The b=2 rank code is 256 B/vector
(16× smaller than 4096 B floats), b=4 is 512 B (8×), and the reported two-stage
`sign→rq2` row accounts for both stage-1 sign codes and the RankQuant reranker
(384 B/vector). These rows do **not** retain or rescore against the original
float corpus; the public two-stage path is sign/bitmap candidate generation
followed by RankQuant b=2 rerank. At **nDCG@10 within bootstrap noise of exact**
(on trec-covid the ordinal rows even edge ahead; see [Benchmarks](#benchmarks)).
- **vs HNSW (the honest public scale story).** On the committed trec-covid run,
ordvec wins single-query latency while HNSW wins the highly-parallel threaded
view. That is the public comparison here. At larger corpora, graph or shard
layers are the right comparison target; this README does not claim public
million-scale HNSW crossover or GPU bandwidth numbers until the underlying run
artifacts are committed.
- **Reproducible on your machine, one command:**
```sh
make bench-beir-setup make benchmark-beir ```
The figures and result tables in this README were produced by that command on
public BEIR data: the harness writes the figures and the nDCG/timing summaries,
the README tables transcribe those outputs, and you can regenerate or verify
every number yourself (exact latencies vary with hardware and batch size). The
default run reproduces **scifact + trec-covid**; the harness also supports
`nfcorpus` and `fiqa`. Latency for every method is measured in **one Rust
process** (no Python/FFI in the hot path); see the [Benchmarks](#benchmarks)
section for the single-query, batched-throughput, and threaded views and their
caveats.
## What's different
Compressed-retrieval paths almost all carry a **fit step**: product
quantization fits a k-means codebook, OPQ adds a learned rotation,
scalar / binary quantizers calibrate to the data distribution, graph indexes
(HNSW) build a navigable graph, and Matryoshka needs a model trained with its
loss. ordvec fits **none** of them — it quantizes the *ordinal and sign*
structure of each vector on its own:
- **Training-free, data-oblivious.** No codebook, no learned rotation, no
fit step. Encoding is a per-vector rank (or sign) transform — index the
very first vector with no prior data, and never refit when the corpus
drifts.
- **Robust by construction on messy corpora.** Because the code is built
from per-vector ranks (magnitude discarded) with no global frequency / IDF
term, the two things that corrupt learned codebooks — near-duplicate hubs
and mixed chunk lengths — have nothing to grab: b=4 R@10 moved −0.002 under
15% templated-hub injection and +0.002 across a four-chunk-length mixture.
([details + honest scope](docs/RANK_MODES.md#a-consequence-robust-by-construction-on-messy-corpora))
- **Zero system dependencies.** Pure Rust — no BLAS / LAPACK / `ndarray` /
`faer`. Builds and cross-compiles cleanly, including to `aarch64` and
`wasm32`.
- **Ordinal + sign quantization.** Compresses the *rank order* of
coordinates (1/2/4 bits each) and their signs — a different lever from
the product / scalar / binary quantization most crates use.
- **Predictable footprint.** Exactly `dim * bits / 8` bytes per document —
known before you see any data (256 B at dim = 1024, 2-bit), with
`bits ∈ {1, 2, 4}` the size/recall knob. (`b = 8` is an opt-in
evidence/refinement width — asymmetric scoring at any dim, symmetric only
when `dim % 256 == 0` — not a broad retrieval mode. In v0.5.0 it is
Rust-only, in-memory, not accepted by the Python `RankQuant` constructor, and
not persistable to `.ovrq`; each prepared asymmetric query owns a
`dim * 256` `f32` LUT, about 64 MiB at the maximum dimension.)
- **Two-stage retrieval, built in.** A cheap bitmap / sign-popcount
prefilter feeds a RankQuant rerank — the coarse→fine pipeline ships as
library primitives. The coarse-scan→fine-rerank pattern, and the
`RankQuantFastscan` block-32 4-bit LUT path, follow the FAISS FastScan and
binary-quantization-plus-rescore lineage; ordvec ships them
batteries-included and dependency-free, not as new techniques.
ordvec is a compressed **flat-scan** substrate (optionally two-stage): small
codes scored by fast SIMD — AVX-512/AVX2 runtime-dispatched on x86_64, baseline
NEON on aarch64, and `simd128` on wasm32. It
is the code-and-scan layer, not a navigable-graph index — but the codes are
small and index-agnostic, so they compose *under* an ANN or sharding layer for
large-scale serving rather than competing with one.
## Ordinal index family
- **`Rank`** — full-precision rank vectors (`u16` per coordinate).
- **`RankQuant`** — ranks bucketed into `1 << bits` equal-width
bins, `bits` bits per coordinate (`dim * bits / 8` bytes/doc). Both a
symmetric (Spearman) and asymmetric (float-query LUT) scorer. `bits ∈
{1, 2, 4}` are the cross-language persisted retrieval widths in v0.5.0;
`b = 8` is Rust-only and in-memory for evidence/refinement.
- **`Bitmap`** — a top-bucket bitmap per document (one bit per
coordinate); scoring is `popcount(Q AND D)`, a coarsened rank overlap.
- **`SignBitmap`** — a sign bitmap per document for sign-cosine
candidate generation, feeding a caller-owned second-stage rerank.
Two further paths, for callers who need them:
- **`RankQuantFastscan`** — a stable, documented *but specialized* public
type: an optional b=2 FastScan kernel (block-32 nibble/PQ-LUT, AVX-512 →
scalar dispatch) for absolute-minimum stage-1 scan latency, at 2× the
RankQuant b=2 footprint (`dim/2` bytes/doc) and 8-bit LUT scoring noise. It
persists to `.ovfs` (magic `OVFS`) through direct
`RankQuantFastscan::{write,load}` calls. In v0.5.0, `.ovfs` is not yet part
of the `probe_index_metadata()` / `ordvec-manifest` v1 contract; bind it with
an application-owned digest or attestation if it crosses a trust boundary.
Reach for it only when scan latency at b=2 is the binding constraint; the
headline retrieval surface is still `RankQuant` / `Bitmap` / two-stage.
- **`MultiBucketBitmap`** *(behind `--features experimental`)* — the
multi-bucket bilinear-overlap probe behind the research-side decomposition;
an algebraic scaffold, not the top-bucket theorem surface or a production
path.
## The bitmap prefilter has a checked finite model
The `Bitmap` prefilter scores candidates by `popcount(Q AND D)` over each
document's fixed-size top-bucket set. In the idealized uniform constant-weight
null, two unrelated `n_top`-active bitmaps in `dim` coordinates overlap
**hypergeometrically**, `H(dim, n_top, n_top)`, with expected overlap
`n_top² / dim` (e.g. 16 at `dim = 256`, `n_top = 64`). That makes the null
selectivity of an overlap cutoff closed-form.
The current proof story is stronger than a closed-form null alone. Two pieces
are machine-checked in Lean 4, both `sorry`-free on Lean's standard axiom base
(`propext`, `Classical.choice`, `Quot.sound`):
- the **ordinal invariance** on which the rank transform rests — that a vector's
sorting permutation is unchanged by any strictly monotone reparametrisation
of its coordinates — in
[`takens-formalization`](https://github.com/Project-Navi/takens-formalization)
(theorem `isOrdinalPatternOf_comp_strictMono`); and
- the **finite constant-weight bitmap admission model** — symmetry makes
literal overlap the canonical query-preserving invariant, quotient
sufficiency reduces the decision to that evidence, a finite overlap-tilt
signal model makes an overlap-count threshold Bayes-optimal among
deterministic admission rules, and the uniform constant-weight bitmap null
assigns that same threshold event exactly the hypergeometric upper tail — in
[`ordvec-formalization`](https://github.com/Project-Navi/ordvec-formalization)
(theorem `exists_uniformBitmapOverlapTail_finiteBayesRisk_le_and_hypergeomTail`).
This is an *in-model* result. It proves the rule shape and the idealized finite
null under explicit quotient, symmetry, and monotone-overlap assumptions. It
does not prove that real encoders satisfy those assumptions, that the textbook
hypergeometric is every deployment corpus's null, or that ordinal quotients are
representation-complete. Whether true neighbours clear a cutoff remains an
empirical contract to measure.
Details in [`docs/RANK_MODES.md`](docs/RANK_MODES.md).
## Quickstart
```toml
[dependencies]
ordvec = "0.5"
# Or, to track unreleased `main`, use a git dependency instead:
# ordvec = { git = "https://github.com/Project-Navi/ordvec" }
```
```rust
use ordvec::RankQuant;
let dim = 1024;
let n_docs = 10_000;
let mut index = RankQuant::new(dim, 2); // 2 bits/coord → 256 bytes/doc
// `add` takes a flat, row-major buffer of `dim * n_docs` f32s.
// Replace this with your real embeddings.
let doc_embeddings: Vec<f32> = vec![0.0; dim * n_docs];
index.add(&doc_embeddings);
// Asymmetric scan: full-precision queries vs bucketed docs (recommended).
let query_embeddings: Vec<f32> = vec![0.0; dim * 4]; // 4 queries, row-major
let results = index.search_asymmetric(&query_embeddings, 10);
let top_ids = results.indices_for_query(0); // top-10 doc ids for query 0
let top_scores = results.scores_for_query(0);
```
For the two-stage compressed-scan path (`Bitmap` / `SignBitmap` candidate
generation → `RankQuant` rerank) and the full mode comparison, see
[`docs/RANK_MODES.md`](docs/RANK_MODES.md).
### Caller-owned serial two-stage (DB / runtime integration)
For runtimes that own their own parallelism — an embedded vector DB driving a
bounded thread pool, or a binding releasing the GIL — ordvec exposes a
**no-rayon** serial two-stage path so the *caller* schedules the work, with an
**allocation-free rerank step** (`_into`) for the steady-state hot loop:
```rust
use ordvec::{RankQuant, SignBitmap, SubsetScratch};
// Shape sketch (not standalone): `rq: RankQuant` and `sign: SignBitmap` are
// built and `add`-ed as in the Quickstart above; `queries` is your flat
// `dim * nq` f32 batch, `m` the shortlist size, `k` the top-k.
// Stage 1 — serial CSR candidate generation (never enters rayon):
let cb = sign.top_m_candidates_batched_serial_csr(&queries, m); // CandidateBatch { offsets, candidates }
// Stage 2 — rerank into CALLER-OWNED buffers with a reusable scratch:
let nq = queries.len() / dim;
let out_k = k.min(rq.len());
let mut scratch = SubsetScratch::new(); // reuse across batches
let mut out_scores = vec![f32::NEG_INFINITY; nq * out_k];
let mut out_indices = vec![-1i64; nq * out_k];
rq.search_asymmetric_subset_batched_serial_into(
&queries, &cb.offsets, &cb.candidates, k,
&mut scratch, &mut out_scores, &mut out_indices,
);
```
Contract: candidates are **CSR** (`offsets.len() == nq + 1`; row `qi` is
`candidates[offsets[qi]..offsets[qi+1]]`; rows need **not** be sorted). Output is
**rectangular** `nq * out_k` and **sentinel-padded** (`-1` / `NEG_INFINITY`) for
underfull rows — size both buffers to `nq * k.min(index.len())`. Scores, row ids,
and the deterministic tie policy (`score desc, global row-id asc`) match the
single-query `search_asymmetric_subset`. **Only the `_into` rerank step is
allocation-free** — SIMD or scalar — on repeated calls of the *same* batch
shape, reusing the warmed `SubsetScratch` and your output buffers (no per-row
alloc, no scalar-LUT alloc, no whole-buffer preclear). Stage 1
(`top_m_candidates_batched_serial_csr`) still allocates a fresh
`CandidateBatch` each call. Neither primitive enters rayon —
partition the query batch and call `_into` once per worker range from your own
pool. A focused decomposition benchmark lives in
[`examples/two_stage_bench.rs`](examples/two_stage_bench.rs).
### Python
The same `Rank` / `RankQuant` / `Bitmap` / `SignBitmap` API is available from
Python — the bindings ship to PyPI as `ordvec`:
```bash
pip install ordvec
```
Wheels target CPython 3.10+ (abi3); to build from source instead, see
[`ordvec-python/`](https://github.com/Project-Navi/ordvec/tree/main/ordvec-python).
The runtime dependency floor is `numpy>=2.2`.
### Threading / concurrency
`ordvec` supports concurrent read-only/search use. Mutation is exclusive.
The consolidated cross-language ownership and lifetime contract is in
[`docs/bindings-safety.md`](docs/bindings-safety.md).
Python search, candidate-generation, scoring, and `add` methods release the GIL
after copying NumPy inputs into Rust-owned buffers, so ordinary Python in-place
array mutation in another thread cannot race the detached Rust scan. Large calls
may temporarily require an additional input-sized buffer.
The C ABI allows concurrent search and info calls on one loaded handle.
`ordvec_index_free` must not race with any other call on the same handle.
The Go wrapper serializes `Close` against `Search` and `Info`; after `Close`,
`Search` and `Info` return `ErrClosed`. Callers must not mutate query or
candidate slices passed to `Search` until the call returns.
## Documentation
- **Design deep-dive & reproducible benchmark tables:**
[`docs/RANK_MODES.md`](docs/RANK_MODES.md)
- **Design alternatives evaluated and cut:**
[`docs/ALTERNATIVES_CONSIDERED.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/ALTERNATIVES_CONSIDERED.md)
- **Index-file trust model:**
[`docs/INDEX_PROVENANCE.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/INDEX_PROVENANCE.md),
[`docs/determinism.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/determinism.md),
[`THREAT_MODEL.md`](https://github.com/Project-Navi/ordvec/blob/main/THREAT_MODEL.md)
- **Manifest verifier, C ABI, and Go wrapper:**
`ordvec-manifest` is versioned and published in lockstep with the core crate
through its own package gate; use the GitHub checkout for `ordvec-ffi/`,
`ordvec-go/`, and
[`docs/c-api.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/c-api.md).
- **Bindings safety and ownership contract:**
[`docs/bindings-safety.md`](docs/bindings-safety.md)
- **Artifact and platform matrix:**
[`docs/artifact-platform-matrix.md`](docs/artifact-platform-matrix.md)
- **Pre-1.0 compatibility policy:**
[`docs/compatibility-policy.md`](docs/compatibility-policy.md) defines the
stable, experimental, repo-local sidecar, persisted-format, examples/docs,
MSRV, and release-note review surfaces.
- **Formal proof spine:** [`ordvec-formalization`](https://github.com/Project-Navi/ordvec-formalization),
including its [`proof-spine`](https://github.com/Project-Navi/ordvec-formalization/blob/main/docs/proof-spine.md),
[`theorem-map`](https://github.com/Project-Navi/ordvec-formalization/blob/main/docs/theorem-map.md),
and [`reviewer brief`](https://github.com/Project-Navi/ordvec-formalization/blob/main/docs/reviewer-brief.md).
- **API docs:** <https://docs.rs/ordvec>, <https://docs.rs/ordvec-manifest>
- **Paper (OrdVec / RankQuant):** _link TBD — see
[Research collaboration](#research-collaboration)._
## Benchmarks
### BEIR retrieval (public datasets, reproducible)
A fully reproducible harness over standard [BEIR](https://github.com/beir-cellar/beir)
datasets lives in [`benchmarks/beir/`](https://github.com/Project-Navi/ordvec/tree/main/benchmarks/beir). It embeds the corpus
with **Harrier-Q8** (GGUF `Q8_0` via `llama-cpp-python`, CUDA), then measures
ordvec's methods against two references **in a single Rust process** so the
latency comparison is genuinely apples-to-apples — same machine, batch, and
thread count, no Python/FFI in the hot path:
- **`flat`** — exact inner-product brute force (identical retrieval to FAISS
`IndexFlatIP`), a pure-Rust SIMD GEMM. *Baseline, not ground truth.*
- **`hnsw`** — pure-Rust HNSW (`hnsw_rs`, M=32, ef_construction=200,
ef_search=128) — the portable stand-in for the C++ hnswlib.
The committed figures use the default method set in `Makefile`. They do not
yet include the newer `sign-rq2-threaded` probe row. Read HNSW byte labels as
stored float-vector bytes plus an implementation-owned graph side structure;
the tables and plot generator now spell that caveat out rather than treating
the graph as zero.
Reproduce end-to-end (downloads the data, embeds, runs every method, renders the
figures, and emits the summary tables transcribed below):
```sh
make bench-beir-setup # Python deps + CUDA llama-cpp-python
make benchmark-beir # quality (nDCG) + scaling sweep + graphics
```
#### Quality — nDCG@10 vs the official BEIR qrels
nDCG@10 is computed against the human-annotated qrels (not against `flat`).
`Δ vs flat` is the paired-bootstrap mean delta; `*` marks a 95% CI that straddles
0 (i.e. within noise of exact). `flat` and the ordvec rows are **deterministic**
(byte-identical run to run); the `hnsw` row is **approximate** — its graph is
built in parallel, so its nDCG and latency vary slightly between runs (≈±0.003
nDCG here, within the same noise band). The numbers below are one representative
run; regenerate your own with `make benchmark-beir`.
| scifact (5,183) | `flat` (exact) | 4096 | 0.7551 | (baseline) |
| | `hnsw` M=32 | 4096 + graph | 0.7554 | +0.0003 * |
| | **ordvec rq4** | **512** | **0.7549** | −0.0003 * |
| | ordvec rq2 | 256 | 0.7471 | −0.0080 * |
| | ordvec sign→rq2 | 384 | 0.7471 | −0.0080 * |
| trec-covid (171,332) | `flat` (exact) | 4096 | 0.7574 | (baseline) |
| | `hnsw` M=32 | 4096 + graph | 0.7555 | −0.0019 * |
| | ordvec rq2 | 256 | 0.7632 | +0.0057 * |
| | **ordvec rq4** | **512** | **0.7636** | +0.0062 * |
| | ordvec sign→rq2 | 384 | 0.7638 | +0.0064 * |
Every ordvec row is within bootstrap noise of exact dense at **8–16× smaller**
storage; on trec-covid the ordinal codes even edge slightly ahead.
#### Latency — three honest views
The reported ordvec rows do not stream the float corpus, so their per-query cost
has a much smaller constant than `flat`; they are still O(n) compressed-code
scans. `flat`'s cost is dominated by streaming the 4096-byte vectors, which is
also O(n) and **memory-bandwidth-bound**. That single fact explains all three
views (trec-covid, 171,332 docs, 1024-d):
**1. Single query (batch = 1, 1 thread)** — latency-sensitive serving, where
`flat` cannot amortize its memory traffic:

`flat` 56 ms → ordvec `sign→rq2` **0.53 ms (≈106×)**, `bitmap→rq2` 0.62 ms (≈91×),
`hnsw` 1.5 ms (37×). The scaling curve [above](#benchmark-at-a-glance) is this
view swept over the committed subsamples — the speedup over `flat` grows across
that public sweep.
**2. Batched throughput (batch = 32, 1 thread)** — when many queries arrive at
once, `flat`'s GEMM amortizes the corpus stream across the batch (56→4 ms),
narrowing the gap: ordvec `sign→rq2`/`bitmap→rq2` stay ≈8–9.5× ahead.
**3. Many cores (batch = 32, 32 threads)** — everything parallelizes and the
field compresses; `hnsw` threads best:

`hnsw` 4.8× vs `flat`, ordvec `bitmap→rq2` 3.7×, `rq2` 2.5×, `sign→rq2` 2.1×.
This committed chart uses the default `sign-rq2` row, not the newer
within-query-threaded `sign-rq2-threaded` probe row; regenerate public figures
before using that probe for release claims. In this default-method view,
**HNSW wins this regime** — by a hair on threaded throughput. The honest
ordvec-vs-HNSW tradeoff, all from this same run (trec-covid, 171,332 docs):
| threaded latency (32 threads, batch 32) | **0.23 ms** ✅ | 0.52 ms |
| single-query latency (batch 1) | 1.52 ms | **0.53 ms** ✅ (~3×) |
| index size / vector | 4096 B + graph | **256–384 B** ✅ (8–16× less) |
| build time, 171K docs | **51.4 s** | **0.26 s** ✅ (training-free) |
| nDCG@10 (trec-covid) | 0.7555 | **0.7638** ✅ |
So even where HNSW edges ahead on threaded latency, ordvec gets there with **no
graph to build** (instant, training-free, and rebuilt for free when the corpus
drifts) and **8–16× less memory** — and it still wins single-query latency and
ties or edges quality. And the two aren't mutually exclusive: ordvec's codes are
index-agnostic, so they compose *under* an HNSW/sharding layer (see
[Scope](#scope)) rather than replacing it.
**Read it honestly:** ordvec's latency win over exact `flat` is a single-query /
low-batch phenomenon in the committed BEIR run; under large-batch throughput a
batched exact GEMM is a strong baseline, and HNSW wins the committed threaded
default-method view. ordvec's scan is O(n) — the same class as `flat`, but over an
8–16× smaller reported working set, so its constant is far lower. Its per-query
latency is **not** flat in `n`; an earlier "near-flat" claim was an artifact of
small (≤57K) corpora and is retracted. The durable wins are **compression at
iso-quality** and **operability** (training-free, no graph build, no tuning,
append-only). `flat` is a comparison reference, not ground truth; nDCG@10 is the
qrel-based metric. Numbers vary with encoder, dataset, hardware, and batch —
regenerate them with `make benchmark-beir`.
### Synthetic stress test
The head-to-head benchmark generates a seeded synthetic corpus in-process, so
the **quality numbers (R@10, candidate-recall, bytes/vec) are deterministic**
and regenerable from a clean checkout with no external corpus file:
```sh
cargo run --release --features bench-utils --example bench_rank
```
A few operating points from the committed run
([`benchmarks/rank_modes_results.txt`](benchmarks/rank_modes_results.txt)):
| `Rank` asym (full-precision reference) | 512 | 3.71 | 8 | 0.845 |
| `RankQuant` b=4 asym | 128 | 0.31 | 96 | 0.806 |
| `RankQuant` b=2 asym | 64 | 0.24 | 126 | 0.572 |
| `RankQuant` b=2 FastScan | 128 | 0.09 | 333 | 0.570 |
| Two-stage b=2 (M=500, CR=1.000) | 96 | 0.11 | 275 | 0.572 |
*One representative run on a **synthetic** corpus (dim=256, n=30k, seed=1),
AMD Ryzen 9 9950X (AVX-512), 32 threads, single-thread scan. **R@10 is
deterministic** run-to-run; **throughput/latency vary** with hardware and run.
R@10 is measured against FP32 brute-force cosine on this generated corpus. Treat
it as a small, self-contained kernel and stress-test fixture; the real-embedding
retrieval task above is the better guide to retrieval-relevant behaviour.*
## Scope
ordvec is a **library and substrate**, not a turnkey service: small
ordinal/sign codes, fast SIMD scoring, and a built-in two-stage prefilter —
the code-and-scan layer of a retrieval system. It is not a navigable-graph
index (HNSW) on its own — yet — and not a serving tier at all: ordvec is the
substrate other systems build on, so its small, index-agnostic codes slot
**under** an ANN or sharding layer for large-scale serving rather than
replacing it. Encoding is training-free and data-oblivious by design —
no codebook fit — so you index the first vector with no prior data and never
refit as the corpus grows.
Quality evidence now has two layers: the real-embedding retrieval table above
for the paper claim, and the reproducible synthetic stress test for a
clean-checkout kernel sanity check.
## Security: index-file trust
The probe/manifest-covered on-disk formats (`.ovr` / `.ovrq` / `.ovbm` /
`.ovsb`; legacy `.tvr` / `.tvrq` / `.tvbm` / `.tvsb` files still load) carry
**no built-in checksum, MAC, or signature — by design.** The loaders validate
*structure* (magic, version, bounds, exact-length payload) but not *origin*: a
structurally valid file can still be untrusted. `RankQuantFastscan` also writes
and loads `.ovfs` directly, but in v0.5.0 that format is not yet covered by
`probe_index_metadata()` or `ordvec-manifest` v1. If an index file crosses a
trust boundary (network transfer, shared storage), verify it before loading.
`ordvec-manifest` binds supported index files to a JSON manifest by SHA-256,
header metadata, row identity, named auxiliary sidecars, and attestation shape
checks. It does not sign artifacts, manage keys, or decide deployment trust
policy. No in-format crypto is shipped because it would add key management the
library can't own. See
[`docs/PERSISTED_FORMAT.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/PERSISTED_FORMAT.md),
[`docs/INDEX_PROVENANCE.md`](https://github.com/Project-Navi/ordvec/blob/main/docs/INDEX_PROVENANCE.md),
and [`THREAT_MODEL.md`](https://github.com/Project-Navi/ordvec/blob/main/THREAT_MODEL.md)
in the full repository.
## Provenance
ordvec's active upstream, implementation history, issues, releases, and
governance live in `Project-Navi/ordvec`.
Courtesy note: ordvec was developed using the early
[turbovec](https://github.com/RyanCodrai/turbovec) project context as a
rapid-development scaffold, with thanks to that lineage. It is not a source
fork of turbovec.
The ordvec project is jointly maintained by
[@Project-Navi](https://github.com/Project-Navi) and
[@Baur-Software](https://github.com/Baur-Software).
## Acknowledgements
Thanks to Todd Baur ([@toadkicker](https://github.com/toadkicker)) for the
sign-cosine intuition and engineering polish.
Thanks to Mike Singleton ([@singleton2787](https://github.com/singleton2787))
for mathematical assistance and mentorship.
## Research collaboration
ordvec is the reference implementation for an in-progress paper on **ordinal
retrieval** — using the rank and sign structure of embeddings, rather than
their floating-point magnitudes, as the retrieval signal. The repository is
open specifically to grow a group of collaborators, **including potential
named co-authorship where contributions meet the paper's authorship bar** —
a different invitation than "send a PR."
Collaboration we're actively seeking:
- **Real-corpus evaluation** — running the modes against public corpora
(GloVe, MTEB / BEIR, OpenAI embedding dumps) beyond the synthetic benchmark.
- **Theory** — extending and independently auditing the `sorry`-free Lean
formalization, especially the finite bitmap proof spine, rank-cosine
invariants, and empirical diagnostics for when real encoders meet or violate
the model assumptions.
- **Independent reproduction** — re-running the benchmark on other hardware
and reporting the numbers.
If that's your area, see
[GOVERNANCE.md](https://github.com/Project-Navi/ordvec/blob/main/GOVERNANCE.md)
and open an issue or a discussion.
## Contributing
Contributions to the code, the docs, and the paper are all welcome — see
[CONTRIBUTING.md](https://github.com/Project-Navi/ordvec/blob/main/CONTRIBUTING.md).
## Minimum supported Rust version
ordvec's MSRV is **Rust 1.89** — the release that stabilized the specific
AVX-512 intrinsics the SIMD kernels compile against (it also clears the 1.87
floor from `is_multiple_of`). Because the kernels are built against those
intrinsics, this is a hard compile floor, not just a convenience pin: a
toolchain below 1.89 won't build the crate. Raising the MSRV is treated as a
minor-version change under the
[compatibility policy](docs/compatibility-policy.md). The current feature
stability matrix and downstream embedding notes live in
[`docs/msrv-and-features.md`](docs/msrv-and-features.md).
## License
Licensed under either of
- MIT License ([LICENSE-MIT](LICENSE-MIT))
- Apache License, Version 2.0 ([LICENSE-APACHE-2.0](LICENSE-APACHE-2.0))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.