RAG and vector searchLoading progress…

HNSW layers

Trace IVF and HNSW search and explain how candidate exploration trades latency for recall.

HNSW layers

HNSW stacks graphs in layers. Each node gets a top layer at random so that about 1/M of the nodes reach layer 1, 1/M² reach layer 2, and so on. Search starts at the sparse top, makes long hops, drops a layer at a time, and finishes with a detailed beam search of width efSearch on the dense bottom layer. Build parameters (such as M) affect connectivity and memory; efSearch affects search breadth and latency.

M (connectivity)16nodes1,000,000
layer ≥chance for a nodeexpected nodes
011000000
10.062562500
20.0039063906.25
30.000244244.14
upper layers are sparse, so a search can take long hops there, then refine on the dense bottom layer small example (query x = 10, entry node 0, ef = 1): bottom layer alone ends on node 1 (stuck) with a sparse upper layer 0 → 3 → 4 the bottom search starts at node 4 and finds the nearest node ef (efSearch) is the width of the bottom-layer exploration, not the number of results k
How many nodes reach each layer? Then see why a sparse layer helps.

Predict first. Is efSearch the same setting as the number of returned results?

Chapter context

An approximate index organizes vectors so a query can avoid most comparisons. Clustering and graph navigation use different mechanisms to choose candidates, and their build settings influence later search quality.

Chapter reference: math, worked example & code

This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.

Math and notation

Under the simplifying assumption of equally sized IVF lists, N vectors, C cells and p probes produce approximately

Ncandidates≈Np/C.N_{candidates}\approx Np/C.

Add coarse scoring, uneven list sizes and any reranking costs separately. HNSW has no equivalent universal fixed candidate-count formula.

Worked example

For 102,400 vectors evenly divided among 1024 cells, a cell has 100 vectors. Probing eight cells scans about 800 candidates, compared with 102,400 in a full scan. This excludes coarse-centroid comparisons and says nothing by itself about recall.

Reference code

The following example uses the Python standard library and does not call a remote service.

total,cells,probes=102400,1024,8
assert total/cells == 100
assert total*probes/cells == 800
assert total/(total*probes/cells) == 128

Python examples are provided for study and local execution; this page does not execute Python.

Common mistakes

An average candidate count is not an actual worst-case bound. More graph edges consume memory. Approximate search settings must be evaluated against the current corpus and metric.