# Faiss User Guide

Faiss is a library for **efficient similarity search and clustering of dense vectors**. You give it a set of vectors (embeddings, image descriptors, audio fingerprints — anything you can represent as a fixed-length array of `float32` numbers), and it finds, for any query vector, the most similar vectors in the set. It scales from a few thousand vectors held exactly in memory up to billions of vectors compressed onto a single server, and the most performance-critical algorithms run on the GPU.

Faiss is written in **C++** with complete, first-class **Python/NumPy** bindings. This guide focuses on the Python interface, since that is how most people use Faiss, with notes on the C++ equivalents where they matter. It is developed by Meta's Fundamental AI Research (FAIR) group and is released under the MIT license.

> [!NOTE]
> This guide documents Faiss **v1.14.2**. The version is baked into the library — you can confirm it at runtime with `faiss.__version__` (Python) or the `FAISS_VERSION_MAJOR/MINOR/PATCH` macros (C++).

---

## Table of Contents

1. [Quick Start](#1-quick-start)
2. [Installation](#2-installation)
3. [Core Concepts](#3-core-concepts)
4. [Building and Searching an Index](#4-building-and-searching-an-index)
5. [Choosing an Index Type](#5-choosing-an-index-type)
6. [The Index Factory](#6-the-index-factory)
7. [Distance Metrics](#7-distance-metrics)
8. [Saving and Loading Indexes](#8-saving-and-loading-indexes)
9. [Using IDs](#9-using-ids)
10. [Running on the GPU](#10-running-on-the-gpu)
11. [Clustering with K-means](#11-clustering-with-k-means)
12. [Common Tasks (Recipes)](#12-common-tasks-recipes)
13. [Troubleshooting](#13-troubleshooting)
14. [FAQ](#14-faq)
15. [Reference](#15-reference)

---

## 1. Quick Start

The complete "hello world" for Faiss: build an exact index, add 100,000 vectors, and find the 4 nearest neighbors of each query.

```python
import numpy as np
import faiss                     # make faiss available

d = 64                           # vector dimension
nb = 100000                      # database size
nq = 10000                       # number of queries

np.random.seed(1234)             # make reproducible
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

index = faiss.IndexFlatL2(d)     # build the index
print(index.is_trained)          # True — a flat index needs no training
index.add(xb)                    # add vectors to the index
print(index.ntotal)              # 100000

k = 4                            # we want the 4 nearest neighbors
D, I = index.search(xq, k)       # search
print(I[:5])                     # IDs of the neighbors of the first 5 queries
print(D[:5])                     # squared L2 distances to those neighbors
```

```text
True
100000
[[ 381  207  210  477]
 [ 526  911  142   72]
 [ 838  527 1290  425]
 ...]
```

That is the whole shape of the API: create an index, `add()` your database vectors, then `search()` your query vectors. `search()` returns two arrays:

- `I` — the integer **labels** (IDs) of the nearest neighbors, shape `(nq, k)`.
- `D` — the corresponding **distances**, shape `(nq, k)`.

> [!TIP]
> All vectors passed to Faiss must be a 2-D `float32` NumPy array in row-major (C-contiguous) order, shaped `(n_vectors, d)`. If your array is `float64` or non-contiguous, convert it first with `np.ascontiguousarray(x, dtype='float32')`. Passing the wrong dtype is the single most common beginner error — see [Troubleshooting](#13-troubleshooting).

---

## 2. Installation

The supported installation path is **conda**, but `pip` wheels are also published as of v1.14.2. Choose CPU-only or one of the GPU builds.

### Conda (recommended)

=== "CPU only"

    ```bash
    conda install -c pytorch -c conda-forge faiss-cpu=1.14.2
    ```

=== "GPU (CUDA)"

    ```bash
    conda install -c pytorch -c nvidia -c conda-forge faiss-gpu=1.14.2
    ```

=== "GPU (NVIDIA cuVS)"

    ```bash
    conda install -c pytorch -c nvidia -c rapidsai -c conda-forge libnvjitlink faiss-gpu-cuvs=1.14.2
    ```

The `faiss-gpu` package contains **both CPU and GPU** indices, so you do not need `faiss-cpu` as well.

> [!IMPORTANT]
> The `conda-forge` channel is required for up-to-date BLAS dependencies (Intel MKL on x86-64, OpenBLAS on ARM). For `faiss-gpu`, the `nvidia` channel is additionally required for the CUDA runtime. For `faiss-gpu-cuvs`, you also need the `rapidsai` channel.

**Platform availability:**

| Package | Platforms | GPU support |
|---|---|---|
| `faiss-cpu` | Linux (x86-64, aarch64), macOS (arm64), Windows (x86-64) | none |
| `faiss-gpu` | Linux (x86-64) | CUDA 11.4 and 12.1 |
| `faiss-gpu-cuvs` | Linux (x86-64) | CUDA 13.2 + NVIDIA cuVS 26.06 |

Nightly pre-release builds are available from the `pytorch/label/nightly` channel.

### Pip

Pip wheels were added in v1.14.2 (built with scikit-build-core + cibuildwheel):

```bash
pip install faiss-cpu
```

> [!WARNING]
> The `faiss-gpu` pip wheel expects the matching NVIDIA CUDA runtime wheels to be installed alongside it. If they are missing, importing Faiss raises a clear error such as `faiss-gpu installed but nvidia-cuda-runtime-cu12 is missing`. Install the runtime wheels it names (`nvidia-cuda-runtime-cu12`, `nvidia-cublas-cu12`, `nvidia-curand-cu12`) in the pinned version ranges. See [Troubleshooting](#13-troubleshooting).

### Pixi

If you use [Pixi](https://pixi.sh), the same conda channels and package names apply:

```bash
pixi init -c pytorch -c conda-forge
pixi add faiss-cpu=1.14.2
```

### Verify the installation

```python
import faiss
print(faiss.__version__)
index = faiss.IndexFlatL2(8)
print("OK", index.d)
```

```text
1.14.2
OK 8
```

Building from source (CMake) is covered in the **Admin Guide**; most users should install a prebuilt package.

---

## 3. Core Concepts

Faiss is built around one central object: the **index**. An index stores a set of vectors and answers nearest-neighbor queries against them. Different index types make different trade-offs between:

- **search speed**
- **search quality** (recall — how often the true nearest neighbor is found)
- **memory used per vector**
- **training time** and **add time**

Understanding four ideas is enough to use Faiss well:

**Vectors and labels.** Every vector you add gets an integer **label** (an `int64` ID). By default vectors are labelled sequentially: the first vector added is `0`, the next `1`, and so on. Search returns these labels. You can assign your own labels — see [Using IDs](#9-using-ids).

**Training.** Some index types must first *learn* the distribution of your data before they can store vectors efficiently (for example, to compute cluster centroids or a quantization codebook). These indexes require a one-time `train()` call on a representative sample before you `add()`. You can check whether an index needs training with the `is_trained` attribute.

> [!NOTE]
> `IndexFlat*` (exact, brute-force) indexes never need training — `is_trained` is `True` from the start. Indexes whose names contain `IVF`, `PQ`, `LSH`, `SQ`, etc. generally **do** need training.

**Exact vs. approximate.** A *flat* index compares the query against every stored vector — exact results, but slow and memory-heavy at scale. *Approximate* indexes (IVF, HNSW, PQ, ...) trade a small amount of recall for orders-of-magnitude speed and/or memory savings.

**Compression.** Some indexes store the original vectors verbatim; others store only a compact, quantized code (e.g. Product Quantization). Compressed indexes can hold billions of vectors in RAM but return approximate distances.

---

## 4. Building and Searching an Index

Every index follows the same lifecycle. Not all steps apply to every type.

```python
index = faiss.IndexIVFFlat(quantizer, d, nlist)  # 1. construct
index.train(xb)                                   # 2. train (if is_trained is False)
index.add(xb)                                     # 3. add database vectors
D, I = index.search(xq, k)                        # 4. search
```

### The core operations

| Operation | Python | What it does |
|---|---|---|
| Train | `index.train(x)` | Learn data distribution. No-op for flat indexes. |
| Add | `index.add(x)` | Store vectors with sequential labels. |
| Add with IDs | `index.add_with_ids(x, ids)` | Store vectors with your own `int64` labels. |
| Search | `D, I = index.search(xq, k)` | Find the `k` nearest neighbors of each query. |
| Range search | `lims, D, I = index.range_search(xq, radius)` | Find all neighbors within `radius`. |
| Reconstruct | `index.reconstruct(label)` | Return the stored (or decoded) vector for a label. |
| Remove | `index.remove_ids(selector)` | Remove vectors by ID. Not all types support this. |
| Reset | `index.reset()` | Empty the index. |

### Inspecting an index

```python
print(index.d)           # vector dimension
print(index.ntotal)      # number of vectors currently stored
print(index.is_trained)  # whether the index is ready to add/search
print(index.metric_type) # faiss.METRIC_L2 or faiss.METRIC_INNER_PRODUCT, ...
```

### Range search

When you want "everything closer than a threshold" rather than a fixed `k`, use `range_search`. Because each query can return a different number of results, the output uses a flat layout with a `lims` offset array:

```python
radius = 1.5
lims, D, I = index.range_search(xq, radius)
# neighbors of query i are I[lims[i] : lims[i+1]]
for i in range(5):
    print(f"query {i}: {I[lims[i]:lims[i+1]]}")
```

> [!NOTE]
> Range search is **mandatory only for k-NN**; many index types implement `search` but not `range_search`. If a type does not support it, the call raises a runtime error rather than returning wrong results.

---

## 5. Choosing an Index Type

The right index depends on your dataset size and your tolerance for approximate results. Below are the most commonly used types, all available directly as Python classes.

### IndexFlat — exact search

The baseline. Stores vectors uncompressed and compares the query against all of them. Always 100% recall.

```python
index = faiss.IndexFlatL2(d)   # exact, squared-L2 distance
index = faiss.IndexFlatIP(d)   # exact, inner-product (dot-product) distance
index.add(xb)                  # no training needed
D, I = index.search(xq, k)
```

> [!TIP]
> Use `IndexFlatL2` / `IndexFlatIP` whenever your dataset fits in memory and you can afford a linear scan (roughly up to a few hundred thousand to a million vectors, depending on dimension). It is also the gold standard for measuring the recall of approximate indexes.

### IndexIVFFlat — inverted file (faster, exact-ish)

Partitions the space into `nlist` cells using a coarse quantizer, then at search time only scans the `nprobe` cells nearest the query. This requires training.

```python
nlist = 100                              # number of partitions (cells)
quantizer = faiss.IndexFlatL2(d)         # used to assign vectors to cells
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)

index.train(xb)                          # learn the partition centroids
index.add(xb)
index.nprobe = 10                        # cells to visit at search (default 1)
D, I = index.search(xq, k)
```

The key search-time knob is **`nprobe`**: higher values search more cells, increasing recall at the cost of speed. `nprobe = 1` is fastest but lowest recall.

### IndexIVFPQ — inverted file + product quantization (compressed)

Adds **Product Quantization (PQ)** on top of IVF so each vector is stored as a short code instead of the full vector. This is the workhorse for very large datasets that must fit in RAM.

```python
nlist = 100
m = 8                                    # number of sub-quantizers (must divide d)
nbits = 8                                # bits per sub-vector code
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, nbits)
index.train(xb)
index.add(xb)
index.nprobe = 10
D, I = index.search(xq, k)
```

> [!NOTE]
> With PQ, each vector is compressed to `m` bytes (when `nbits = 8`). Distances are approximate. Increase `m` for better accuracy at higher memory cost.

### IndexHNSW — graph-based (high recall, no training)

Builds a navigable small-world graph. Excellent recall/speed trade-off, no training required, but higher memory and slower adds.

```python
index = faiss.IndexHNSWFlat(d, 32)       # M = 32 neighbors per node
index.hnsw.efConstruction = 40           # build quality (higher = better, slower build)
index.add(xb)
index.hnsw.efSearch = 64                 # search quality (higher = better recall, slower)
D, I = index.search(xq, k)
```

The graph parameters live on the nested `index.hnsw` object: `M` (set at construction), `efConstruction`, and `efSearch`.

### IndexPQFastScan — SIMD-accelerated PQ

A PQ variant that uses SIMD lookup tables for very fast distance computation. Often paired with a refinement step for accuracy.

```python
m = 8; nbits = 4; bbs = 32               # bbs (build block size) must be a multiple of 32
index = faiss.IndexPQFastScan(d, m, nbits, faiss.METRIC_L2, bbs)
index.train(xb)
index.add(xb)
D, I = index.search(xq, k)
```

### Refinement wrappers

Wrap a fast, compressed index in `IndexRefineFlat` to re-rank its top candidates using exact distances — recovering much of the lost accuracy:

```python
base = faiss.IndexPQFastScan(d, m, nbits, faiss.METRIC_L2)
index = faiss.IndexRefineFlat(base)
index.train(xb)
index.add(xb)
params = faiss.IndexRefineSearchParameters(k_factor=3)  # fetch 3*k candidates, re-rank
D, I = index.search(xq, k, params=params)
```

### Quick selection guide

| Situation | Recommended index |
|---|---|
| Small dataset, need exact results | `IndexFlatL2` / `IndexFlatIP` |
| Medium dataset, want speed, can train | `IndexIVFFlat` |
| Large dataset, must save memory | `IndexIVFPQ` |
| Want high recall without tuning, RAM is plentiful | `IndexHNSWFlat` |
| Need maximum query throughput | `IndexPQFastScan` (+ `IndexRefineFlat`) |

> [!TIP]
> Rather than choosing a class and its constructor arguments by hand, most users describe the index they want with a short string and let the [index factory](#6-the-index-factory) build it.

---

## 6. The Index Factory

`faiss.index_factory` builds an index from a compact **description string**. This is the idiomatic way to create indexes because it composes preprocessing, quantization, and search structures in one expression and makes it trivial to swap index types in benchmarks.

```python
index = faiss.index_factory(d, "IVF100,Flat")              # IVF with 100 cells, flat storage
index = faiss.index_factory(d, "PQ16")                     # pure product quantization, 16 sub-vectors
index = faiss.index_factory(d, "HNSW32")                   # HNSW graph, M=32
index = faiss.index_factory(d, "IVF4096,PQ32")             # IVF + PQ
index = faiss.index_factory(d, "PQ32x4fs,Refine(SQ8)")     # FastScan PQ with SQ8 refinement
index = faiss.index_factory(d, "OPQ16_64,IVF1000,PQ16")    # OPQ rotation + IVF + PQ
```

The signature is:

```python
index = faiss.index_factory(d, description, metric=faiss.METRIC_L2)
```

### How the string is read

A factory string is a comma-separated pipeline read left to right: **optional preprocessing → optional coarse quantizer (IVF) → encoding**.

| Component | Meaning |
|---|---|
| `Flat` | Store vectors uncompressed (exact within the visited cells). |
| `IVFx` | Inverted file with `x` cells. Needs training. Set `nprobe` at search. |
| `PQx` | Product Quantization with `x` sub-vectors. |
| `PQxXy` | PQ with `x` sub-vectors of `y` bits each (e.g. `PQ32x4`). |
| `...fs` | FastScan variant (e.g. `PQ32x4fs`) — SIMD-accelerated. |
| `SQ8`, `SQ4`, `SQfp16` | Scalar quantization to 8-bit / 4-bit / fp16. |
| `HNSWx` | HNSW graph with `M = x`. |
| `LSHx` | Locality-sensitive hashing. |
| `PCAx`, `OPQm_d` | Preprocessing: PCA reduction to `x` dims, OPQ rotation. |
| `L2norm` | Normalize vectors (turns inner product into cosine similarity). |
| `Refine(...)` | Wrap with an exact/finer re-ranking stage. |
| `IDMap`, `IDMap2` | Add support for custom IDs to an index that lacks it. |

> [!IMPORTANT]
> Set the metric when you build the index, not after. `faiss.index_factory(d, "IVF100,Flat", faiss.METRIC_INNER_PRODUCT)` produces an inner-product index; the default is `METRIC_L2`. See [Distance Metrics](#7-distance-metrics).

> [!TIP]
> For cosine similarity, prepend `L2norm,` to the factory string (or normalize your vectors with `faiss.normalize_L2(x)` before adding) **and** use `METRIC_INNER_PRODUCT`. Cosine similarity is just inner product on normalized vectors.

### Supported index families

Faiss ships a large catalog of index types, all reachable through the factory or as direct classes:

- **Flat / exact:** `IndexFlat`, `IndexFlatL2`, `IndexFlatIP`
- **Inverted file:** `IndexIVFFlat`, `IndexIVFPQ`, `IndexIVFPQR`, `IndexIVFScalarQuantizer`, `IndexIVFSpectralHash`
- **Product / additive quantization:** `IndexPQ`, `IndexPQFastScan`, `IndexAdditiveQuantizer` (Residual / Local Search quantizers), `IndexIVFAdditiveQuantizer`
- **Graph-based:** `IndexHNSW` (with Flat/PQ/SQ storage), `IndexNSG`
- **Hashing / compact:** `IndexLSH`, `IndexLattice`, `IndexRaBitQ`, `IndexRowwiseMinMax`
- **Wrappers / transforms:** `IndexPreTransform`, `IndexRefine` / `IndexRefineFlat`, `IndexIDMap` / `IndexIDMap2`, `Index2Layer`
- **Binary vectors:** `IndexBinaryFlat`, `IndexBinaryIVF`, `IndexBinaryHNSW`, `IndexBinaryHash`
- **Intel SVS (optional build):** `IndexSVSFlat`, `IndexSVSVamana`, `IndexSVSVamanaLVQ`, `IndexSVSVamanaLeanVec`

> [!NOTE]
> SVS (Intel Scalable Vector Search) index types — including factory strings like `SVSVamana64` and `SVSVamana32,LVQ4x4` — are only present when Faiss was built with the SVS integration enabled. The conda/pip packages may or may not include them depending on the build.

---

## 7. Distance Metrics

The metric determines what "nearest" means. Set it when constructing the index (constructor argument or `index_factory` metric parameter).

| Constant | Meaning |
|---|---|
| `faiss.METRIC_L2` | Squared Euclidean distance (default). Smaller = closer. |
| `faiss.METRIC_INNER_PRODUCT` | Maximum inner (dot) product. Larger = closer. Use for cosine on normalized vectors. |
| `faiss.METRIC_L1` | L1 / city-block distance. |
| `faiss.METRIC_Linf` | Chebyshev (infinity) distance. |
| `faiss.METRIC_Lp` | General L_p distance; set `p` via `index.metric_arg`. |
| `faiss.METRIC_Canberra` | Canberra distance. |
| `faiss.METRIC_BrayCurtis` | Bray–Curtis dissimilarity. |
| `faiss.METRIC_JensenShannon` | Jensen–Shannon divergence. |
| `faiss.METRIC_Jaccard` | Jaccard similarity (`sum min / sum max`). |
| `faiss.METRIC_NaNEuclidean` | Squared Euclidean, ignoring NaN components. |
| `faiss.METRIC_GOWER` | Gower's distance (mixed numeric/categorical). |

> [!WARNING]
> `METRIC_L2` returns **squared** distances, not Euclidean distances. If you need the true L2 distance, take the square root of the values in `D` yourself.

> [!NOTE]
> The additional metrics beyond L2 and inner product (`L1`, `Linf`, `Canberra`, `Jaccard`, `GOWER`, ...) are supported primarily by the **flat** (brute-force) indexes. Compressed and graph indexes generally support only L2 and inner product.

---

## 8. Saving and Loading Indexes

Indexes serialize to a single file and reload exactly as they were. This works for any index type.

```python
faiss.write_index(index, "my_index.faiss")     # save
index = faiss.read_index("my_index.faiss")      # load
D, I = index.search(xq, k)                       # use immediately — no retrain/re-add
```

### Load flags

`read_index` accepts an optional integer flag to control how the index is loaded — useful for very large indexes:

| Flag | Effect |
|---|---|
| `faiss.IO_FLAG_READ_ONLY` | Open read-only (no in-place modification). |
| `faiss.IO_FLAG_MMAP` | Memory-map the data instead of reading it into RAM. |
| `faiss.IO_FLAG_ONDISK_SAME_DIR` | Resolve an on-disk inverted-list file in the same directory as the index. |
| `faiss.IO_FLAG_SKIP_IVF_DATA` | Load only list sizes, not the IVF payload. |

```python
index = faiss.read_index("huge_index.faiss", faiss.IO_FLAG_MMAP)
```

> [!CAUTION]
> Only load index files you trust. An index file is a serialized data structure; reading one from an untrusted source executes the deserialization logic against attacker-controlled sizes. Faiss includes hardening limits for exactly this case — see the **Security** section of the Admin Guide if you must accept index files from outside your system.

---

## 9. Using IDs

By default, `add()` assigns sequential labels `0, 1, 2, ...`. To control the labels — for example to match your own database primary keys — use `add_with_ids`:

```python
ids = np.array([100, 200, 300, ...], dtype='int64')
index.add_with_ids(xb, ids)
D, I = index.search(xq, k)   # I now contains your custom IDs
```

> [!NOTE]
> Not every index type supports `add_with_ids` directly. If it doesn't, wrap it with `IndexIDMap` (or `IndexIDMap2`, which also supports `reconstruct` by ID):
>
> ```python
> index = faiss.IndexIDMap(faiss.IndexFlatL2(d))
> index.add_with_ids(xb, ids)
> ```
>
> In a factory string, prepend `IDMap,` or `IDMap2,`.

To remove vectors by ID, build an `IDSelector` and call `remove_ids`:

```python
sel = faiss.IDSelectorBatch(np.array([100, 200], dtype='int64'))
n_removed = index.remove_ids(sel)
```

---

## 10. Running on the GPU

GPU indexes are a near drop-in replacement for CPU indexes. You can either construct GPU indexes directly or move a CPU index onto the GPU. Requires a `faiss-gpu` (or `faiss-gpu-cuvs`) build.

### Move a CPU index to a single GPU

```python
res = faiss.StandardGpuResources()             # allocate GPU resources
index_flat = faiss.IndexFlatL2(d)              # build a CPU index
gpu_index = faiss.index_cpu_to_gpu(res, 0, index_flat)   # device 0

gpu_index.add(xb)
D, I = gpu_index.search(xq, k)
```

IVF and other trainable indexes work the same way — train and add after moving to the GPU:

```python
quantizer = faiss.IndexFlatL2(d)
index_ivf = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
gpu_index_ivf = faiss.index_cpu_to_gpu(res, 0, index_ivf)
gpu_index_ivf.train(xb)
gpu_index_ivf.add(xb)
D, I = gpu_index_ivf.search(xq, k)
```

### Use all available GPUs

```python
print("number of GPUs:", faiss.get_num_gpus())
cpu_index = faiss.IndexFlatL2(d)
gpu_index = faiss.index_cpu_to_all_gpus(cpu_index)   # shard across every GPU
gpu_index.add(xb)
D, I = gpu_index.search(xq, k)
```

> [!TIP]
> Input and output arrays can live in CPU memory — Faiss copies them to/from the GPU automatically. Results are faster if both input and output stay resident on the GPU, but for most workloads the convenience of NumPy arrays is worth it.

> [!NOTE]
> To bring a GPU index back to the CPU (e.g. before `write_index`), use `faiss.index_gpu_to_cpu(gpu_index)`.

---

## 11. Clustering with K-means

Faiss includes a fast, standalone K-means implementation built on top of its indexes. It is frequently used to compute centroids for IVF or for general vector clustering.

```python
d = 64
k = 1024                                   # number of clusters
kmeans = faiss.Kmeans(d, k, niter=20, verbose=True)
kmeans.train(xb)                           # xb: (n, d) float32, n must exceed k

centroids = kmeans.centroids               # shape (k, d)
D, I = kmeans.assign(xq)                    # nearest centroid for each query
```

Useful constructor options (passed as keyword arguments):

| Option | Meaning |
|---|---|
| `niter` | Number of clustering iterations (default 25). |
| `nredo` | Re-run clustering this many times and keep the best. |
| `spherical` | Normalize centroids (for cosine-style clustering). |
| `gpu` | `True` to use all GPUs, or an integer for a specific count. |
| `seed` | RNG seed for reproducibility. |
| `init_method` | `RANDOM` (default), `KMEANS_PLUS_PLUS`, or `AFK_MC2`. |

After training, `kmeans.obj` holds the objective value at each iteration and `kmeans.iteration_stats` holds detailed per-iteration statistics.

> [!TIP]
> For large datasets, `faiss.SuperKmeans` (added in v1.14.2) is a drop-in replacement for `Kmeans` that uses ADSampling + progressive pruning to converge faster, exposing the same `centroids`, `obj`, and `assign()` interface.

---

## 12. Common Tasks (Recipes)

### Cosine similarity search

```python
faiss.normalize_L2(xb)                     # normalize in place
index = faiss.IndexFlatIP(d)               # inner product on unit vectors == cosine
index.add(xb)
faiss.normalize_L2(xq)
D, I = index.search(xq, k)                 # D holds cosine similarities (higher = closer)
```

### Tune IVF recall vs. speed

```python
index = faiss.index_factory(d, "IVF1000,Flat")
index.train(xb); index.add(xb)
for nprobe in (1, 4, 16, 64):
    index.nprobe = nprobe
    D, I = index.search(xq, k)
    # measure recall against an IndexFlatL2 ground truth here
```

### Measure recall against exact ground truth

```python
from faiss.contrib.evaluation import knn_intersection_measure

# ground truth from an exact index
gt_index = faiss.IndexFlatL2(d); gt_index.add(xb)
_, I_gt = gt_index.search(xq, k)

# approximate index
_, I_approx = index.search(xq, k)
recall = knn_intersection_measure(I_approx, I_gt)
print("recall@k:", recall)
```

### Use a built-in synthetic dataset for experiments

```python
from faiss.contrib import datasets
ds = datasets.SyntheticDataset(64, 50000, 100000, 10000)  # d, n_train, n_db, n_query
index = faiss.index_factory(ds.d, "IVF100,Flat")
index.train(ds.get_train())
index.add(ds.get_database())
D, I = index.search(ds.get_queries(), 10)
```

### Use PyTorch tensors directly

```python
import faiss.contrib.torch_utils   # import enables Torch tensor arguments
import torch
xb_t = torch.rand(100000, 64)
index = faiss.IndexFlatL2(64)
index.add(xb_t)                     # Torch CPU tensor accepted directly
```

> [!IMPORTANT]
> After importing `faiss.contrib.torch_utils`, arguments to a single call must be uniformly NumPy arrays **or** uniformly Torch tensors — no mixing. Torch **GPU** tensors require a Faiss GPU index.

---

## 13. Troubleshooting

### Error: `TypeError: Input argument ... must be ndarray of dtype uint8/float32 ...`

**Cause:** You passed an array of the wrong dtype (commonly `float64`) or a non-contiguous array.
**Solution:** Convert before the call: `x = np.ascontiguousarray(x, dtype='float32')`.
**Prevention:** Always create your arrays as `float32` from the start (`.astype('float32')`).

### Error: `xq must be float32 or float16` (GPU path)

**Cause:** GPU search requires `float32` or `float16` query/database arrays and `int64`/`int32` ID arrays.
**Solution:** Cast queries to `float32`; cast ID arrays to `int64`.
**Prevention:** Build all GPU inputs as `float32`.

### Error: `faiss-gpu installed but nvidia-cuda-runtime-cu12 is missing — pip install 'nvidia-cuda-runtime-cu12>=12.6,<13'`

**Cause:** The `faiss-gpu` pip wheel needs the NVIDIA CUDA runtime wheels, which were not installed.
**Solution:** Install exactly what the message names, in the pinned range:
```bash
pip install 'nvidia-cuda-runtime-cu12>=12.6,<13' 'nvidia-cublas-cu12>=12.6,<13' 'nvidia-curand-cu12>=10.3.7,<11'
```
**Prevention:** Prefer the conda `faiss-gpu` package, which pulls CUDA in as a dependency.

### Error: `faiss-gpu: failed to load <lib> — corrupt nvidia-*-cu12 wheel?`

**Cause:** A CUDA runtime wheel is present but damaged.
**Solution:** Force-reinstall the runtime wheels:
```bash
pip install --force-reinstall 'nvidia-cuda-runtime-cu12>=12.6,<13' 'nvidia-cublas-cu12>=12.6,<13' 'nvidia-curand-cu12>=10.3.7,<11'
```

### `faiss` fails to import on Python 3.12 (`numpy.distutils` deprecated)

**Cause:** Older Faiss builds relied on `numpy.distutils`, which was removed in newer Python/NumPy.
**Solution:** Upgrade to a current Faiss release (v1.14.2) where CPU-feature detection no longer depends on `numpy.distutils` for NumPy ≥ 2.0.
**Prevention:** Keep Faiss and NumPy current together.

### `No module named 'faiss.swigfaiss'`

**Cause:** The compiled SWIG extension for your CPU's instruction set was not built or not found (most common when building from source).
**Solution:** Build the matching SWIG target. Faiss reports your supported instruction sets and tells you which `FAISS_OPT_LEVEL` to use and which target to build (`swigfaiss`, `swigfaiss_avx2`, `swigfaiss_avx512`, `swigfaiss_avx512_spr`, or `swigfaiss_sve`).
**Prevention:** Install a prebuilt conda/pip package instead of building from source.

### Search results are poor / recall is low

**Cause:** For IVF indexes, the default `nprobe = 1` visits only one cell.
**Solution:** Raise `index.nprobe` (try 8, 16, 32...). For HNSW, raise `index.hnsw.efSearch`. For PQ, increase `m`/`nbits` or add an `IndexRefineFlat` wrapper.
**Prevention:** Benchmark recall against an `IndexFlatL2` ground truth while tuning.

### `RuntimeError: ... Not an IVF index` / `factory string ...: unbalanced parentheses`

**Cause:** A malformed factory string, or calling an IVF-only operation (e.g. setting `nprobe`) on a non-IVF index.
**Solution:** Check the factory string parentheses are balanced and the operation matches the index family.

---

## 14. FAQ

**Q: Does Faiss have a command-line tool?**
No. Faiss is a library. You use it from Python or C++ code. The scripts in `benchs/`, `demos/`, and `perf_tests/` are benchmarks and examples, not a supported CLI.

**Q: What data type do my vectors need to be?**
`float32`, in a 2-D row-major (C-contiguous) NumPy array of shape `(n, d)`. Some specialized paths accept `float16`, `uint8`, or `int8`, but `float32` is the universal default.

**Q: Which index should I start with?**
Start with `IndexFlatL2` to establish correct, exact results. Then, if it's too slow or too large, move to `IndexIVFFlat`, and if memory is the constraint, `IndexIVFPQ`. Use `IndexFlatL2` as your recall ground truth throughout.

**Q: How do I do cosine similarity?**
Normalize your vectors (`faiss.normalize_L2`) and use `METRIC_INNER_PRODUCT` (or prepend `L2norm,` in a factory string). Cosine similarity is inner product on unit-length vectors.

**Q: My `METRIC_L2` distances look too big — are they wrong?**
No. `METRIC_L2` returns **squared** L2 distances. Take `np.sqrt(D)` for true Euclidean distances.

**Q: Can I add more vectors after searching?**
Yes — `add()` is incremental for most index types. (HNSW gained optional persistent locks for safe incremental adds in v1.14.2.) Re-running `search()` reflects the new vectors.

**Q: Do I need a GPU?**
No. The CPU build is fully featured and fast for most workloads. The GPU build accelerates large-scale exact search, IVF, PQ, and K-means when you have NVIDIA (CUDA/cuVS), AMD (ROCm), or Apple Silicon (Metal, IndexFlat) hardware.

**Q: How big can an index get?**
With compression (IVFPQ and similar) Faiss is designed to hold **billions** of vectors in RAM on a single server. For datasets that exceed RAM, see on-disk and sharded indexes in the Admin Guide and the `faiss.contrib` helpers.

**Q: Is the search exact?**
`IndexFlat*` is exact. All other types are approximate by design and trade a little recall for large speed/memory gains. The amount is tunable.

**Q: How do I make results reproducible?**
Seed NumPy (`np.random.seed`) for your data, and pass `seed=` to `Kmeans`. Note that multi-threaded add/search may produce ties in slightly different orders.

---

## 15. Reference

### Index lifecycle methods

| Method | Description |
|---|---|
| `train(x)` | Learn the data distribution (required when `is_trained` is `False`). |
| `add(x)` | Add vectors with sequential labels. |
| `add_with_ids(x, ids)` | Add vectors with custom `int64` labels. |
| `search(xq, k, params=None)` | Return `(D, I)` — distances and labels of `k` nearest neighbors. |
| `range_search(xq, radius)` | Return `(lims, D, I)` — all neighbors within `radius`. |
| `assign(xq, k=1)` | Return labels only (no distances). |
| `reconstruct(label)` | Return the stored/decoded vector for a label. |
| `reconstruct_n(i0, ni)` | Reconstruct a range of stored vectors. |
| `remove_ids(selector)` | Remove vectors selected by an `IDSelector`. |
| `reset()` | Remove all vectors. |
| `merge_from(other)` | Move all vectors from another compatible index into this one. |

### Common attributes

| Attribute | Meaning |
|---|---|
| `index.d` | Vector dimension. |
| `index.ntotal` | Number of stored vectors. |
| `index.is_trained` | Whether the index is ready to add/search. |
| `index.metric_type` | The distance metric in use. |
| `index.nprobe` | (IVF) cells to scan at search time. |
| `index.hnsw.efSearch` / `efConstruction` | (HNSW) search / build quality. |

### Top-level functions

| Function | Description |
|---|---|
| `faiss.index_factory(d, desc, metric)` | Build an index from a description string. |
| `faiss.write_index(index, fname)` | Serialize an index to disk. |
| `faiss.read_index(fname, flags=0)` | Load an index from disk. |
| `faiss.normalize_L2(x)` | Normalize vectors in place (for cosine search). |
| `faiss.index_cpu_to_gpu(res, dev, index)` | Move an index to a GPU. |
| `faiss.index_cpu_to_all_gpus(index)` | Shard an index across all GPUs. |
| `faiss.index_gpu_to_cpu(index)` | Move a GPU index back to the CPU. |
| `faiss.get_num_gpus()` | Number of GPUs Faiss can see. |
| `faiss.Kmeans(d, k, **kw)` | K-means clustering object. |
| `faiss.pairwise_distances(xq, xb, metric)` | Full pairwise distance matrix. |

### Environment variables

| Variable | Effect |
|---|---|
| `FAISS_OPT_LEVEL` | Force the SIMD level to load (`generic`, `avx2`, `avx512`, `avx512_spr`, `sve`). |
| `FAISS_DISABLE_CPU_FEATURES` | Whitespace/comma-separated list of CPU features to disable at load (e.g. to work around a buggy AVX512 path). |

### `faiss.contrib` helper modules

| Module | Purpose |
|---|---|
| `contrib.datasets` | Standard and synthetic datasets for experiments. |
| `contrib.evaluation` | Recall / intersection measures (`knn_intersection_measure`). |
| `contrib.torch_utils` | Use PyTorch tensors as Faiss arguments. |
| `contrib.ondisk` | Merge indexes into an on-disk index. |
| `contrib.ivf_tools` | Override the IVF coarse quantizer. |
| `contrib.inspect_tools` | Inspect SWIG-wrapped C++ objects. |

For architecture, build-from-source instructions, deployment, performance tuning, and security, see the **Admin Guide**.

---

_Generated June 2026 from facebookresearch/faiss at eb4c1ea. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
