weavatrix-search-vector 0.3.1

Persistent, mutable, bounded vector candidate search for Rust and Weavatrix
Documentation
# Weavatrix Search Vector

[![Crates.io](https://img.shields.io/crates/v/weavatrix-search-vector.svg)](https://crates.io/crates/weavatrix-search-vector)
[![Documentation](https://docs.rs/weavatrix-search-vector/badge.svg)](https://docs.rs/weavatrix-search-vector)
[![CI](https://github.com/sergii-ziborov/weavatrix-search-vector/actions/workflows/ci.yml/badge.svg)](https://github.com/sergii-ziborov/weavatrix-search-vector/actions/workflows/ci.yml)
[![License](https://img.shields.io/crates/l/weavatrix-search-vector.svg)](https://github.com/sergii-ziborov/weavatrix-search-vector/blob/main/LICENSE)

`weavatrix-search-vector` is a first-party vector-candidate engine for
Weavatrix and other Rust applications. It provides deterministic hybrid HNSW
plus configurable multi-probe `SimHash`, exact and compact search, cosine/dot/L2
metrics, versioned persistence, read-only memory mapping, sealed mutation
deltas, traversal-time metadata filters, multi-vector keys, embedding adapters,
KNN graph construction, and bounded shard coordination.

The public API is safe Rust, requires Rust 1.88, and has no runtime
dependencies, native libraries, helper processes, or external vector engines.
Two private audited modules isolate runtime-detected `std::arch` kernels and
read-only OS mapping on Windows, Linux, and macOS. All other code remains safe
Rust.

## Installation

```text
cargo add weavatrix-search-vector
```

Or add the crate directly to `Cargo.toml`:

```toml
[dependencies]
weavatrix-search-vector = "0.3"
```

## Boundary

Vector Search owns:

- vector and query validation;
- deterministic HNSW construction;
- uncertainty-driven `SimHash` candidate recovery;
- per-query graph-expansion and routing-recovery policy;
- approximate and exact top-K candidate search;
- cosine, inner-product, and squared-Euclidean distance;
- stable equal-distance ordering by caller-provided `u64` key;
- bounded batch workers;
- versioned snapshots with payload checksums;
- owned and zero-copy read-only snapshot loading;
- staged and sealed insert/upsert/delete/rename deltas with explicit
  deterministic compaction;
- composable metadata predicates applied inside graph traversal, with optional
  exact fallback;
- BF16, F16, F8-E4M3, I8, and binary candidates with optional exact f32
  reranking;
- multiple vectors per caller key with stable per-vector identifiers;
- atomic bundles containing f32 graphs, mutation state, metadata, and an
  optional quantized representation;
- model-neutral embedding provider integration;
- directed KNN candidate graph construction;
- transport-neutral bounded shard fan-out and stable merging;
- recall and retained-allocation evidence.

It does not know about the Weavatrix domain graph, semantic thresholds,
embedding models, provenance, mutual/union policies, text search, or repository
discovery. Semantic consumers remain responsible for exact rescoring and
relationship policy.

## Architecture

The crate is a layered modular engine, not a monolith:

- `domain` owns configuration, typed errors, hits, and metadata values;
- `kernel` owns vector storage, distance math, SIMD dispatch, routing probes,
  and bounded standard-library workers;
- `index` owns exact, HNSW, compact, and multi-vector candidate indexes;
- `persistence` owns validated snapshot codecs, memory mapping, and atomic file
  replacement;
- `services` composes mutable deltas, complete bundles, embedding adapters, KNN
  graphs, metadata-filtered search, and distributed shard coordination;
- `lib.rs` is a thin public facade that re-exports the stable API.

The checked-in strict Weavatrix contract enforces inward-only dependencies,
zero runtime cycles, source/test/benchmark files no longer than 300 lines, and
functions no longer than 100 lines. It contains no exceptions or ratchet
baseline. The layout keeps candidate retrieval reusable: Weavatrix semantic
policy and provenance remain outside this crate.

## Example

```rust
use weavatrix_search_vector::{IndexConfig, SearchPolicy, VectorIndex};

let first = [1.0, 0.0, 0.0];
let second = [0.9, 0.1, 0.0];
let third = [0.0, 1.0, 0.0];
let vectors = [(10, first.as_slice()), (20, second.as_slice()), (30, third.as_slice())];

let index = VectorIndex::build(IndexConfig::new(3), &vectors)?;
let hits = index.search_with_policy(
    &[1.0, 0.0, 0.0],
    2,
    SearchPolicy::high_recall(12),
)?;

assert_eq!(hits[0].key, 10);
assert_eq!(hits[1].key, 20);
# Ok::<(), weavatrix_search_vector::SearchError>(())
```

`VectorIndex::search_batch` preserves query order and reuses one visited set
and heap set per bounded standard-library worker. `VectorIndex::search_exact`
and `ExactIndex` provide deterministic ground truth for recall tests.
`SearchPolicy` independently controls graph expansion and routing recovery, so
raising `efSearch` is no longer the only recall lever.

## Persistence and live updates

```rust,no_run
use weavatrix_search_vector::{
    IndexBundle, IndexConfig, MappedVectorIndex, Metadata, MutableVectorIndex,
    QuantizationKind, QuantizedIndex, VectorIndex, VectorRecord,
};

let vector = [1.0, 0.0, 0.0];
let index = VectorIndex::build(IndexConfig::new(3), &[(10, vector.as_slice())])?;
index.save("vectors.wvx")?;

let mapped = MappedVectorIndex::open("vectors.wvx")?;
assert_eq!(mapped.search(&vector, 1)?[0].key, 10);

let mutable = MutableVectorIndex::build(
    IndexConfig::new(3),
    &[VectorRecord::new(10, vector.to_vec())],
)?;
mutable.upsert(20, &[0.9, 0.1, 0.0], Metadata::new())?;
mutable.delete(10);
mutable.seal_delta()?;

let active = [(20, [0.9, 0.1, 0.0])];
let active = active
    .iter()
    .map(|(key, value)| (*key, value.as_slice()))
    .collect::<Vec<_>>();
let compact = QuantizedIndex::build(
    IndexConfig::new(3),
    QuantizationKind::Float16,
    &active,
)?;
IndexBundle::new(mutable, Some(compact))?.save("complete.wvxb")?;
# Ok::<(), weavatrix_search_vector::SearchError>(())
```

`VectorIndex::save` writes normalized vectors, routing buckets, and all HNSW
replicas through a flushed temporary sibling. `MappedVectorIndex` validates
the version, ranges, graph references, finite values, and checksum before
serving queries directly from mapped vectors and graph arrays. Structure-only
validation is available for trusted immutable artifacts.

`MutableVectorIndex` separates cheap staged writes from a sealed HNSW delta, so
frequent writes do not force full base compaction. `IndexBundle` atomically
persists base and sealed graphs, staged vectors, tombstones, metadata, and the
optional quantized index under one checksum. Legacy f32 snapshots remain
loadable.

## Extended search primitives

- `MetadataIndex` and `MetadataFilter` compose equality, existence, numeric
  ranges, text prefixes, Boolean operations, and exact fallback.
- `QuantizedIndex` supports BF16, F16, F8-E4M3, I8, and packed binary
  representations. Compact, f32, and multi-vector indexes support
  file/buffer/stream round-trips.
- `MultiVectorIndex` preserves `(key, vector_id)` identity and can return either
  per-vector hits or the best hit per caller key.
- `EmbeddingProvider` and `EmbeddingIndex` batch external local or hosted
  encoders without selecting a model or adding a runtime dependency.
- `KnnGraph` builds deterministic directed candidate edges in compressed-row
  form; semantic thresholds and graph meaning stay with the consumer.
- `SearchShard` and `DistributedIndex` merge heterogeneous local, mapped,
  quantized, mutable, or remote-backed shards under one worker budget.

## Algorithm

Each HNSW replica uses:

- key-sorted normalized vector storage;
- seed/key-derived levels and insertion permutation;
- deterministic parallel bulk-construction waves;
- greedy upper-layer descent;
- bounded best-first layer search;
- diversified outgoing links and retained reverse links;
- doubled layer-zero outgoing degree;
- a compact 14-bit `SimHash` table with 5-probe low-overhead and 12-probe
  high-recall policies, configurable up to 470 deterministic buckets;
- runtime-dispatched first-party AVX2+FMA, AVX2, SSE2, NEON, and scalar
  kernels;
- exact configured-metric distances for every returned f32 HNSW hit.

Independent replicas and the construction work inside each replica share the
configured build-worker budget. The built index is immutable, `Send`, and
`Sync`. Memory is proportional to vector storage and retained graph links,
never to the square of the vector count.

## Reference benchmark

The reference gate is 10,000 vectors x 384 dimensions, cosine distance,
top-8, one warm-up and three release runs:

- build plus all 10,000 approximate queries at most 3 seconds on the reference
  Windows host;
- recall@8 at least 99.9% against the exact oracle;
- retained allocation estimate below 256 MiB;
- identical API behavior on Windows, Linux, and macOS;
- Rust 1.88, rustfmt, Clippy and rustdoc with warnings denied.

The 2026-07-27 Windows run passes the local performance, recall, and allocation
gates:

| Evidence | Result |
| --- | ---: |
| Median build, 3 runs | 197.007 ms |
| Median all-query search, 3 runs | 81.500 ms |
| Median build + search, 3 runs | 278.508 ms |
| Full-oracle recall@8, all 10,000 queries | 99.9888% |
| Estimated retained index allocation | 18.037 MiB |

Machine: Intel Core Ultra 7 255U, 12 cores / 14 logical processors, Windows 11
Enterprise 10.0.26200, `rustc 1.97.1` GNU. The corpus is deterministic,
synthetic, and clustered. The allocation figure is calculated from retained
vector/link capacities; it is not process RSS. See
[`docs/benchmark-2026-07-27.md`](https://github.com/sergii-ziborov/weavatrix-search-vector/blob/main/docs/benchmark-2026-07-27.md)
for commands,
raw results, and limitations.

```text
cargo bench --bench vector_search -- run
```

Environment variables `WV_VECTOR_COUNT`, `WV_VECTOR_DIMENSIONS`,
`WV_VECTOR_TOP_K`, `WV_VECTOR_RUNS`, `WV_VECTOR_EXACT_QUERIES`,
`WV_VECTOR_CONNECTIVITY`, `WV_VECTOR_EXPANSION_BUILD`,
`WV_VECTOR_EXPANSION_QUERY`, and `WV_VECTOR_REPLICAS` control the reproducible
corpus and policy. Set `WV_VECTOR_EXACT_QUERIES=10000` for the complete
reference recall calculation.

These figures establish this crate's disclosed workload, not a general speed
claim against another vector engine. Cross-engine benchmarks require identical
vectors, query sets, thread budgets, recall, and process-memory measurement.

## Competitors

The 0.3 quality-gated scale comparison uses 50,000 deterministic clustered
vectors x 384 dimensions, top-8, all 50,000 vectors queried, and three measured
runs after warm-up. Every displayed policy passes a 99.9% minimum recall gate
over the same 1,000 exact-oracle queries:

| Engine | Passing policy | Build | All-query search | Total | Minimum recall@8 | Retained memory |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| Weavatrix 0.3 | M12 / efC48 / efS12 / 12 probes | **1,792 ms** | **1,176 ms** | **2,968 ms** | 99.9750% | **90.437 MiB** |
| `usearch 2.26.0` | M16 / efC160 / efS128 | 17,755 ms | 1,826 ms | 19,580 ms | **100.0000%** | 144.736 MiB |
| `hnsw_rs 0.3.4` | M32 / efC256 / efS512 | 16,646 ms | 20,004 ms | 35,802 ms | 99.9875% | unavailable |

The policies are engine-specific because equal numeric HNSW parameters do not
produce equal recall. Weavatrix uses 14 build/query workers; `hnsw_rs` uses
14/14. `USearch` uses one build worker because its parallel builds did not pass
the common quality gate in this corpus, and 14 query workers. These are local
host measurements, not universal latency guarantees.

The earlier 50k audit exposed a real Weavatrix ceiling: changing efSearch from
24 through 64 left recall at 99.85%. `SearchPolicy` fixes the cause by
separating graph expansion from routing recovery. The final efS12/12-probe
policy reaches 99.975% and is faster than the previously observed 1.434-second
`USearch` query row at comparable quality.

See
[`docs/competitive-benchmark-0.3.0-2026-07-27.md`](https://github.com/sergii-ziborov/weavatrix-search-vector/blob/main/docs/competitive-benchmark-0.3.0-2026-07-27.md)
for commands, policies, failed quality candidates, limitations, and the
functional matrix.

## 0.3 functional expansion

Version 0.3 closes the main core-Rust API gaps identified against `USearch` and
`hnsw_rs`:

- cosine, dot-product, and squared-Euclidean f32 indexes;
- predicates applied during owned and mapped graph traversal;
- batch insert/upsert/delete, rename, sealed HNSW delta, and explicit
  compaction;
- checksummed atomic bundles for graphs, mutable delta, tombstones, metadata,
  and quantized state;
- BF16, F16, F8-E4M3, I8, and packed binary representations;
- multiple vectors per key with persisted `(key, vector_id)` identity;
- file, buffer, and stream serialization;
- per-query recall policy for owned and mapped indexes;
- AVX2+FMA, AVX2, SSE2, NEON, and scalar runtime dispatch.

AVX-512 and SVE intrinsics are not used because they are not stable under the
crate's Rust 1.88/MSRV contract. GPU runtimes, language bindings, and
distributed consensus remain separate integration layers rather than runtime
dependencies of this pure-Rust core.

## 0.2 feature evidence

On the same 10,000 x 384 corpus, the median of three separately launched
release runs opened the fully checksummed mapped snapshot in 50.096 ms,
executed 1,000 mapped HNSW queries in 42.577 ms, applied 100 in-memory upserts
in 0.165 ms, and compacted them in 241.023 ms. The 16.850 MiB snapshot and
3.777 MiB scalar-int8 retained estimate are different representations and
should not be compared as equal-recall algorithms.

See
[`docs/benchmark-0.2.0-2026-07-27.md`](https://github.com/sergii-ziborov/weavatrix-search-vector/blob/main/docs/benchmark-0.2.0-2026-07-27.md)
for raw runs, operation definitions, the full-recall core gate, and
limitations.

## Deliberate boundaries

The crate does not bundle embedding models, tokenizers, GPU runtimes, network
transports, distributed consensus, semantic thresholds, provenance policy, or
the Weavatrix domain graph. It supplies the vector, storage, mutation,
filtering, candidate-graph, and shard-coordination primitives those layers use.