Skip to content

Releases

mlxVS 0.12.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 20:28

mlxVS 0.12.0: CAGRA-Q, the graph search over VPQ codes.

pip install -U mlxvs
  • CAGRA-Q: CagraIndex.compress() (C++ vpq_build, graph_search_vpq) replaces the stored rows by cuVS's VPQ
    codes: each row's nearest coarse center (sqrt(n) of them) plus ceil(d / 4) codes of its residual in one
    shared 256-entry codebook. The graph search computes distances from the codes (both single- and multi-CTA, filters
    included); search(..., refine_ratio=4, dataset=rows) re-ranks exactly, from rows on the GPU or in a NumPy array
    or numpy.memmap. SIFT1M: 36 MB instead of 505 MB, compressed in 6.3 s; 234,000 queries/s from the codes alone
    (recall@10 0.716), recall@10 0.981 at 103,000 queries/s refined (itopk_size 128).
  • search_kernels.h: the VPQ kernels are the same sources with the distance function swapped.

mlxVS 0.11.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 20:18

mlxVS 0.11.0: IvfPqIndex and refine.

pip install -U mlxvs
  • IvfPqIndex (C++ ivf_pq_build, ivf_pq_search, ivf_pq_extend), as cuVS's IVF-PQ: k-means lists, rows
    stored as pq_dim codes of pq_bits (4 to 8) of their residual to the center, per-subspace codebooks, cuVS's
    pq_dim heuristic (zero columns pad the dimension to a multiple of pq_dim). Searches build a float32 lookup
    table per (query, list) in chunks of subspaces and sum it over each row's codes. SIFT1M: 63 MB of codes for
    505 MB of rows, built in 21.7 s; with refine_ratio=4 recall@10 0.954 at 18,300 queries/s (n_probes 20) and
    0.993 at 8,100 (50), as IVF-Flat's.
  • refine (C++ and Python), as cuVS's: exact re-ranking of candidate ids. From Python the rows may be a NumPy
    array or numpy.memmap: only the candidates' rows are read, on the host.

mlxVS 0.10.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 20:05

mlxVS 0.10.0: CagraIndex.extend and IvfFlatIndex.

pip install -U mlxvs
  • CagraIndex.extend (C++ cagra_extend), as cuVS's cagra::extend: each new row's 2 * degree nearest rows
    come from a graph search and are pruned to degree by rank-based detour counts; each of its first degree / 2
    neighbors gives it the second-half slot of its neighbor with the most incoming edges; its list interleaves its
    own neighbors with those displaced. 18,000 rows extended by 2,000 search as well as an index built on all
    20,000 (recall@10 0.9994 against 0.9998).
  • IvfFlatIndex (C++ ivf_flat_build, ivf_flat_search, ivf_flat_extend), as cuVS's IVF-Flat: k-means lists,
    the n_probes lists of nearest center scanned exactly. The scan groups (query, list) pairs by list, so each
    threadgroup reads a list's rows once for up to 16 queries. SIFT1M, 1024 lists, built in 9.5 s: recall@10 0.954
    at 35,700 queries/s (n_probes 20), 0.993 at 17,300 (50). save and load as one .safetensors file.

mlxVS 0.9.1

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 19:04

mlxVS 0.9.1: deterministic ties in brute_force_knn.

pip install -U mlxvs
  • brute_force_knn (and every exact k-NN built on it): when more candidates tie with the k-th score than are
    kept, the ones of lowest id are kept. Before, the threads that arrived first decided, so ties (frequent with
    float32 distances of data far from the origin) could give different neighbors from run to run. scikit-learn's
    check_fit_idempotent caught it through the sklearn-ann transformer.

mlxVS 0.9.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 15:03

mlxVS 0.9.0: a filter per query, and byte datasets in the C API.

pip install -U mlxvs
  • A filter per query, as cuVS's bitmap filter: filter= also takes bool [nq, n] or uint32 [nq, ceil(n / 32)];
    C API mlxvs_index_search_bitmap.
  • C API byte datasets: mlxvs_index_build_typed with MLXVS_INT8 or MLXVS_UINT8 rows, stored as they are.
  • The C API's search functions share one implementation.

mlxVS 0.8.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 13:46

mlxVS 0.8.0: hnswlib and JVector export from C++, C and Java.

pip install -U mlxvs
  • save_hnswlib and save_jvector in C++ (include/mlxvs/mlxvs.h) and the C API (mlxvs_index_save_hnswlib,
    mlxvs_index_save_jvector); CagraIndex.save_hnswlib and save_jvector call them and write the same bytes as
    before. From Java: build on the GPU through the C API and write a file JVector 4 searches
    (examples/java_ffm, then examples/jvector).

mlxVS 0.7.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 12:44

mlxVS 0.7.0: int8 and uint8 datasets.

pip install -U mlxvs
  • Byte datasets, as cuVS's int8 and uint8 datasets: graph_search reads int8 and uint8 rows as they are
    (four columns per 32-bit load), and CagraIndex.build(..., dtype="uint8") (or "int8") stores rows that are
    integers in range, a quarter of float32's memory. Results equal float32's on integer data. SIFT1M quantized to
    uint8 (its file holds normalized floats, so this loses precision): 120 MB, 478,000 queries/s at recall@10 0.925
    and 267,000 at 0.965 (float32: 481 MB, 259,000 at 0.935 and 148,000 at 0.980).
  • bench_search.py --dtype uint8 and int8.

mlxVS 0.6.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 12:00

mlxVS 0.6.0: filtered search and seed nodes, as cuVS.

pip install -U mlxvs
  • Filtered search, as cuVS's bitset filter: graph_search(..., filter=) (and CagraIndex.search) takes a bool mask
    [n] or a bitset uint32 [ceil(n / 32)]. Rejected rows are still traversed but leave the list once expanded
    and are never returned; multi-CTA's list grows with the filtering rate as cuVS's (filtering_rate, computed from
    the filter by default). SIFT1M, 10% of rows allowed: recall@10 0.978 multi-CTA (single-CTA, as in cuVS, 0.74 at
    itopk_size 64 and 0.86 at 256).
  • Seed nodes, as cuVS's seed_ptr: seeds= [nq, num_seeds] start nodes per query.
  • C API: mlxvs_index_search_filtered and mlxvs_index_search_seeded.
  • The README's roadmap records an experiment left out: Vamana graphs from search-path candidates (DiskANN's) stay
    below CAGRA's graph on SIFT1M for four to five times the build time.

mlxVS 0.5.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 11:28

mlxVS 0.5.0: a C API, and mlxvs from Java through the FFM API.

pip install -U mlxvs
  • A C API, include/mlxvs/mlxvs_c.h in libmlxvs_c.dylib: an index handle (mlxvs_index_build, _search,
    _save, _load, _from_graph, _graph), mlxvs_brute_force_knn, parameter structs with cuVS's defaults,
    status codes and mlxvs_last_error. Only the mlxvs_* symbols are exported. Indexes are CagraIndex's
    .safetensors files: built in Python, they load in C, and the other way round.
  • examples/java_ffm: mlxvs from Java 22+ through the Foreign Function and Memory API, no JNI.
  • cmake --install installs the C library and header.

mlxVS 0.4.0

Choose a tag to compare

@mikepapadim mikepapadim released this 12 Nov 11:10

mlxVS 0.4.0: Vamana pruning with JVector's diversity rule on the GPU.

pip install -U mlxvs
  • vamana_prune: Vamana's robust pruning with JVector's diversity rule (VamanaDiversityProvider: passes with
    alpha = 1.0, 1.2, ... over the candidates by similarity, JVector's similarities) on the GPU; it equals a CPU
    reference of JVector's rule on every row of the tests.
  • vamana_build: k-NN graph, vamana_prune, then each node's list with its incoming edges of lowest rank pruned
    again; CagraIndex.build(..., graph="vamana", alpha=1.2). SIFT1M, degree 32 from 64 candidates: built in 15 s,
    recall@10 0.974 at itopk_size 64 (CAGRA's graph 0.980), and the same in JVector after save_jvector.
  • The reverse-edge passes of cagra_optimize are shared with vamana_build.