FAISS vector similarity search library for PascalAI. Efficiently find the K nearest neighbors among millions of embedding vectors — the foundation for RAG pipelines, image deduplication, recommender systems, and semantic search.
ppm install faiss
libfaiss-dev (Ubuntu/Debian: sudo apt install libfaiss-dev) or build from source at github.com/facebookresearch/faiss
FAISS (Facebook AI Similarity Search) is Meta's battle-tested library for fast nearest-neighbor
search over dense float vectors. It underpins production RAG systems, recommendation engines, and image search
at billion-vector scale. The PascalAI binding exposes its full index hierarchy — from exact brute-force
search (IndexFlat) to compressed approximate indexes (IVFPQ, HNSW) —
through a clean, idiomatic API.
Choose your index by the accuracy/speed/memory tradeoff you need: fitFlat for exact results on
small corpora, fitHNSW for fast approximate search with low memory, fitIVFPQ for
billion-scale datasets where RAM is constrained.
uses faiss;
{ Store 1000 document embeddings (dim=768) }
var idx := NewIndex(768, fitFlat);
{ Assume embeddings is an array of 1000*768 doubles }
Add(idx, embeddings, 1000);
WriteLn('Stored: ' + IntToStr(IndexSize(idx)));
{ Search: find 5 nearest to a query embedding }
var result := SearchOne(idx, queryVec, 5);
for var i := 0 to result.K - 1 do
WriteLn('ID=' + IntToStr(result.Labels[i]) +
' dist=' + FloatToStr(result.Distances[i]));
FreeIndex(idx);
uses faiss;
var idx := NewHNSWIndex(384, 32); { dim=384, M=32 connections }
AddWithIDs(idx, vectors, docIDs, 50000);
{ Save to disk }
SaveIndex(idx, '/data/docs.faiss');
FreeIndex(idx);
{ Later: load and search }
var idx2 := LoadIndex('/data/docs.faiss');
var r := SearchOne(idx2, queryEmb, 10);
uses faiss;
{ Normalize vectors first for cosine similarity }
NormalizeVectors(myVectors, N, D);
var idx := NewIndex(D, fitFlatIP); { inner product = cosine after L2-norm }
Add(idx, myVectors, N);
var r := SearchOne(idx, queryNorm, 5);
| Constant | Description |
|---|---|
| fitFlat | Exact brute-force L2 search. 100% accurate; slow on large corpora. Best for < 100 K vectors. |
| fitFlatIP | Exact inner-product search. Use with L2-normalized vectors to get cosine similarity. |
| fitIVFFlat | Approximate IVF (inverted file) with flat quantizer. Requires training. Good accuracy/speed balance. |
| fitHNSW | Graph-based approximate search (Hierarchical Navigable Small World). No training; very fast queries; higher RAM use. |
| fitPQ | Product Quantization — compresses vectors to reduce memory by 4–64× at the cost of some accuracy. |
| fitIVFPQ | IVF + PQ combined. Best for billion-scale datasets with limited RAM. |
| fitLSH | Locality-Sensitive Hashing for binary/Hamming distance search. |
| Function | Description |
|---|---|
| NewIndex(dim, kind) | Create a new empty index with the given vector dimension and index type constant |
| NewIVFIndex(dim, nlist) | Create an IVF index with nlist Voronoi cells; call Train before Add |
| NewHNSWIndex(dim, M) | Create an HNSW index with M bi-directional links per node (16–64 typical) |
| FreeIndex(idx) | Release all memory held by an index handle |
| IsTrained(idx) | Returns True if the index has been trained and is ready to accept vectors |
| Train(idx, vecs, n) | Train an IVF or PQ index on a representative sample of n vectors |
| Function | Description |
|---|---|
| Add(idx, vecs, n) | Add n vectors to the index; assigns sequential IDs starting from 0 |
| AddWithIDs(idx, vecs, ids, n) | Add n vectors with explicit integer IDs (e.g. database row IDs) |
| AddOne(idx, vec) | Convenience wrapper to add a single vector; returns its assigned ID |
| IndexSize(idx) | Returns the number of vectors currently stored in the index |
| Function | Description |
|---|---|
| Search(idx, queries, nq, k) | Search nq query vectors in one batch; returns TSearchResult with Labels and Distances arrays (shape nq×k) |
| SearchOne(idx, query, k) | Search a single query vector for its k nearest neighbors; returns TSearchResult |
| RangeSearch(idx, query, radius) | Return all vectors within radius distance of the query; result count varies |
| SetNProbe(idx, nprobe) | For IVF indexes: set number of cells to visit per query (higher = more accurate, slower) |
| Function | Description |
|---|---|
| SaveIndex(idx, path) | Serialize the index to a binary file at path (can be reloaded across processes) |
| LoadIndex(path) | Deserialize and return an index from a previously saved binary file |
| IndexToBytes(idx) | Serialize the index to an in-memory byte array for storage in a database or object store |
| IndexFromBytes(data) | Deserialize an index from a byte array returned by IndexToBytes |
| Function | Description |
|---|---|
| KMeans(vecs, n, k, niter) | Run K-means clustering on n vectors for niter iterations; returns centroid array and cluster assignments |
| NormalizeVectors(vecs, n, d) | In-place L2 normalization of n vectors of dimension d; required before using fitFlatIP for cosine similarity |
| PairwiseDistances(a, b, na, nb, d) | Compute the full na×nb L2 distance matrix between two sets of vectors; returns a flat Float array |
All vectors in a single index must have the same dimensionality. Common embedding dimensions: 384 (MiniLM), 768 (BERT-base), 1536 (OpenAI text-embedding-3-small), 3072 (text-embedding-3-large). The dimension is fixed at index creation time and cannot be changed.
fitFlat and fitIVFFlat minimize L2 (Euclidean) distance. For cosine similarity,
call NormalizeVectors on all vectors before indexing and use fitFlatIP (inner product).
After normalization, inner product equals cosine similarity.
IVF and PQ indexes require a training pass before vectors can be added. Pass a representative sample of at
least 39 × nlist vectors to Train. HNSW and Flat indexes do not require training.
Check IsTrained(idx) before calling Add if using a trainable index type.
For IVF indexes, SetNProbe(idx, nprobe) controls how many Voronoi cells are visited per query.
The default is 1 (fastest, least accurate). Set nprobe = nlist for exact results.
A value of 8–64 is a practical accuracy/speed sweet spot for most workloads.
Approximate per-vector memory use: Flat — 4×D bytes (full float32 storage); HNSW — 4×D + ~M×8 bytes per vector for the graph; PQ — roughly D/M bytes per vector after compression (M = number of sub-quantizers). For 10 M vectors at dim=768, a Flat index uses ~29 GB; an IVFPQ index can reduce this to under 1 GB.