- Every metric — cosine, dot product, Euclidean (L2), Manhattan (L1), Hamming
- SIMD + scalar — AVX2 on x86_64, NEON on aarch64, with a readable scalar fallback
- Runtime dispatch — detect CPU features once; route each call to the fastest available kernel
- Two entry points — a type-level
Distancetrait when the metric is known at compile time, andcompute/compute_batchoverDistanceMetricwhen it is chosen at runtime - Normalized fast path —
cosine_normalized(and anormalizehelper) skip the norm work for pre-normalized embeddings - Allocation-free — every distance call borrows; batch evaluation writes into a caller-owned buffer
- Never panics on bad input — empty, mismatched, and non-finite inputs return a typed
IqdbError - Standalone — usable by anyone doing vector similarity in Rust, iQDB or not
Installation
[]
= "0.5"
Quick start
Pick the metric at compile time through the Distance trait:
use ;
let a = ;
let b = ;
// Cosine distance of perpendicular unit vectors is 1.0.
let cos = compute.expect;
assert!;
// Euclidean (L2): a 3-4-5 right triangle.
let l2 = compute.expect;
assert!;
Or pick it at runtime with the DistanceMetric tag from iqdb-types:
use compute;
use DistanceMetric;
let a = ;
let b = ;
// Dot-product similarity: 1*4 + 2*(-5) + 3*6 = 12.
let s = compute.expect;
assert!;
Score one query against many candidates into a caller-owned buffer (no allocation):
use compute_batch;
use DistanceMetric;
let query = ;
let candidates: = ;
let mut out = ;
compute_batch
.expect;
assert_eq!;
When embeddings are already unit length, take the fast cosine path — it skips the norm, square root, and division:
use ;
// Normalize once at ingest, then compare with the fast path.
let a = normalize.expect;
let b = normalize.expect;
let d = cosine_normalized.expect; // 1 - dot(a, b)
assert!;
Inspect the kernel the host will use:
let features = detect_features;
// On an AVX2 x86_64 host `features.avx2` is true; on aarch64 `features.neon` is.
let _ = ;
Errors
Every fallible call returns iqdb_types::Result. Empty inputs surface as
IqdbError::InvalidVector; length mismatches as
IqdbError::DimensionMismatch { expected, found }; a batch whose output buffer
does not match the candidate count returns IqdbError::InvalidConfig. The
library never panics on hostile input.
Status
This is the v0.5.0 API-freeze release: all five metrics ship with a scalar reference path plus runtime-dispatched AVX2 and NEON kernels (each property-tested, differentially verified, and fuzzed against the scalar twin), the cosine_normalized fast path for pre-normalized embeddings, and allocation-free batch evaluation. The public API is frozen for 1.x — additive changes only until 2.0 (the frozen surface is recorded in the ROADMAP). The remaining 0.x work is the RC soak against real index consumers, per the ROADMAP; the full surface is documented in docs/API.md.
Where It Fits
iqdb-distance sits just above the types crate. It powers:
iqdb-types— theDistanceMetricenum and vector types it operates oniqdb-quantize— quantized distance reuses this SIMD infrastructureiqdb-flat/iqdb-hnsw/iqdb-ivf— every index computes distances here
Its only first-party dependency is iqdb-types, so it is unblocked today.
Standards
Built to the iQDB Rust standard. See REPS.md (Rust Efficiency & Performance Standards) and dev/DIRECTIVES.md for the engineering law and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.