Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Faiss User 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.

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
  2. Installation
  3. Core Concepts
  4. Building and Searching an Index
  5. Choosing an Index Type
  6. The Index Factory
  7. Distance Metrics
  8. Saving and Loading Indexes
  9. Using IDs
  10. Running on the GPU
  11. Clustering with K-means
  12. Common Tasks (Recipes)
  13. Troubleshooting
  14. FAQ
  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.

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
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.


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.

CPU only

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

GPU (CUDA)

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

GPU (NVIDIA cuVS)

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):

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.

Pixi

If you use Pixi, the same conda channels and package names apply:

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

Verify the installation

import faiss
print(faiss.__version__)
index = faiss.IndexFlatL2(8)
print("OK", index.d)
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.

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.

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

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, ...

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:

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.

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

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.

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.

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.

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.

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:

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 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.

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:

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.

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.

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.
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:

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):

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:

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

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:

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

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.

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)

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

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

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

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

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:

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:

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.