iqdb-distance 1.0.0

SIMD-accelerated distance and similarity functions for vector search - part of the iQDB family.
Documentation
  • 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 Distance trait when the metric is known at compile time, and compute/compute_batch over DistanceMetric when it is chosen at runtime
  • Normalized fast pathcosine_normalized (and a normalize helper) 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

[dependencies]
iqdb-distance = "1.0"

Quick start

Pick the metric at compile time through the Distance trait:

use iqdb_distance::{Cosine, Distance, Euclidean};

let a = [1.0_f32, 0.0, 0.0];
let b = [0.0_f32, 1.0, 0.0];

// Cosine distance of perpendicular unit vectors is 1.0.
let cos = Cosine::compute(&a, &b).expect("non-empty, same length");
assert!((cos - 1.0).abs() < 1e-6);

// Euclidean (L2): a 3-4-5 right triangle.
let l2 = Euclidean::compute(&[0.0, 0.0, 0.0], &[3.0, 4.0, 0.0]).expect("valid pair");
assert!((l2 - 5.0).abs() < 1e-6);

Or pick it at runtime with the DistanceMetric tag from iqdb-types:

use iqdb_distance::compute;
use iqdb_types::DistanceMetric;

let a = [1.0_f32, 2.0, 3.0];
let b = [4.0_f32, -5.0, 6.0];

// Dot-product similarity: 1*4 + 2*(-5) + 3*6 = 12.
let s = compute(DistanceMetric::DotProduct, &a, &b).expect("valid pair");
assert!((s - 12.0).abs() < 1e-6);

Score one query against many candidates into a caller-owned buffer (no allocation):

use iqdb_distance::compute_batch;
use iqdb_types::DistanceMetric;

let query = [0.0_f32, 0.0];
let candidates: [&[f32]; 3] = [&[1.0, 0.0], &[0.0, 2.0], &[3.0, 4.0]];
let mut out = [0.0_f32; 3];

compute_batch(DistanceMetric::Euclidean, &query, &candidates, &mut out)
    .expect("output length matches candidate count");
assert_eq!(out, [1.0, 2.0, 5.0]);

When embeddings are already unit length, take the fast cosine path — it skips the norm, square root, and division:

use iqdb_distance::{cosine_normalized, normalize};

// Normalize once at ingest, then compare with the fast path.
let a = normalize(&[1.0_f32, 2.0, 3.0]).expect("non-zero magnitude");
let b = normalize(&[-2.0_f32, 0.5, 4.0]).expect("non-zero magnitude");

let d = cosine_normalized(&a, &b).expect("valid pair"); // 1 - dot(a, b)
assert!((0.0..=2.0).contains(&d));

Inspect the kernel the host will use:

let features = iqdb_distance::detect_features();
// On an AVX2 x86_64 host `features.avx2` is true; on aarch64 `features.neon` is.
let _ = (features.avx2, features.neon);

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

v1.0.0stable. 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 committed under SemVer for the 1.x series (no breaking changes until 2.0; the frozen surface is recorded in the ROADMAP), benchmarked (6.8–10.3× SIMD speedups on AVX2 at 768-dim), and verified on Windows + Linux across stable and the 1.87 MSRV. The full surface is documented in docs/API.md.

Where It Fits

iqdb-distance sits just above the types crate. It powers:

  • iqdb-types — the DistanceMetric enum and vector types it operates on
  • iqdb-quantize — quantized distance reuses this SIMD infrastructure
  • iqdb-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.