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 theFAISS_VERSION_MAJOR/MINOR/PATCHmacros (C++).
Table of Contents
- Quick Start
- Installation
- Core Concepts
- Building and Searching an Index
- Choosing an Index Type
- The Index Factory
- Distance Metrics
- Saving and Loading Indexes
- Using IDs
- Running on the GPU
- Clustering with K-means
- Common Tasks (Recipes)
- Troubleshooting
- FAQ
- 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
float32NumPy array in row-major (C-contiguous) order, shaped(n_vectors, d). If your array isfloat64or non-contiguous, convert it first withnp.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.
Conda (recommended)
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-forgechannel is required for up-to-date BLAS dependencies (Intel MKL on x86-64, OpenBLAS on ARM). Forfaiss-gpu, thenvidiachannel is additionally required for the CUDA runtime. Forfaiss-gpu-cuvs, you also need therapidsaichannel.
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-gpupip wheel expects the matching NVIDIA CUDA runtime wheels to be installed alongside it. If they are missing, importing Faiss raises a clear error such asfaiss-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_trainedisTruefrom the start. Indexes whose names containIVF,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, ...
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:
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
searchbut notrange_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.
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/IndexFlatIPwhenever 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
mbytes (whennbits = 8). Distances are approximate. Increasemfor 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 isMETRIC_L2. See Distance Metrics.
Tip
For cosine similarity, prepend
L2norm,to the factory string (or normalize your vectors withfaiss.normalize_L2(x)before adding) and useMETRIC_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
SVSVamana64andSVSVamana32,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_L2returns squared distances, not Euclidean distances. If you need the true L2 distance, take the square root of the values inDyourself.
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_idsdirectly. If it doesn't, wrap it withIndexIDMap(orIndexIDMap2, which also supportsreconstructby ID):index = faiss.IndexIDMap(faiss.IndexFlatL2(d)) index.add_with_ids(xb, ids)In a factory string, prepend
IDMap,orIDMap2,.
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), usefaiss.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 forKmeansthat uses ADSampling + progressive pruning to converge faster, exposing the samecentroids,obj, andassign()interface.
12. Common Tasks (Recipes)
Cosine similarity search
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.