pub struct VecqIndex { /* private fields */ }Expand description
A quantized vector database in memory.
Each vector is stored as padded_dim / 2 bytes of 4-bit Lloyd-Max codes
(computed after RHDH rotation) plus one f32 correction factor. The score
against an f32 query is an unbiased estimate of the cosine similarity
after undoing the per-vector quantization scale.
Vectors can be stored anonymously via VecqIndex::add or under a
caller-chosen u64 key via VecqIndex::add_keyed. Keyed vectors can be
removed in place (tombstoned); tombstoned slots keep their storage but are
skipped by searches and dropped by VecqIndex::compact and by
VecqIndex::to_bytes. Slot indices stay stable until a
compaction, so integrators can treat a slot as a transient handle while
keys are the durable identity.
Implementations§
Source§impl VecqIndex
impl VecqIndex
Sourcepub fn to_bytes(&self) -> Vec<u8> ⓘ
pub fn to_bytes(&self) -> Vec<u8> ⓘ
Serialize the index to bytes (format version 1.1, f16 scales).
Tombstoned slots are skipped: the output always holds the live vectors
in slot order, so a round-trip through bytes has the same effect as
VecqIndex::compact on disk without disturbing in-memory slot
indices. Keys are not part of the file format; persist a key→slot
table alongside (e.g. in SQLite) if you need keyed access across a
reload.
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self, Error>
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error>
Parse an index from bytes produced by [to_bytes] (or a v1 file).
Source§impl VecqIndex
impl VecqIndex
Sourcepub fn new(dim: usize, seed: u64) -> Self
pub fn new(dim: usize, seed: u64) -> Self
Create an empty index for dim-dimensional unit vectors.
seed must be persisted with the index for cross-platform determinism.
pub fn is_empty(&self) -> bool
Sourcepub fn slots(&self) -> usize
pub fn slots(&self) -> usize
Total slots in use, including tombstoned ones
(slots() == len() + tombstones()).
Sourcepub fn tombstones(&self) -> usize
pub fn tombstones(&self) -> usize
Number of tombstoned slots awaiting VecqIndex::compact.
pub fn dim(&self) -> usize
pub fn seed(&self) -> u64
Sourcepub fn add(&mut self, v: &[f32]) -> usize
pub fn add(&mut self, v: &[f32]) -> usize
Quantize and add one vector (any norm; normalized internally).
Returns the slot index holding the vector (stable until compaction).
Sourcepub fn add_keyed(&mut self, key: u64, v: &[f32]) -> usize
pub fn add_keyed(&mut self, key: u64, v: &[f32]) -> usize
Quantize and add one vector under a caller-chosen u64 key.
If key already exists, the vector is replaced in place (the slot
index is preserved, matching usearch’s insert semantics). Otherwise a
new slot is appended. Returns the slot index holding the vector.
Sourcepub fn remove_keyed(&mut self, key: u64) -> bool
pub fn remove_keyed(&mut self, key: u64) -> bool
Remove a keyed vector. The slot becomes a tombstone: its storage is
kept (slot indices stay stable) but searches skip it until
VecqIndex::compact. Returns false if the key is unknown.
Sourcepub fn key_of(&self, slot: usize) -> Option<u64>
pub fn key_of(&self, slot: usize) -> Option<u64>
Look up the key stored at slot (None for anonymous slots,
tombstones, or out-of-range indices).
Sourcepub fn contains_key(&self, key: u64) -> bool
pub fn contains_key(&self, key: u64) -> bool
Whether key currently identifies a live vector.
Sourcepub fn compact(&mut self)
pub fn compact(&mut self)
Rebuild the index in place, dropping tombstoned slots.
All remaining vectors keep their keys; slot indices shift to become dense (0..len). Search results are unchanged.
Sourcepub fn prepare_query(&self, q: &[f32]) -> PreparedQuery
pub fn prepare_query(&self, q: &[f32]) -> PreparedQuery
Prepare an f32 query in rotated space (call once per query).
Sourcepub fn score(&self, pq: &PreparedQuery, idx: usize) -> f32
pub fn score(&self, pq: &PreparedQuery, idx: usize) -> f32
Asymmetric score of vector idx against a prepared query.
Returns estimated cosine similarity in [-1, 1].
Dispatches to the explicit NEON path on aarch64, the explicit AVX2
path on x86_64 when the host supports it (runtime detection), and the
fixed 8-bucket scalar path otherwise. All use the identical
association order (per code byte: mul, mul, add, then add into bucket
j; final pairwise tree), so they produce the same f32 bits — guarded
by neon_matches_scalar_bitwise / avx2_matches_scalar_bitwise in
tests.
Sourcepub fn search(&self, q: &[f32], k: usize) -> Vec<(usize, f32)>
pub fn search(&self, q: &[f32], k: usize) -> Vec<(usize, f32)>
Brute-force top-k search. Returns (slot index, score) sorted by score desc. Tombstoned slots are skipped.
Uses a bounded min-heap of size k (no O(n log n) sort, no O(n) allocation per query): push while the heap is not full, then only push-and-pop when the candidate beats the current k-th score.
Sourcepub fn search_keyed(&self, q: &[f32], k: usize) -> Vec<(u64, f32)>
pub fn search_keyed(&self, q: &[f32], k: usize) -> Vec<(u64, f32)>
Keyed variant of VecqIndex::search: returns (key, score) sorted by
score desc, restricted to live keyed vectors.