Skip to content

vicinity

crates.iodocs.rsPyPI

Approximate nearest-neighbor search.

vicinity provides Rust indexes and Python bindings for vector search. HNSW is the default in-memory index. IVF-PQ is the compressed in-memory index. Other indexes are feature-gated and documented in docs/algorithms.md.

Install

[dependencies]
vicinity = { version = "0.11.1", features = ["hnsw"] }

Optional features enable additional indexes:

vicinity = { version = "0.11.1", features = ["ivf_pq"] }
vicinity = { version = "0.11.1", features = ["diskann"] }
vicinity = { version = "0.11.1", features = ["hnsw", "serde"] }

HNSW

HNSW is the default index for dense vectors that fit in memory. Cosine distance expects unit-norm vectors unless auto_normalize(true) is set.

use vicinity::hnsw::HNSWIndex;letmut index = HNSWIndex::builder(128).m(16).ef_search(50).auto_normalize(true).build()?;
index.add_slice(0,&[0.1;128])?;
index.add_slice(1,&[0.2;128])?;
index.build()?;let results = index.search(&[0.1;128],5,50)?;// Vec<(doc_id, distance)>; lower distance is closer.

Use DistanceMetric when you need L2, angular, or inner-product distance:

use vicinity::{distance::DistanceMetric, hnsw::HNSWIndex};let index = HNSWIndex::builder(384).metric(DistanceMetric::L2).build()?;

IVF-PQ

IVF-PQ stores compressed vectors and searches an inverted file. Use it when raw vectors dominate memory and lower recall is acceptable.

use vicinity::ivf_pq::{IVFPQIndex,IVFPQParams};let params = IVFPQParams{num_clusters:1024,num_codebooks:8,nprobe:16,
..Default::default()};letmut index = IVFPQIndex::new(128, params)?;for(id, vector)in dataset.iter().enumerate(){
index.add_slice(id asu32, vector)?;}
index.build()?;let compressed = index.search(&query,5)?;let reranked = index.search_reranked(&query,5,200)?;

search() uses PQ distances and works after compact(). search_reranked() keeps raw vectors and reranks a candidate pool with exact cosine distance.

See examples/ivf_pq_demo.rs for a runnable example.

Python

The Python package is pyvicinity.

pip install pyvicinity
importnumpyasnpfrompyvicinityimportDistanceMetric, HNSWIndexembeddings=np.random.default_rng(0).standard_normal((10_000, 384), dtype=np.float32)
index=HNSWIndex(
dim=384,
metric=DistanceMetric.Cosine,
auto_normalize=True,
seed=42,
)
index.add_items(embeddings)
index.build()
ids, distances=index.search(embeddings[0], k=10)
batch_ids, batch_distances=index.batch_search(embeddings[:32], k=10)

For compressed search:

frompyvicinityimportIVFPQIndexindex=IVFPQIndex(dim=384, num_clusters=256, num_codebooks=8, codebook_size=256)
index.add_items(embeddings)
index.build(training_sample_size=100_000, kmeans_max_iter=20)
ids, distances=index.search(embeddings[0], k=10, nprobe=16, rerank_pool=500)

Runnable Python examples are in examples/python/. The package ships .pyi stubs and a py.typed marker.

Python exposes the common HNSW constructor subset, HNSW JSON save/load, IVF-PQ directory save/load, and IVF-PQ file search. Mmap-backed IVF-PQ search is available in normal Python builds because the python feature includes persistence; Rust builds need the persistence feature for mmap=True. New Python APIs should first have stable Rust benchmarks, persistence behavior, and examples; the bindings are not intended to mirror every experimental Rust module. Rust-only surfaces include DiskANN, store::UpdatableIndex, FreshGraph, filtered search/update APIs, and HNSW binary segments.

Persistence

HNSW supports JSON save/load with the serde feature:

index.save_to_file("index.json")?;let loaded = HNSWIndex::load_from_file("index.json")?;

The persistence feature adds a binary segment format for HNSW. The store feature adds store::UpdatableIndex, a segmented index with add/delete, checkpoint, compaction, and crash recovery. See examples/updatable_store.rs.

Benchmarks

The benchmark runner writes JSONL rows with recall, QPS, build time, RSS, and latency percentiles:

cargo run --example ann_benchmark --release --features hnsw,ivf_pq,ivf_avq -- \
data/ann-benchmarks/glove-25-angular \
--algo hnsw --algo ivfpq --algo ivf_avq --json --fresh

Selected current full-corpus rows use 1,000 fixed queries and report the median of three isolated runs on an Apple M3 Max with Rust 1.97.1. Index bytes are in-memory heap estimates.

DatasetAlgorithmRecall@10QPSIndex bytes
GloVe-100 cosineHNSW, ef_search=160091.51%742.31,501,879,266
GloVe-100 cosineSQ8U, ef_search=40095.63%470.91,620,230,666
GloVe-100 cosineSQ4 flat99.99%25.2906,426,308
SIFT-128 L2HNSW, ef_search=20098.20%3,835.51,381,000,000
SIFT-128 L2SymphonyQG-VR, ef_search=10099.31%3,148.23,332,000,004
SIFT-128 L2IVF-RaBitQ, nprobe=6498.47%66.1840,131,072
SIFT-128 L2SQ4 flat99.22%19.6605,066,752

Commands, repeat spread, single-run screens, and historical results are in docs/benchmark-results.md.

Choosing an Index

WorkloadStart withTry next
Small corpus (<10K vectors)Brute forceHNSW when scale or latency requires
Dense vectors that fit in memoryHNSWNSW or Vamana
Raw vectors dominate RAMIVF-PQ or IVF-RaBitQCompare recall and probe cost on the target metric
Frequent writes/deletesEvaluate store::UpdatableIndexCompare FreshGraph, in-place HNSW, and LSM HNSW on churn rows
Metadata filtersHNSW with post-filteringACORN, Curator, and FilteredGraph need selectivity sweeps
Sparse learned retrievalSparseMIPSWorkload-specific sparse baseline
File-backed graph searchEvaluate DiskANNPromote after full-corpus mmap/file rows
File-backed compressed searchIVF-PQ file or mmap searcherAdd rerank only when the raw-vector locality cost is acceptable

The full algorithm table is in docs/algorithms.md.

Limits

  • Search is approximate; tune recall with ef_search, nprobe, and rerank pool sizes.
  • HNSW cosine and angular search need normalized vectors unless auto_normalize(true) is enabled.
  • DiskANN is available but still experimental for production file-backed search.
  • Some algorithms are research paths, not recommended defaults.

Documentation

License

MIT OR Apache-2.0

About

Approximate nearest-neighbor search

Topics

Resources

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages