59 Commits
Author SHA1 Message Date
kzahiri1 b4c66ba603 Fix reverse_index_factory/get_code_size round-trip for HNSW, IMI and IVFPQR (#5564)
Summary:
Three defects in `contrib/factory_tools.py` where a reversed factory string rebuilds a different index or fails to parse. `test_get_code_size_hnsw_roundtrip` already pins this invariant for flat HNSW; these cases escape it.

1. **HNSW storage dropped.** The `IndexHNSW` branch never read `index.storage`, so `HNSW32,SQ8` and `HNSW32,PQ16x8` both reversed to `"HNSW32"` and rebuilt as `IndexHNSWFlat`. `get_code_size` then applied the flat `d*4` formula: at d=128 it reported 768 bytes/vector for an index whose storage is 128 plus the 256-byte graph estimate, so 384.

2. **IMI regex fields transposed.** `index_factory.cpp:295` parses `IMI2x([0-9]+)`, but the regex read `IMI\d+x2`. Every real IMI key (`IMI2x10,PQ16`, and the ones in `demos/demo_auto_tune.py`) raised `RuntimeError("cannot parse")`.

3. **IndexIVFPQR matched by the IndexIVFPQ check**, dropping the refinement stage: `IVF64,PQ8+16` reversed to `IVF64,PQ8x8`, 8 bytes instead of 24.

Consumers are `benchs/bench_all_ivf/parse_bench_all_ivf.py` (imports `get_code_size` as `unitsize`) and `benchs/bench_fw/index.py`.

Flat storage stays implicit as the bare `HNSW<M>` string, and a null `storage` pointer still returns `HNSW<M>` instead of raising.

**Behavior change worth flagging:** `IndexHNSW2Level` storage is `Index2Layer`, which `reverse_index_factory` does not handle, so it now raises `NotImplementedError` instead of silently returning a wrong `"HNSW32"`. It is not reachable from `index_factory`.

Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5564

Test Plan:
Four tests added to `TestFactoryTools`. All four fail on unpatched `factory_tools.py` and pass with it:

```
# pristine contrib/factory_tools.py, new tests present
4 failed, 7 passed
FAILED test_get_code_size_hnsw_non_flat_storage
FAILED test_get_code_size_imi
FAILED test_hnsw_storage_reverse_index_factory
FAILED test_ivfpqr_reverse_index_factory

# with the fix
11 passed
```

Full `tests/test_contrib.py tests/test_factory.py`, pristine vs branch:

```
pristine: 2 failed, 88 passed
branch:   2 failed, 92 passed
```

Failure sets are identical. The two are `TestComputeGT::test_compute_GT_gpu` and `test_compute_GT_ip_gpu`, pre-existing and unrelated (`AttributeError: module 'faiss' has no attribute 'GpuMultipleClonerOptions'` on a CPU-only build).

An A/B sweep over 40 factory strings changes exactly the 7 expected lines and nothing else:

```
- HNSW32,SQ8      IndexHNSWSQ   HNSW32        768
+ HNSW32,SQ8      IndexHNSWSQ   HNSW32,SQ8    384
- HNSW32,SQfp16   IndexHNSWSQ   HNSW32        768
+ HNSW32,SQfp16   IndexHNSWSQ   HNSW32,SQfp16 512
- HNSW32,PQ16x8   IndexHNSWPQ   HNSW32        768
+ HNSW32,PQ16x8   IndexHNSWPQ   HNSW32,PQ16x8 272
- IVF64,PQ8+16    IndexIVFPQR   IVF64,PQ8x8   8
+ IVF64,PQ8+16    IndexIVFPQR   IVF64,PQ8+16  24
- IMI2x5,Flat     IndexIVFFlat  IMI2x5,Flat   <RuntimeError>
+ IMI2x5,Flat     IndexIVFFlat  IMI2x5,Flat   512
- IMI2x5,PQ8      IndexIVFPQ    IMI2x5,PQ8x8  <RuntimeError>
+ IMI2x5,PQ8      IndexIVFPQ    IMI2x5,PQ8x8  8
- IMI2x8,PQ8x8    IndexIVFPQ    IMI2x8,PQ8x8  <RuntimeError>
+ IMI2x8,PQ8x8    IndexIVFPQ    IMI2x8,PQ8x8  8
```

flake8 at the 80-char limit from CONTRIBUTING reports the same 12 pre-existing findings before and after, none in the changed lines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed By: mnorris11

Differential Revision: D118661065

Pulled By: trang-nm-nguyen

fbshipit-source-id: b50a9522361e3fd14f276b36196d3c728567b980
2026-09-04 11:14:06 -07:00
mayuriphad 02ea14372c Fix range_search_max_results to respect all similarity metrics (#5533)
Summary:
## Bug

`range_search_max_results()` (and its helper `apply_maxres()`) in `contrib/exhaustive_search.py` decide whether to keep the highest- or lowest-scoring results when pruning the accumulated results down to `max_results`, based on:

```python
keep_max=index.metric_type == faiss.METRIC_INNER_PRODUCT
```

This hardcodes the assumption that `METRIC_INNER_PRODUCT` is the only "similarity" metric (higher = better). But `faiss.is_similarity_metric()` (see `faiss/MetricType.h`) also treats `METRIC_Jaccard` as a similarity metric, and this same, correct check is already used a few lines above in `range_search_gpu()` in the very same file (`keep_max = faiss.is_similarity_metric(index_gpu.metric_type)`).

As a result, when `range_search_max_results` is used with a `METRIC_Jaccard` index and the result set grows past `max_results`, the pruning logic incorrectly discards the *highest*-similarity matches and keeps the *lowest*-similarity ones, and moves the returned radius in the wrong direction.

## Root cause

Two call sites in `contrib/exhaustive_search.py` compare `index.metric_type` directly against `faiss.METRIC_INNER_PRODUCT` instead of using the library's own `faiss.is_similarity_metric()` helper, which is the single source of truth for which metrics are "larger is better".

## Fix

Replace both occurrences of `index.metric_type == faiss.METRIC_INNER_PRODUCT` with `faiss.is_similarity_metric(index.metric_type)`, matching the existing pattern used in `range_search_gpu` in the same module.

Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5533

Test Plan:
Added `test_query_iterator_similarity_metric` to `tests/test_contrib.py`. It exercises `range_search_max_results` against a lightweight fake index using `METRIC_Jaccard` (real `IndexFlat.range_search` doesn't support `METRIC_Jaccard` directly, so a fake index is used to isolate the pruning logic under test) and asserts that pruning keeps the highest-scoring results and raises the radius accordingly.

- Confirmed the new test fails on `main` (old code keeps the *lowest*-similarity results and lowers the radius) and passes after the fix, by swapping the module in-place against an installed `faiss-cpu` and running the assertions directly (full repo `pytest` collection isn't possible in this environment without rebuilding the C++ extension for `main`, since the installed wheel is older and `tests/common_faiss_tests.py` on `main` references APIs from a newer build).
- The fix is a pure two-line change that only affects the boolean passed to `apply_maxres`, verified by direct comparison of before/after behavior with the reproduction script.

Reviewed By: mnorris11

Differential Revision: D117001619

Pulled By: alibeklfc

fbshipit-source-id: 27e6757951d63a4d4f4f998640a1726ef8f6c220
2026-08-26 15:55:01 -07:00
Alibek Zhakubayev a238933d17 Finish E501 line-length lint sweep across faiss (183 violations, 52 files) (#5444)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5444

We fix every remaining flake8 E501 (line > 80 chars) violation in
fbcode/faiss/ (excluding gpu/), closing out a lint-cleanup initiative that a
June 29, 2026 diff stack (9a05463576 -> d304e29b9a -> 7d140d86ca) explicitly
promised but left 41% incomplete. The middle diff in that stack aligned
.flake8's max-line-length to black's 80-char width and stated the remaining
277 violations after the config change were "genuine lint (E501 long
strings/URLs, F401/F841/B011, etc.), addressed in later commits in this
stack." The follow-up commit fixed only the F401/F403/F841/B011 categories
(45 issues) and never touched E501, leaving 192 violations across 53 files
live in trunk. A direct re-scan today found 201 violations across 54 files
(two files gained lines since the original count).

We fixed 183 violations across 52 files. Two of the originally-flagged
files, benchs/bench_rabitq.py and
demos/index_pq_flat_separate_codes_from_codebook.py, already carry a
file-level `# flake8: noqa` directive (the second is a Bento/Grimaldi
notebook export that also fails standalone py_compile due to embedded shell
magic) -- flake8 ignores both files entirely, so no changes were needed
there and they are excluded from this diff.

Fix approach, by category:
- The large majority of violations are prose: docstrings and comments
  rewrapped at word boundaries, preserving each file's existing indentation
  and docstring conventions. No wording was changed.
- Code lines (function calls, f-strings, assert messages, long argument
  lists) were reflowed in black-compatible style: parenthesization, comma
  breaks, or implicit adjacent string-literal concatenation. Every touched
  file is confirmed unchanged by `black --line-length 80 --diff` after the
  edit.
- A small number of genuinely unwrappable long URLs (no reasonable break
  point) were suppressed with `# noqa: E501` rather than force-wrapped,
  matching the precedent set by the original black-format diff stack.
- demos/offline_ivf/offline_ivf.py: a repeated, non-trivial ratio expression
  used in both an assert condition and its (lazily-evaluated) failure
  message was hoisted into a single local variable, eliminating the
  duplicate computation. The expression is pure and deterministic (built
  from already-computed arrays), so this is behaviorally identical to the
  original -- not a functional change.
- `arc lint -a` additionally auto-fixed a real bug it detected while
  processing these files: `raise NotImplemented` -> `raise
  NotImplementedError` in contrib/evaluation.py (the sentinel object
  NotImplemented is not an exception type; raising it produces a TypeError
  masking the intended NotImplementedError). It also added a missing
  trailing newline in benchs/fb/bench_index_binary_from_float.py and, in
  the same file, rewrote the `#!/usr/bin/env python2` shebang to
  `#!/usr/bin/env fbpython` -- that rewrite was reverted by hand, since the
  file still does `import cPickle` (a Python 2-only module), so the
  auto-fix would have made the shebang describe an interpreter the script
  cannot actually run under. Fixing the underlying py2-to-py3 migration is
  out of scope for this line-length-only sweep.
- Rebasing onto master (this diff's base commit had drifted) surfaced two
  concurrent upstream fixes to `check_ref_knn_with_draws(...)` calls in
  tests/test_ivf_flat_panorama.py and tests/test_refine_panorama.py (an
  unrelated in-flight correctness fix passing `D_panorama`/`atol` instead
  of a stale `D_regular` arg with no `atol`). The rebase took upstream's
  corrected arguments and re-applied only this diff's line-wrap on top.

We verified zero functional change with an AST-level check: every string
and f-string literal in every touched file was extracted before and after
the edit and compared with whitespace normalized, confirming no wording,
value, or expression was altered beyond the intended `# noqa: E501`
suffixes and the one documented ratio-hoisting simplification above.

Reviewed By: mnorris11

Differential Revision: D112525458

fbshipit-source-id: e4b85d73dafcaac779f3522a383a66e490fa942a
2026-07-17 10:20:42 -07:00
Tia Lim a7d91d44d9 black -l 80: tests/ formatting (2/3) (#5427)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5427

# this is a no-op change

Part 2 of a 3-diff split of the faiss `black -l 80` reformat into small, reviewable diffs (each 3-4 files, <100 changed lines). Follow-up to D109736099 (landed), which formatted the faiss Python tree; these files drifted or are new.

Ran `black -l 80` (line length 80, matching faiss's `CONTRIBUTING.md`: "80 character line length (both for C++ and Python)") over the test files: `tests/test_build_blocks.py`, `tests/test_contrib.py`, `tests/test_eden.py`, `tests/test_ivf_flat_panorama.py`.

Pure-formatting only. `black`'s AST-equivalence safety check guarantees no identifier, string/numeric value, operator, or control-flow change — only whitespace, wrapping, trailing commas, and quote normalization. Independently verified: `ast.dump` equality (docstrings normalized) and comment-content preservation. `fbcode/faiss/` is excluded from Meta's standard Python autoformatter (it mirrors to public GitHub), so `black -l 80` is the correct tool.

___

Reviewed By: trang-nm-nguyen

Differential Revision: D112191719

fbshipit-source-id: 20fc89768077a16fb6abd6c2fe58981c3febf0ea
2026-07-15 17:47:15 -07:00
Alibek Zhakubayev e1f8d4fc0d Fix reverse_index_factory for multi-bit RaBitQ indexes (#5419)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5419

### THIS DIFF

Fix two bugs in `reverse_index_factory` for multi-bit RaBitQ indexes, in
both the C++ and Python implementations.

**C++ (`factory_tools.cpp`):**
- `IndexIVFRaBitQ` case returned `prefix + ",RaBitQ"` unconditionally,
  regardless of `rabitq.nb_bits`. For a 2-bit index built from
  `"IVF_nlist,RaBitQ2"`, the function returned `"IVF_nlist,RaBitQ"` (1-bit
  factory string). This means `TelemetryWrapper` logs the wrong index type
  for every production multi-bit RaBitQ IVF index.
- `IndexRaBitQ` (standalone) had the same flaw — always returned
  `"RaBitQ"` regardless of `nb_bits`.

**Python (`contrib/factory_tools.py`):**
- Neither `IndexIVFRaBitQ` nor `IndexRaBitQ` had a case in
  `reverse_index_factory`, so any Python caller using this API on a RaBitQ
  index raised `NotImplementedError`.

**Fix:** Read `rabitq.nb_bits` from the typed cast result and append the
bit count when `nb_bits > 1` (e.g., `"RaBitQ2"`). For `nb_bits == 1`
the output is still `"RaBitQ"`, preserving backward compatibility with
existing logs and round-trip tests.

### CONTEXT

Multi-bit RaBitQ (`nb_bits=2..9`) was added alongside standard 1-bit
RaBitQ. `index_factory` has parsed `"RaBitQ2"`, `"RaBitQ4"`, etc., since
the feature landed, but `reverse_index_factory` was never updated to emit
these strings. Every production `IndexIVFRaBitQ` with `nb_bits > 1`
(including the RaBitQ2 deployment) logs `",RaBitQ"` (1-bit) in telemetry.
The Python API has silently been broken for all RaBitQ index types.

The fix pattern matches how other parameterized types (`PQMxN`, `HNSWNM`,
etc.) embed their parameters in the factory string.

Reviewed By: mnorris11

Differential Revision: D110042828

fbshipit-source-id: d4e976bd9fc99691a20fb33d893af2d36deeb1e1
2026-07-14 12:27:43 -07:00
Alibek Zhakubayev bf53109ec1 Fix get_code_size to handle HNSW coarse quantizers with any M value (#5416)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5416

We fix two bugs in `get_code_size` (`contrib/factory_tools.py`) that raise
`RuntimeError` for any valid HNSW factory string with M≠32.

**Bug 1 — IVF+HNSW regex hardcodes M=32 (line 20):**
`re.match("IVF\\d+(_HNSW32)?,(.*)")` — `_HNSW8`, `_HNSW16`, `_HNSW64` do
not match. Execution falls through all branches to
`raise RuntimeError("cannot parse IVF64_HNSW16,Flat")`.
Fix: change `_HNSW32` to `_HNSW\\d+`.

**Bug 2 — Standalone HNSW string comparison hardcodes M=32 (lines 48–49):**
`if indexkey == "HNSW32" or indexkey == "HNSW32,Flat"` — any other M falls
through to `RuntimeError`. Fix: replace with
`re.match("HNSW(\\d+)(,Flat)?$")` and generalized formula
`d * 4 + M * 2 * 4` (equivalent to the original `d * 4 + 64 * 4` for M=32;
the `# roughly` comment is preserved since HNSW graph overhead depends on
ntotal and level distribution).

**Root cause:** `reverse_index_factory` already correctly handles any M via
`get_hnsw_M()` — line 96 produces `"IVF{n}_HNSW{M},..."`, line 125 produces
`"HNSW{M}"`. The standard roundtrip
`get_code_size(d, reverse_index_factory(index))` crashes for every
IVF+HNSW or standalone HNSW index with M≠32. HNSW M=16 and M=64 are common
production configurations.

Reviewed By: limqiying

Differential Revision: D109142320

fbshipit-source-id: 7ddcfd83211b7e95e1bab20a3dee017c4d08e450
2026-07-13 12:59:16 -07:00
Osamaali313 bd6760153c Fix OperatingPoints leaving dominated points on the Pareto frontier for equal-time points (#5334)
Summary:
`OperatingPoints.add_operating_point` (in `contrib/evaluation.py`) maintains a Pareto-optimal set of `(key, perf, t)` points where higher `perf` and lower `t` are better. The admission test `is_pareto_optimal` rejects a new point when an existing one dominates it using **non-strict** comparisons in both dimensions:

```python
if perf >= perf_new and t <= t_new:   # existing dominates new
    return False
```

But the prune loop that removes previously-stored points the new point now dominates used a **strict** time comparison:

```python
if perf >= perf2 and t < t2:           # <-- strict on time
    self.suboptimal_points.append(self.operating_points.pop(i))
```

Because of this asymmetry, when a new point has **strictly better performance at the same time** as a stored point, the new point is admitted (it is not dominated), but the older point it now dominates is **not** removed. The frontier then contains a point dominated by another point in the same set, breaking the Pareto invariant. Dominated operating points leak into the "optimal" set used by the benchmark/auto-tune machinery (`benchs/bench_fw/utils.py`, `benchs/bench_hybrid_cpu_gpu.py`). This also diverges from the C++ `AutoTune::add` this is documented to port (`faiss/AutoTune.cpp`), which replaces an equal-perf point at lower-or-equal time.

## Fix

Use `t <= t2` in the prune loop so it matches the admission test:

```diff
-                if perf >= perf2 and t < t2:
+                if perf >= perf2 and t <= t2:
```

This is safe: identical points never reach the loop (they fail `is_pareto_optimal` and go straight to `suboptimal_points`), so only genuinely dominated points are removed.

## Validation

Extracted the real `is_pareto_optimal` / `add_operating_point` logic verbatim and ran:

- **Targeted:** add `A=(0.5, t=1.0)` then `B=(0.9, t=1.0)`. Before: frontier `['A', 'B']` (A dominated by B). After: `['B']`.
- **Fuzz (2000 random sequences, coarse `t` to force equal-time ties):** dominated points left in the frontier — **before: 249, after: 0**.

Added `TestOperatingPoints.test_equal_time_better_perf_shadows` to `tests/test_contrib.py` (pure-Python, no compiled core needed); it fails on `main` and passes with the fix.

Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5334

Reviewed By: trang-nm-nguyen

Differential Revision: D110116217

Pulled By: limqiying

fbshipit-source-id: 554b2bb92c22eb1c4b00896ec36fa1db46756f06
2026-06-30 14:48:34 -07:00
Tia Lim 22220d2c4f Fix genuine flake8 lint in faiss Python (F401/F403/F841/B011) (#5351)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5351

Fixes 45 genuine (non-style) flake8 issues across 20 faiss Python files, on top of the black-80 base in this stack. These are real code issues, not formatting.

- `B011` (19): replaced `assert False` / `assert False, "msg"` with `raise AssertionError(...)`. `assert` statements are stripped under `python -O`, so an `assert False` guard silently vanishes in optimized builds; `raise AssertionError` always fires.
- `F401` (10) / `F403` (7): the intentional star-import re-exports in `python/loader.py` and `python/loader_fb.py` (which expose the SWIG symbols as `faiss.X`) are annotated with `# noqa: F401,F403`; genuinely unused imports in test/bench scripts are removed.
- `F841` (9): unused local variables removed, or replaced with `_` in tuple unpacking, preserving behavior.

No runtime behavior changes; formatting stays at black width 80.

Reviewed By: mnorris11

Differential Revision: D109739445

fbshipit-source-id: 7d140d86cab9b2df691003bd043fc880a31410cd
2026-06-29 18:38:01 -07:00
Tia Lim 0f0f728166 Format faiss Python files with black (line length 80) (#5349)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5349

# this is a no-op change

Ran `black -l 80` over all tracked Python files under `fbcode/faiss/`, using line length 80 to match faiss's documented coding style (`CONTRIBUTING.md`: "80 character line length (both for C++ and Python)"). 172 files were reformatted; 28 were already compliant.

This is a pure-formatting change. `black` runs its AST-equivalence safety check on every file, so no identifiers, string/numeric values, operators, or control flow were altered — only whitespace, indentation, line wrapping, blank lines, trailing commas, and string-quote normalization (single to double quotes).

`fbcode/faiss/` is intentionally excluded from Meta's standard Python autoformatter (`arc f` / `arc lint -a` report no formatter for these paths) because it mirrors to public GitHub, so the Python had drifted out of any consistent style. The C++ is already clang-format-clean (0 of 807 files change), so this effort is Python-only. This diff brings the Python to canonical `black` formatting.

Excluded `demos/index_pq_flat_separate_codes_from_codebook.py`: it contains a Jupyter `!rm` shell-magic line and is not valid Python, so `black` cannot parse it.

This is the base of a 3-diff stack: (1) this black reformat; (2) `.flake8` alignment (max-line-length 80 + black-compatible ignores E203/W503); (3) genuine lint fixes (F401/F403/F841/B011).

Reviewed By: mnorris11

Differential Revision: D109736099

fbshipit-source-id: 9a054635766ed11aa3760a788395babd3d198abf
2026-06-29 17:29:05 -07:00
Alibek Zhakubayev cc9be4b1e4 fix: reverse_index_factory missing 11 SQ types including all TurboQuant variants (#5322)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5322

`reverse_index_factory` in `contrib/factory_tools.py` had two bugs:

1. `IndexIVFScalarQuantizer` branch hardcoded `prefix + ",SQ8"` for every quantizer type. Any IVF+SQfp16, IVF+SQ4, IVF+SQbf16, IVF+SQ8_direct, IVF+SQ8_direct_signed, or IVF+TurboQuant index returned the wrong factory string. When used to reconstruct the index via `index_factory`, the string produced a wrong type. All non-SQ8 IVF scalar-quantizer indexes were mislogged by the telemetry wrapper.

2. `IndexScalarQuantizer` branch's `sqtypes` dict held 5 entries (QT_8bit, QT_4bit, QT_6bit, QT_fp16, QT_bf16) and used `sqtypes[index.sq.qtype]` directly. Any standalone SQ index with QT_8bit_direct, QT_8bit_direct_signed, or any of the 9 TurboQuant types (QT_1bit_tqmse through QT_5bit_tq, added in b985e9c43271, 2026-06-04) raises KeyError with no error message.

We fix both by hoisting a single `sq_names` dict to the top of `reverse_index_factory`, covering all 19 `ScalarQuantizer::QuantizerType` values. Both the IVF and standalone branches look up from this shared dict.

Addressing review feedback (mnorris): the dict now also includes the three remaining enum values that were initially missed — `QT_0bit` ("SQ0", parsed by `index_factory` and intended for the IVF path), and `QT_8bit_uniform`/`QT_4bit_uniform` ("SQ8u"/"SQ4u", synthetic names since `index_factory` does not parse the uniform variants). The dict is ordered to mirror the C++ enum so it can be checked for completeness against it.

`test_ivf_scalar_quantizer_types` verifies all 19 types in the IVF context; `test_scalar_quantizer_types` verifies the 18 standalone types (`QT_0bit` is IVF-only).

Reviewed By: mnorris11

Differential Revision: D108272403

fbshipit-source-id: 3f747966495cfe7eee1cc4c328cba51b6ee9bcba
2026-06-19 02:06:14 -07:00
Alibek Zhakubayev 46ef80db63 Support IndexIDMap/IndexIDMap2 in reverse_index_factory (#5266)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5266

`reverse_index_factory` in `contrib/factory_tools.py` raised `NotImplementedError` for `IndexIDMap` and `IndexIDMap2`, even though both simply wrap an inner index. This adds handling for both: an `IndexIDMap` is described as `IDMap,<inner>` and an `IndexIDMap2` as `IDMap2,<inner>`, recursing into the wrapped index. `IndexIDMap2` is checked first because it subclasses `IndexIDMap`. This matches the C++ `reverse_index_factory` in `faiss/factory_tools.cpp`, which already handles `IDMap`, and round-trips with `index_factory` for IDMap-wrapped indexes such as `IDMap,Flat` and `IDMap,IVF100,Flat`.

Reviewed By: mnorris11

Differential Revision: D107159123

fbshipit-source-id: 40dca17a78743bf53373ec3077fd640e0fff76d4
2026-06-02 10:49:01 -07:00
Alibek Zhakubayev cb69d7c566 Fix Dq=None crash and np.empty nondeterminism in search_preassigned wrappers (#5221)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5221

## TL;DR

Two related bugs in the Python wrappers for `search_preassigned` and `range_search_preassigned`: passing `Dq=None` crashes with `ValueError`, and `np.empty` in `ivf_tools.range_search_preassigned` produces nondeterministic results for IVFPQ with `by_residual=True`. We fix both by substituting `np.zeros` when the caller omits coarse distances.

## Bug 1: Dq=None crashes (class_wrappers.py, 4 sites)

The Python wrappers for `search_preassigned` and `range_search_preassigned` on both `Index` and `IndexBinaryIVF` document `Dq` as optional, but passing `None` crashes:

```
ValueError: argument is not a numpy array
```

The cause: each wrapper validates `Dq` inside `if Dq is not None:` but has no `else` branch. When `Dq` is `None`, `swig_ptr(Dq)` is called with `None`, which SWIG rejects.

## Bug 2: np.empty nondeterminism (ivf_tools.py)

`ivf_tools.range_search_preassigned` allocates a dummy `coarse_dis` with `np.empty` when the caller does not provide one. `np.empty` returns uninitialized memory. For `IndexIVFPQ` with L2 metric and `by_residual=True` (the default), the C++ search kernel reads `coarse_dis` for distance correction. Uninitialized values produce nondeterministic search results with no error or warning.

The sibling function `ivf_tools.search_preassigned` already uses `np.zeros` for the same purpose. The `range_search_preassigned` variant diverged.

## Fix

**class_wrappers.py (4 sites):** Add `else: Dq = np.zeros(Iq.shape, dtype=DTYPE)` after each `if Dq is not None:` block. Float wrappers use `dtype='float32'`, binary wrappers use `dtype='int32'`. `Iq` is already coerced and shape-validated at each site, so the shape is correct.

**ivf_tools.py (1 site):** Change `np.empty` to `np.zeros` at line 75. Restores parity with `search_preassigned` at line 53.

## Why zeros

`Dq` represents the distances from queries to their assigned IVF centroids. Zeros means "no distance correction." For most index types (`IVF,Flat`, `IVF,SQ8`, `IndexBinaryIVF`), the C++ code ignores `Dq` entirely in scoring. For `IVFPQ` with `by_residual=True`, zeros omit the coarse component from the returned distances but preserve the neighbor ranking (because all vectors in the same inverted list share the same coarse distance, so it cancels in relative comparisons). Users who need accurate absolute distances should compute and pass real `Dq` via `index.quantizer.search()`.

Zeros is deterministic, semantically neutral, and consistent with the existing convention in `ivf_tools.search_preassigned`.

Reviewed By: junjieqi

Differential Revision: D105299681

fbshipit-source-id: 9f59a7bd839b253ee31d42c1ed013234a7adfd87
2026-05-18 16:49:50 -07:00
Trang Nguyen 130fc2481a Fix flaky test (#5150)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5150

https://www.internalfb.com/intern/test/844425196588481
We are receving alert due to unhealthy test. Claude is suggesting me to reduce the test size, I think it is worth a try.

Reviewed By: mnorris11

Differential Revision: D102641694

fbshipit-source-id: 05c38a01e646e0f35d205e63c4c489eee8b6d986
2026-04-27 15:15:28 -07:00
generatedunixname2012458493028417 ed11f283d6 Fix broken fbcode//faiss/tests:test_contrib - test_checkpoint (test_contrib.TestBigBatchSearch) (#5139)
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5139

Reviewed By: junjieqi

Differential Revision: D101979704

fbshipit-source-id: b3b1575fd3431dca7d3b5e7ec86e4009810095fb
2026-04-22 16:35:46 -07:00
Matthijs Douze c4ac852e5b Add a balanced assignment function (#5050)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5050

- Implement balanced assignment in clustering.py based on notebook N10159950
- add a test that shows we improve the imabalace at some cost in MSE

Reviewed By: algoriddle

Differential Revision: D99819394

fbshipit-source-id: 568b6deb7d2b95b8228dbb276c5578df23b01a96
2026-04-08 03:18:41 -07:00
Michael Norris 4fab13c9c6 Upgrade Faiss OSS side to numpy2 (#4523)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4523

This upgrades OSS Faiss to numpy 2.3.3. I tried to fix issues based on https://numpy.org/devdocs/numpy_2_0_migration_guide.html.

Overall, numpy2 causes some test issues on various platforms/optimization levels.

Test changes that are OK:
- test_contrib.py: test_float: changing to use item(): this is just the new way of getting the value, like recommended in the migration guide.

Test changes that indicate some problem:
- test_contrib.py: test_ivf_train_2level: This fails on many platforms (avx2, conda installs). I could not find what the issue was. I had to increase the bounds slightly.
- test_index_accuracy.py: test_OIVFPQ: This fails just on AVX512. I had to increase the iterations from 10 to 12.
- test_local_search_quantizer.py and test_standalone_codec.py: Some of these break on Windows. Others needed lighter tolerances.

Debugging attempts: Especially for test_ivf_train_2level, I tried to unsuccessfully track down what might have changed. I suspected the type promotion change from numpy1 to 2.
```
- np.float32(3) + 3. now returns a float32 when it previously returned a float64.
- np.array([3], dtype=np.float32) + np.float64(3) will now return a float64 array. (The higher precision of the scalar is not ignored.)
```
I changed all datasets to be explicitly float64s, and checked for any constants in the code path. No luck.

Devmate was less than useless, it was actively a waste of time (as usual...)

Reviewed By: subhadeepkaran

Differential Revision: D79900113

fbshipit-source-id: 541cfa6f1fa1d60c84d855ce71f0e1de7ea7c7f1
2025-10-02 09:49:05 -07:00
Tia Lim 5c61ed8c94 Fix E302 lint errors: Add required blank lines before class definitions (#4541)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4541

Per PEP 8 style guide (https://pep8.org/#blank-lines), top-level class
definitions should be surrounded by two blank lines. This commit addresses
\E302 lint violations across the faiss codebase by ensuring all class
definitions have exactly 2 blank lines before them.

Changes:
- Fixed \E302 errors in 28 Python files across tests, benchmarks, contrib,
  and GPU modules
- Applied consistent PEP 8 formatting for class definitions
- No functional changes - formatting only

Files affected include test files, benchmark scripts, contrib modules,
and GPU test utilities. All changes maintain existing functionality
while improving code style compliance.

Reviewed By: junjieqi

Differential Revision: D80682723

fbshipit-source-id: 6ad5458c3bf1e5c06f35dc8fcdbea2ac4ba83c5f
2025-08-21 15:40:14 -07:00
Michael Norris 32beb162f2 Migration off defaults to conda-forge channel (#4126)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4126

Good resource on overriding channels to make sure we aren't using `defaults`:https://stackoverflow.com/questions/67695893/how-do-i-completely-purge-and-disable-the-default-channel-in-anaconda-and-switch

Explanation of changes:
-
- changed to miniforge from miniconda: this ensures we only pull in from conda-defaults when creating the environment
- architecture: ARM64 and aarch64 are the same thing. But there is no miniforge package for ARM64, so we need to make it check for aarch64 instead. However, mac breaks this rule, and does have macOS-arm64! So there is a conditional for mac to use arm64. https://github.com/conda-forge/miniforge/releases/
- action.yml mkl 2022.2.1 change: conda-forge and defaults have completely different dependencies. Defaults required intel-openmp, but now on conda-forge, mkl 2023.1 or higher requires llvm-openmp >=14.0.6, but this is incompatible with the pytorch build <2.5 which requires llvm-openmp<14.0. We would need to upgrade Python to 3.12 first, upgrade Pytorch build, then upgrade this mkl. (The meta.yaml changes are the ones that narrow it to 2022.2.1 during `conda build faiss`.) So, this has just been changed to 2022.2.1.
- mkl now requires _openmp_mutex of type "llvm" instead of "gnu": prior non-cuVS builds all used gnu, because intel-openmp from anaconda defaults channel does not require llvm-openmp. Now we need to remove the gnu one which is automatically pulled in during miniconda setup, and only keep the llvm version of _openmp_mutex.
- liblief: The above changes tried to pull in liblief 0.15. This results in an error like `AttributeError: module 'lief._lief.ELF' has no attribute 'ELF_CLASS'`. When I checked passing PR builds on defaults, they use lief 0.12, so I pinned that one for Python 3.9 3.10 3.11. For Python 3.12, we need lief 0.14 or higher.
- gcc_linux-64 =11.2 for faiss-gpu on cudatoolkit-11.2: GPU builds kept trying to reference 11.2 when 14.2 was installed. I couldn't figure out why, or how to point it to the 14.2 installed on the host. Current nightly builds still reference 11.2, so I gave up and pinned 11.2 to keep it the same. Moving to 14.2 will take some more investigation.
- meta.yaml mkl 2023.0 vs 2023.1 with python versions: 3.9, 3.10, and 3.11 pass with 2023.0, but python 3.12 needs mkl 2023.1 or higher. Otherwise we get:
```
INTEL MKL ERROR: $PREFIX/lib/python3.12/site-packages/faiss/../../.././libmkl_def.so.2: undefined symbol: mkl_sparse_optimize_bsr_trsm_i8.
Intel MKL FATAL ERROR: Cannot load libmkl_def.so.2.
```
so the solution was to put a bunch of conditions in in faiss/meta.yaml.
We should be able to use Jinja macros to reduce duplication but it requires some investigation. It was failing: https://github.com/facebookresearch/faiss/actions/runs/12915187334/job/36016477707?pr=4126  (paste of logs here: P1716887936). This can be a future BE task.
Macro example (the `-` signs remove whitespace lines before and after)
```
{% macro inclmkldevel() %}
{%- if PY_VER == '3.9' or PY_VER == '3.10' or PY_VER == '3.11' -%}
        - mkl-devel =2023.0  # [x86_64]
        - liblief =0.12.3  # [not win]
        - python_abi <3.12
{%- elif PY_VER == '3.12' %}
        - mkl-devel >=2023.2.0  # [x86_64]
        - liblief =0.15.1  # [not win]
        - python_abi =3.12
{% endif -%}
{% endmacro %}
```
The python_abi was required to be pinned inside these conditions because otherwise several builds got this error:
```
File "/Users/runner/miniconda3/lib/python3.12/site-packages/conda_build/utils.py", line 1919, in insert_variant_versions
        matches = [regex.match(pkg) for pkg in reqs]
                   ^^^^^^^^^^^^^^^^
    TypeError: expected string or bytes-like object, got 'list'
```

Unit test notes:
-
- test_gpu_basics.py: GPU residual quantizer: Debugged extensively with Matthijs. The problem is in the C++ -> Python conversion. The C++ side prints the right values, but when getting it back to Python, it is filled with junk data. It is only reproducible on CUDA 11.4.4 after switching channels. It is likely a compiler problem. We discussed, and resolved to create a C++ side unit test (so this diff creates TestGpuResidualQuantizer) to verify the functionality and disable the Python unit test, but leave it in the codebase with a comment. Matthijs made extensive notes in https://docs.google.com/document/d/1MjMdOpPgx-MArdrYJZCaQlRqlrhSj5Y1Z9lTyiab8jc/edit?usp=sharing .
- test_contrib.py: this now hangs forever and times out the runner for Windows on Python 3.12. I have it skipping now.
- test_mem_leak.cpp seems flaky. It sometimes fails, then passes with rerun.

Unfixed issues:
-
- I noticed sometimes downloads will fail with the text like below. It passes on re-run.
```
libgomp-14.2.0-h77fa898_1.conda extraction failed
  Warning: error    libmamba Error when extracting package: Could not chdir info/recipe/parent/patches/0005-Hardcode-HAVE_ALIGNED_ALLOC-1-in-libstdc-v3-configur.patch

  error    libmamba Error when extracting package: Could not chdir info/recipe/parent/patches/0005-Hardcode-HAVE_ALIGNED_ALLOC-1-in-libstdc-v3-configur.patch
  Warning: Found incorrect download: libgomp. Aborting

  Found incorrect download: libgomp. Aborting
  Warning:
```

Green build and tests for both build pull request and nightlies: https://github.com/facebookresearch/faiss/actions/runs/12956402963/job/36148818361

Reviewed By: asadoughi

Differential Revision: D68043874

fbshipit-source-id: b105a1e3e6272763ad9daab7fc6f05a79f01c9e2
2025-01-27 14:49:18 -08:00
Junjie Qi 3f3d18d2e7 add test to cover GPU (#4130)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4130

same as title

Reviewed By: asadoughi

Differential Revision: D68388863

fbshipit-source-id: 4ebb38d8454bf95733c918950d7d8d3b22e00d5d
2025-01-21 11:45:07 -08:00
Di-Is 905963f344 Add ngpu default argument to knn_ground_truth (#4123)
Summary:
This pull request introduces a new default argument, `ngpu=-1`, to the `knn_ground_truth` function in the `faiss.contrib`.

## Purpose of Change

### Bug Fix

In the current implementation, running tests under the tests directory (CPU tests) in an environment with faiss-gpu installed would inadvertently use the GPU and cause unintended behavior.
This pull request prevents the GPU from being used during CPU-only tests by explicitly controlling GPU allocation via the ngpu parameter.

### API Consistency

Other functions that call `faiss.get_num_gpus` in `faiss.contrib`, such as `range_search_max_results` and `range_ground_truth`, already include the `ngpu` argument.
Adding this parameter to `knn_ground_truth` will ensure consistency across the API, reduce potential confusion, and improve ease of use.

Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4123

Reviewed By: asadoughi

Differential Revision: D68199506

Pulled By: junjieqi

fbshipit-source-id: cb50e206d8a1a982c21b0ccb42825ea45873f3ef
2025-01-21 11:09:02 -08:00
Mulugeta Mammo 3beb07b198 Add a new architecture mode: 'avx512_spr'. (#4025)
Summary:
This PR adds a new architecture mode to support the new extensions to AVX512, namely [AVX512-FP16](https://networkbuilders.intel.com/solutionslibrary/intel-avx-512-fp16-instruction-set-for-intel-xeon-processor-based-products-technology-guide), which have been available since Intel® Sapphire Rapids.

This PR is a prerequisite for [PR#4020](https://github.com/facebookresearch/faiss/pull/4020) that speeds up hamming distance evaluations.

Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4025

Reviewed By: pankajsingh88

Differential Revision: D67524575

Pulled By: mengdilin

fbshipit-source-id: f3a09943b062d720b241f95aef2f390923ffd779
2024-12-23 08:56:26 -08:00
Junjie Qi 3c25a68e52 fix linter (#4035)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4035

same as title

Reviewed By: bshethmeta

Differential Revision: D66154375

fbshipit-source-id: c5d05aac2d8e502058f0403b0ec0ea243afa18e2
2024-11-19 12:09:40 -08:00
Michael Norris eff0898a13 Enable linting: lint config changes plus arc lint command (#3966)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3966

This actually enables the linting.

Manual changes:
- tools/arcanist/lint/fbsource-licenselint-config.toml
- tools/arcanist/lint/fbsource-lint-engine.toml

Automated changes:
`arc lint --apply-patches --take LICENSELINT --paths-cmd 'hg files faiss'`

Reviewed By: asadoughi

Differential Revision: D64484165

fbshipit-source-id: 4f2f6e953c94ef6ebfea8a5ae035ccfbea65ed04
2024-10-22 09:46:48 -07:00
Matthijs Douze 6baebe2cee begin torch_contrib (#3872)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3872

The contrib.torch subdirectory is intended to receive modules in python that are useful for similarity search and that apply to CPU or GPU pytorch tensors.

The current version includes CPU clustering on torch tensors. To be added:
* implementation of PQ

Reviewed By: asadoughi

Differential Revision: D62759207

fbshipit-source-id: 87dbaa5083e3f2f4f60526815e22ded4e83e8559
2024-09-20 09:15:27 -07:00
Matthijs Douze 0d7817e88f rewrite python kmeans without scipy (#3873)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3873

The previous version required scipy to do the accumulation, which is replaced here with a nifty piece of numpy accumulation.
This removes the need for scipy for non-sparse data.

Reviewed By: junjieqi

Differential Revision: D62884307

fbshipit-source-id: 5443634e487387a2b518fd2a7f9a3d9a40abd4b4
2024-09-20 09:15:27 -07:00
Xiao Fu bf8bd6b689 Delete all remaining print (#3452)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3452

Delete all remaining print within the Tests to improve the readability and effectiveness of the codebase.

Reviewed By: junjieqi

Differential Revision: D57466393

fbshipit-source-id: 6ebd66ae2e769894d810d4ba7a5f69fc865b797d
2024-05-16 19:51:07 -07:00
Kumar Saurabh Arora da9f292a4b Support of skip_ids in merge_from_multiple function of OnDiskInvertedLists (#3327)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3327

**Context**
1. [Issue 2621](https://github.com/facebookresearch/faiss/issues/2621) discuss inconsistency between OnDiskInvertedList and InvertedList. OnDiskInvertedList is supposed to handle disk based multiple Index Shards. Thus, we should name it differently when merging invls from index shard.
2. [Issue 2876](https://github.com/facebookresearch/faiss/issues/2876) provides usecase of shifting ids when merging invls from different shards.

**In this diff**,
1. To address #1 above, I renamed the merge_from function to merge_from_multiple without touching merge_from base class.
why so? To continue to allow merge invl from one index to ondiskinvl from other index.

2. To address #2 above, I have added support of shift_ids in merge_from_multiple to shift ids from different shards. This can be used when each shard has same set of ids but different data. This is not recommended if id is already unique across shards.

Reviewed By: mdouze

Differential Revision: D55482518

fbshipit-source-id: 95470c7449160488d2b45b024d134cbc037a2083
2024-04-03 10:36:56 -07:00
Maria Lomeli 420d25f51c Index pretransform support in search_preassigned (#3225)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/3225

This diff fixes issue [#3113](https://github.com/facebookresearch/faiss/issues/3113), e.g. introduces support for index pretransform in `search_preassigned`.

Reviewed By: mdouze

Differential Revision: D53188584

fbshipit-source-id: 8189c0a59f957a2606391f22cf3fdc8874110a6e
2024-01-30 09:20:07 -08:00
Gergely Szilvasy 2768fb38b2 faiss-gpu-raft package (#2992)
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2992

Reviewed By: mdouze

Differential Revision: D48391366

Pulled By: algoriddle

fbshipit-source-id: 94b7f62afc8a09a9feaea47bf60e5358d89fcde5
2023-08-16 09:30:41 -07:00
Matthijs Douze 687457b2f4 Access graph structure for NSG (#2984)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2984

It is not entirely trivial to access the NSG graph structure from Python (although it is a fixed size N-by-K matrix of vector ids).
This diff adds an inspect_tools function to do that.

Reviewed By: algoriddle

Differential Revision: D48026775

fbshipit-source-id: 94cd7be7f656bcd333d62586531f287ea8e052e5
2023-08-04 06:55:24 -07:00
Gergely Szilvasy 821a401ae9 CodeSet for deduping large datasets (#2949)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2949

A more scalable alternative to `np.unique` for deduping large datasets with a quantized code.

Reviewed By: mlomeli1

Differential Revision: D47443953

fbshipit-source-id: 4a1554d4d4200b5fa657e9d8b7395bba9856a8e3
2023-07-19 10:05:46 -07:00
Gergely Szilvasy 391601dc3f relax test_ivf_train_2level threshold (#2927)
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2927

Reviewed By: mlomeli1

Differential Revision: D47017009

fbshipit-source-id: cfa1df4b9632b085d3a61b56d8617bebd7e5aad6
2023-06-26 05:02:47 -07:00
Matthijs Douze 07fe2b622f Binary cloning and GPU range search (#2916)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2916

Overall better support for binary indexes:
- cloning (to CPU and GPU), only for BinaryFlat for now
- fix bug in reconstruct_n
- range_search_max_results

Reviewed By: algoriddle

Differential Revision: D46755778

fbshipit-source-id: 777ad90aff5c54a77f9685ed6512247a922c6ef5
2023-06-19 06:05:14 -07:00
Gergely Szilvasy 092606b293 bbs producer/consumer threading (#2901)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2901

This diff allows each GPU to work independently, a hot centroid (eg. out-of-distribution queries that hit a centroid heavily) will only block the one GPU that is processing it, others will continue to pick up work independently.

Reviewed By: mdouze

Differential Revision: D46521298

fbshipit-source-id: 171cb06cce8b2d16b7bd744799b105b3cd525be3
2023-06-14 07:58:44 -07:00
Matthijs Douze 6800ebef83 Support independent IVF coarse quantizer
Summary: In the IndexIVFIndepenentQuantizer, the coarse quantizer is applied on the input vectors, but the encoding is performed on a vector-transformed version of the database elements.

Reviewed By: alexanderguzhva

Differential Revision: D45950970

fbshipit-source-id: 30f6cf46d44174b1d99a12384b7d5e2d475c1f88
2023-05-26 02:59:01 -07:00
Matthijs Douze b9ea339617 support range search from GPU (#2860)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2860

Optimized range search function where the GPU computes by default and falls back on gpu for queries where there are too many results.

Parallelize the CPU to GPU cloning, it seems to work.

Support range_search_preassigned in Python

Fix long-standing issue with SWIG exposed functions that did not release the GIL (in particular the MapLong2Long).

Adds a MapInt64ToInt64 that is more efficient than MapLong2Long.

Reviewed By: algoriddle

Differential Revision: D45672301

fbshipit-source-id: 2e77397c40083818584dbafa5427149359a2abfd
2023-05-16 00:27:53 -07:00
Matthijs Douze 2d8886cd4f IVF sorting routine (#2846)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2846

Adds a function to ivf_contrib to sort the inverted lists by size without changing the results. Also moves big_batch_search to its own module.

Reviewed By: algoriddle

Differential Revision: D45565880

fbshipit-source-id: 091a1c1c074f860d6953bf20d04523292fb55e1a
2023-05-04 09:59:06 -07:00
Matthijs Douze 016aa04602 make balanced clusters the default (#2796)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2796

This diff makes balanced clusters the default for 2-level clustering. This seems to improve a bit over the default uniform clusters, see

https://github.com/fairinternal/faiss_improvements/blob/master/better_coarse_quantizer/two_level_clustering.ipynb

Warning: the nc2 argument of two_level_clustering becomes the *total* number of clusters.

Reviewed By: algoriddle

Differential Revision: D44421222

fbshipit-source-id: 951b7fc043be4a41762a7e6f7a6fcfb71e303832
2023-03-28 07:23:30 -07:00
Matthijs Douze 0200d131fc fix windows test (#2775)
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2775

Reviewed By: algoriddle

Differential Revision: D44210010

fbshipit-source-id: b9b620a4b0a874e09ee2f6082ff0f9463716fdf4
2023-03-21 05:34:50 -07:00
Matthijs Douze 2d7dd5b0a6 support checkpointing in big batch search
Summary: Big batch search can be running for hours so it's useful to have a checkpointing mechanism in case it's run on a best-effort cluster queue.

Reviewed By: algoriddle

Differential Revision: D44059758

fbshipit-source-id: 5cb5e80800c6d2bf76d9f6cb40736009cd5d4b8e
2023-03-14 11:11:50 -07:00
Matthijs Douze fa53e2c941 Implementation of big-batch IVF search (single machine) (#2567)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2567

Intuitively, it should be easier to handle big-batch searches because all distance computations for a set of queries can be done locally within each inverted list.

This benchmark implements this in pure python (but should be close to optimal in terms of speed), on CPU for IndexIVFFlat, IndexIVFPQ and IndexIVFScalarQuantizer. GPU is also supported.

The results are not systematically better, see https://docs.google.com/document/d/1d3YuV8uN7hut6aOATCOMx8Ut-QEl_oRnJdPgDBRF1QA/edit?usp=sharing

Reviewed By: algoriddle

Differential Revision: D41098338

fbshipit-source-id: 479e471b0d541f242d420f581775d57b708a61b8
2022-12-09 08:53:13 -08:00
Matthijs Douze f68ddd0564 fix test in test_contrib (#2294)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2294

there is a weird CI failure on one of the platforms occurring in the PR

https://github.com/facebookresearch/faiss/pull/2291

This diff makes the test a bit more robust, correcting inter_perf to computer the intersection measure. Hopefully this will make the bug go away.

Reviewed By: beauby

Differential Revision: D35558855

fbshipit-source-id: f5a926d9d8ebee975e538c65ac37b15d485798aa
2022-04-20 03:03:38 -07:00
Matthijs Douze b8fe92dfee contrib clustering module (#2217)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2217

This diff introduces a new Faiss contrib module that contains:
- generic k-means implemented in python (was in distributed_ondisk)
- the two-level clustering code, including a simple function that runs it on a Faiss IVF index.
- sparse clustering code (new)

The main idea is that that code is often re-used so better have it in contrib.

Reviewed By: beauby

Differential Revision: D34170932

fbshipit-source-id: cc297cc56d241b5ef421500ed410d8e2be0f1b77
2022-02-28 14:18:47 -08:00
Matthijs Douze eb8781557f Fix exhaustive search GT computation with IP distance (#2212)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2212

Fixes issue

https://github.com/facebookresearch/faiss/issues/2205

clear bug report
easy fix
easy to accept ;-)

Reviewed By: beauby

Differential Revision: D33975281

fbshipit-source-id: 088e1f3078dc79402563be7fac3530d76b197006
2022-02-07 19:36:21 -08:00
Matthijs Douze c0052c1533 IndexFlatCodes: a single parent for all flat codecs (#2132)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/2132

This diff adds the class IndexFlatCodes that becomes the parent of all "flat" encodings.
IndexPQ
IndexFlat
IndexAdditiveQuantizer
IndexScalarQuantizer
IndexLSH
Index2Layer

The other changes are:
- for IndexFlat, there is no vector<float> with the data anymore. It is replaced with a `get_xb()` function. This broke quite a few external codes, that this diff also attempts to fix.
- I/O functions needed to be adapted. This is done without changing the I/O format for any index.
- added a small contrib function to get the data from the IndexFlat
- the functionality has been made uniform, for example remove_ids and add are now in the parent class.

Eventually, we may support generic storage for flat indexes, similar to `InvertedLists`, eg to memmap the data, but this will again require a big change.

Reviewed By: wickedfoo

Differential Revision: D32646769

fbshipit-source-id: 04a1659173fd51b130ae45d345176b72183cae40
2021-12-07 01:31:07 -08:00
Matthijs Douze 1829aa92a1 three small fixes (#1972)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/1972

This fixes a few issues that I ran into + adds tests:

- range_search_max_results with IP search

- a few missing downcasts for VectorTRansforms

- ResultHeap supports max IP search

Reviewed By: wickedfoo

Differential Revision: D29525093

fbshipit-source-id: d4ff0aff1d83af9717ff1aaa2fe3cda7b53019a3
2021-07-01 16:08:45 -07:00
Matthijs Douze 2d380e992b Add manifold check for size 0 (#1867)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/1867

Merging code for the 1T photodna index seems to fail at

https://www.internalfb.com/phabricator/paste/view/P412975011?lines=174

with
```
terminate called after throwing an instance of 'facebook::manifold::blobstore::StorageException'
  what():  [400] Begin offset and/or length were invalid -- Begin offset must be positive and length must be non-negative. Received: offset = 2642410612, length = 0
Aborted (core dumped)
```
traces back to

https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/fbcode/manifold/blobstore/BlobstoreThriftHandler.cpp?lines=671%2C700%2C732

There is a single case where we don't check if the read or write size is 0. So let's try this fix.

In the process I realized that the Manifold tests were non functional due to a name collision on common.py. Also fix this in all dependent files.

Differential Revision: D28231710

fbshipit-source-id: 700ffa6ca0c82c49e7d1eae9e76549ec5ff16332
2021-05-09 22:30:31 -07:00
Matthijs Douze 3f2ebf4b1c Add preassigned functions to contrib
Summary:
Adds the preassigned add and search python wrappers to contrib.
Adds the preassigned search for the binary case (was missing before).
Also adds a real test for that functionality.

Reviewed By: beauby

Differential Revision: D26560021

fbshipit-source-id: 330b715a9ed0073cfdadbfbcb1c23b10bed963a5
2021-02-25 11:39:07 -08:00
Matthijs Douze 5602724979 make calling conventions uniform between faiss.knn and faiss.knn_gpu
Summary: The order of xb an xq was different between `faiss.knn` and `faiss.knn_gpu`. Also the metric argument was called distance_type. This diff fixes both. Hopefully not too much external code depends on it.

Reviewed By: wickedfoo

Differential Revision: D26222853

fbshipit-source-id: b43e143d64d9ecbbdf541734895c13847cf2696c
2021-02-03 12:21:40 -08:00
Matthijs Douze 3dd7ba8ff9 Add range search accuracy evaluation
Summary:
Added a few functions in contrib to:
- run range searches by batches on the query or the database side
- emulate range search on GPU: search on GPU with k=1024, if the farthest neighbor is still within range, re-perform search on CPU
- as reference implementations for precision-recall on range search datasets
- optimized code to plot precision-recall plots (ie. sweep over thresholds)

The new functions are mainly in a new `evaluation.py`

Reviewed By: wickedfoo

Differential Revision: D25627619

fbshipit-source-id: 58f90654c32c925557d7bbf8083efbb710712e03
2020-12-17 17:17:09 -08:00