Summary:
Iterator-backed IVF scans ignored the configured `IDSelector`, and array-backed
scans applied it. An iterator-backed scan therefore returned IDs that the
selector excludes. `InvertedListScanner::iterate_codes` and
`InvertedListScanner::iterate_codes_range` now test `sel` for every entry.
`search_preassigned` also keeps the selector for an iterable list. The function
clears `sel` when the selector is an `IDSelectorRange` that sets
`assume_sorted`, because an array-backed list bounds a sorted section instead of
testing every entry. An iterable list holds no contiguous section to bound, so
it keeps the selector and takes the generic path. `range_search` holds no
sorted-range shortcut and needs no equivalent change.
The test loops over both values of `assume_sorted`, and it checks the KNN search
and the range search.
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5592
Test Plan:
`buck2 test fbcode//faiss/tests:test_ivf_index fbcode//faiss/tests:test_custom_result_handler fbcode//faiss/tests:test_hnsw fbcode//faiss/tests:test_rabitq_simd_neon fbcode//faiss/tests:test_params_override fbcode//faiss/tests:test_hnsw_rabitq fbcode//faiss/tests:test_rabitq fbcode//faiss/tests:test_search_params fbcode//faiss/tests:test_io_corrupted fbcode//faiss/tests:test_rabitq_utils`, run at the top of the stack on aarch64: 173 pass, 0 fail, 3 skip.
`buck2 build fbcode//faiss:faiss` succeeds in the static aarch64 build and with `-c faiss.dynamic_dispatch=true`.
Reviewed By: alibeklfc
Differential Revision: D121846696
Pulled By: mnorris11
fbshipit-source-id: 09c8a3673fea6cc53f3370f05f3b6d399dfb4548
Summary:
The IVF list scan prepared state that an early return then discarded. This diff
moves `scanner->set_list`, the `ScopedCodes` fetch and the `ScopedIds`
allocation after every check that can return first. A list that an
`IDSelectorRange` excludes no longer builds a lookup table and no longer fetches
its codes. The diff also folds the `jmin` offset into the initial codes pointer,
and it replaces `std::unique_ptr<ScopedIds>` with `std::optional<ScopedIds>`.
The diff also fixes a null-pointer dereference. `search_preassigned` cleared
`sel` for a sorted `IDSelectorRange` before it tested
`!(sel && store_pairs)`. That combination then reached
`IDSelectorRange::find_sorted_ids_bounds`, which reads `ids[0]`. The `ids`
pointer is null when the caller sets `store_pairs`. The test now runs before the
code clears `sel`, so the combination throws.
`IVF.sorted_range_selector_rejects_store_pairs` covers it.
**Microbenchmark.** The host holds 72 Cooper Lake cores. The build is
`mode/opt`. One core is pinned, and the two binaries interleave over 5 rounds.
The table reports the median of the medians. `IndexIVFPQ`, `d=64`,
`nlist=1024`, 100k vectors, `nprobe=1024`, sorted `IDSelectorRange`.
| ids that pass the range | before | after | ratio |
|---|---|---|---|
| 1% | 836 us | 587 us | **1.43x** |
| 10% | 894 us | 926 us | 0.97x |
The gain grows with the number of lists that the range excludes. At 10% the
saving sits inside the noise.
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5595
Test Plan:
`buck2 test fbcode//faiss/tests:test_ivf_index fbcode//faiss/tests:test_custom_result_handler fbcode//faiss/tests:test_hnsw fbcode//faiss/tests:test_rabitq_simd_neon fbcode//faiss/tests:test_params_override fbcode//faiss/tests:test_hnsw_rabitq fbcode//faiss/tests:test_rabitq fbcode//faiss/tests:test_search_params fbcode//faiss/tests:test_io_corrupted fbcode//faiss/tests:test_rabitq_utils`, run at the top of the stack on aarch64: 173 pass, 0 fail, 3 skip.
`buck2 build fbcode//faiss:faiss` succeeds in the static aarch64 build and with `-c faiss.dynamic_dispatch=true`.
Reviewed By: alibeklfc
Differential Revision: D121846650
Pulled By: mnorris11
fbshipit-source-id: aa3d1d3e6f903ef9e57be5c3dd824a5b1a090228
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5695
Correct GPU tests to decode the exact packed fp16 values passed to `search_ex`; the generic software converter and legacy `roundToHalf` disagree on half-way ties. Keep labels exact while comparing CPU distances with the normal scaled `1e-5` BLAS/SIMD tolerance.
___
Differential Revision: D122785161
fbshipit-source-id: 057ded12bdc018add4bef39e514217b83b59c2cb
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5671
Train IVF coarse quantizers from packed fp16 (IEEE binary16) data without an fp32 copy of the training set, and let the k-means assignment step consume fp16 natively.
**k-means: `Clustering::train_ex(n, x, NumericType, index, weights)`**
Float16 runs k-means directly on the fp16 rows, without a codec:
- NaN/Inf check on the fp16 bit patterns
- subsampling and random initialization copy fp16 rows
- the centroid update accumulates fp16 rows into the fp32 centroids with F16C/NEON (`compute_centroids_fp16`)
- the assignment step passes blocks of at most `decode_block_size` fp16 rows to `Index::search_ex(Float16)`
Only k-means++ / AFK-MC2 initialization still widens the (subsampled) training set.
**Assignment: `Index::search_ex(Float16)`**
- Default for every index: widen bounded blocks and call `search()`.
- `IndexFlat` (L2 / IP): the BLAS kernels take fp16 queries (`knn_L2sqr_fp16`, `knn_inner_product_fp16`). Queries are widened inside the kernel in chunks of at most 16 MiB, each reused for all database tiles; results are bit-identical to the fp32 path on the rounded queries. The AVX2 / AVX512 / SVE top-1 kernels share one templated body. Selectors, small inputs, d <= 32 and database-parallel searches widen chunks and use the fp32 path. `IndexFlat1D` and `IndexFlatPanorama` keep their own search.
- GPU: every `GpuIndex` accepts Float16 (queries are copied to the device as fp16 and widened there); `GpuIndexFlat` with `useFloat16` storage searches fp16 queries directly with the fp16 GEMM.
**IVF**
- `IndexIVF::train_ex(Float16)` and `Level1Quantizer::train_q1_ex` use the native path; only the `train_encoder_num_vectors()` rows sampled for the encoder are widened. `IndexIVFFlat` trains only the coarse quantizer; `IndexIVFFlatDedup` rejects Float16.
- `train_encoded` (IVF and Clustering) remains the generic codec path, e.g. for decode-time normalization.
- `faiss::fp16_to_fp32` (faiss/utils/utils.h) is the shared bulk widening helper.
**Performance** (opt build, AMD Genoa: F16C conversion only, no fp16 arithmetic)
- top-1 knn, d=256, 65536 queries x 1024 centroids: fp32 22.2 ms, fp16 25.3 ms
- k-means, 1024 centroids, 10 iterations: fp32 270 ms, fp16 319 ms, identical centroids
On CPUs without fp16 arithmetic the GEMM still runs in fp32 on widened tiles, so fp16 is not faster than fp32 there. The gains are memory (fp16 data plus a bounded fp32 tile instead of a full fp32 copy) and a single hook (`search_ex`) for fp16-capable backends; GPU `useFloat16` already runs the fp16 GEMM directly.
Reviewed By: mnorris11
Differential Revision: D121848262
fbshipit-source-id: 1400d3eeb7c713359895923dcb86c544570cba69
Summary:
- Use `is_similarity_metric()` for IVF heap initialization, merging, reordering, and result handling.
- Add Jaccard IVFFlat regression coverage across parallel modes 0-3.
## Why
Jaccard scanners use similarity ordering, but `IndexIVF::search_preassigned` selected distance heaps for every metric except inner product. The resulting `FLT_MAX` threshold rejected all Jaccard candidates, returning `-1` even when the only inverted list was fully scanned.
Fixes https://github.com/facebookresearch/faiss/issues/5399.
## Validation
- [x] `cmake --build build --target faiss_test --config Release -j 8`
- [x] `ctest --test-dir build --output-on-failure -C Release` (251 tests, 5 skipped)
- [x] `clang-format-21 --dry-run --Werror faiss/IndexIVF.cpp tests/test_ivf_index.cpp`
## Notes for reviewers
Python and GPU suites were not run locally; the fix and regression exercise the shared C++ IVF path.
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5408
Reviewed By: pankajsingh88, mnorris11
Differential Revision: D112830842
Pulled By: bshethmeta
fbshipit-source-id: c6431d2bee1b4fecd2e46cfda397722947a5407a
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5082
Add two virtual callback methods to FAISS's InvertedListsIterator that fire during the iterate_codes hot loop:
- on_distance_computed(vid, distance): called after computing the distance for each scored vector, before the heap comparison. Enables callers to observe every distance computation (e.g. for building a reserve pool of all scored vectors sorted by distance).
- on_heap_changed(new_id, evicted_id): called when a vector displaces the current worst entry in the top-K heap. Enables callers to react to heap admission events (e.g. maintaining an O(K) PK mapping instead of O(N)).
Both methods have default no-op implementations, so existing code is unaffected. The callbacks are invoked in both the min-heap (L2) and max-heap (IP) branches of iterate_codes.
Zero overhead for non-users: the callbacks are guarded by a boolean flag (has_search_callbacks_, default false) that iterate_codes checks before each virtual dispatch. A correctly-predicted branch on modern x86 costs ~0 cycles due to speculative execution, compared to ~3-5 cycles per virtual call through a vtable. Since the flag is always false for non-callback users, the branch
predictor learns it immediately and never mispredicts.
Alternative approaches considered and why this design was chosen:
- CRTP / templated iterate_codes: would achieve true zero overhead via static dispatch and if-constexpr elimination, but requires changing FAISS's public API and template-instantiating iterate_codes — too invasive for upstream.
- Marking derived classes `final`: only enables devirtualization when the compiler sees the concrete type, which it cannot through InvertedListsIterator base pointers in iterate_codes.
- Raw function pointers with null checks: equivalent performance to the guard flag approach, but less clean API semantics for FAISS's object-oriented design. The guard flag achieves the best tradeoff: zero overhead for non-users, clean virtual override semantics for users, minimal code change, backward compatible.
Reviewed By: mnorris11
Differential Revision: D100352032
fbshipit-source-id: 42cfd24b4fbca1af9ccf07a2bb29528f35173659
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5040
Add the missing `key < nlist` upper-bound check in
`IndexIVF::search1()`, which was the only IVF search entry point
lacking this validation. The other two paths —
`search_preassigned()` and `range_search_preassigned()` — already
had this check.
Also add deserialization acceptance tests verifying that IVF indexes
with various quantizer states deserialize correctly:
1. **Surplus centroids** (`ntotal > nlist`):
Produced by `shard_ivf_index_centroids()`, which distributes
all of the original quantizer's centroids across shards without
adjusting `nlist`. The search-time `key < nlist` bounds check
prevents OOB access if the quantizer returns out-of-range keys.
2. **Trained quantizer** (`ntotal == nlist`):
The normal trained IVF state.
3. **Sharded quantizer** (`0 < ntotal < nlist`):
Also produced by `shard_ivf_index_centroids()`, when the
original quantizer has `ntotal == nlist` and centroids are
split across shards.
4. **Untrained quantizer** (`ntotal == 0`):
Legitimate for custom inverted list management.
Reviewed By: mnorris11
Differential Revision: D99494237
fbshipit-source-id: 6a76b55f104b9c233dfdd2625bb0336ed8061463
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5053
C++ exceptions thrown inside `#pragma omp parallel` regions that are not
caught within the region call `std::terminate` — they cannot propagate
across thread boundaries.
`IndexIVF::range_search_preassigned` had the same class of bugs fixed in
`search_preassigned` by D99455250:
1. **`scan_list_func` lambda**:
`FAISS_THROW_IF_NOT_FMT(key < nlist)` was above the try-catch block,
so a corrupt key >= nlist would throw uncaught and call
`std::terminate`.
2. **Outer parallel region**:
`get_InvertedListScanner()`, `scanner->set_query()`, and
`FAISS_THROW_IF_NOT(scanner.get())` had no try-catch at all.
Fixes:
1. Moved the existing try-catch in `scan_list_func` up to also cover the
key validation.
2. Wrapped the entire `#pragma omp parallel` body in a try-catch that
uses the existing `interrupt`/`exception_string`/`exception_mutex`
pattern to safely propagate exceptions out of the parallel region.
Reviewed By: mnorris11
Differential Revision: D99879998
fbshipit-source-id: e768372cbaf8a22a9459fc3fd9b9df6e019897a6
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5037
C++ exceptions thrown inside `#pragma omp parallel` regions that are not
caught within the region call `std::terminate` — they cannot propagate
across thread boundaries.
`IndexIVF::search_preassigned` had multiple unguarded throw sites inside
its OpenMP parallel region:
1. **`scan_one_list` lambda**:
`FAISS_THROW_IF_NOT_FMT(key < nlist)` and `scanner->set_list()` were
above the try-catch block, so a corrupt key >= nlist would throw
uncaught and call `std::terminate`.
2. **Outer parallel region**:
`get_InvertedListScanner()` and `scanner->set_query()` had no
try-catch at all.
Fixes:
1. Moved the existing try-catch in `scan_one_list` up to also cover the
key validation and `set_list` calls.
2. Wrapped the entire `#pragma omp parallel` body in a try-catch that
uses the existing `interrupt`/`exception_string`/`exception_mutex`
pattern to safely propagate exceptions out of the parallel region.
Reviewed By: mnorris11
Differential Revision: D99455250
fbshipit-source-id: 41e8816d71c031b1e8526f76bd5a8936f4d4f160
Summary:
- Fix compiler warnings in all 20 FAISS C++ test files
- Fixes include `-Wshadow`, `-Wunused-parameter`, and signed/unsigned comparisons
All changes are mechanical. No functional changes.
Part 13/13 of the compiler warnings cleanup (split from https://github.com/facebookresearch/faiss/issues/4810 per maintainer request).
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4944
Test Plan:
- [x] No functional changes — all fixes are mechanical renames and annotations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Reviewed By: mnorris11
Differential Revision: D96996609
Pulled By: alibeklfc
fbshipit-source-id: 72555246a4ccf540eb5422e64018c8d5e2660dce
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4029
Remove headers flagged by facebook-unused-include-check over fbcode.faiss.
+ format and autodeps
This is a codemod. It was automatically generated and will be landed once it is approved and tests are passing in sandcastle.
You have been added as a reviewer by Sentinel or Butterfly.
Autodiff project: uif
Autodiff partition: fbcode.faiss
Autodiff bookmark: ad.uif.fbcode.faiss
Reviewed By: dtolnay
Differential Revision: D65957849
fbshipit-source-id: f6199250db595defd56f5e7b2828f838702e9a16
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3247
add a context parameter to be passed to InvertedLists and InvertedListsIterator.
- add a context field in `SearchParametersIVF`, the context will be passed to `InvertedLists::get_iterator`. The user can create `InvertedListsIterator` with the context object
- add a context parameter in `IndexIVF::add_core` method. the context will be passed to `InvertedLists::add_entry`.
The user can use the context object to pass storage handlers, store error codes from storage layer, logging information, etc.
Reviewed By: mdouze
Differential Revision: D53113911
fbshipit-source-id: ff31d247d3dc949d0bb50bcaffc3142efd027089