advanced ann-indexing 36 min read

HNSW: Hierarchical Navigable Small-World Construction and Search

How one randomized idea — a hierarchy of nested graphs — turns the navigable small-world graph's arbitrary entry into a provably logarithmic descent, and the heuristic that keeps each layer navigable

Overview & motivation

The navigable small-world graph left us with a working index and one loose end. Its search is a greedy beam that walks the graph, and it works — but it starts at a fixed entry node, an arbitrary first-inserted hub that has nothing to do with the query, and from there the beam has to grind across the whole graph to reach the query’s neighborhood. The graph’s navigability, too, is an empirical property of the insertion order, not something the search exploits structurally. HNSW fixes both with a single idea borrowed from a classic data structure: a hierarchy.

A skip list speeds up a sorted linked list by stacking it into express lanes — a sparse top list for long jumps, denser lists below for fine positioning — with each element promoted to a random height. HNSW does the same to the navigable small-world graph. Every vector is assigned a random level; layer 00 holds all of them and is an ordinary NSW graph, and each higher layer holds a geometrically thinning random subset. Search enters at the lone hub on top, descends greedily through the sparse upper layers to land at an entry adapted to the query, and only then runs the familiar beam at layer 00. The laboratory below steps through the layer pyramid and one query’s descent, contrasts the two ways to choose a node’s neighbors, and traces the recall-versus-cost frontiers of HNSW, the flat graph, and the inverted file on one shared cloud.

nodes at layer 3
1 of 35
descent is at node
15
The query (◆) enters at the lone apex hub (layer 3) and beam-1 descends — node 15 15 → 17 → 3 — each upper layer cheaply narrowing the region. At layer 0 a width-ef beam from the descended entry finds the true nearest neighbor (green ring). Step up the layers and watch the graph thin geometrically: ~351331 nodes.

The first panel is the pyramid: step from the lone apex hub down to the full layer 00 and watch the graph thin geometrically while a query descends. The second is the heuristic that keeps each layer navigable — naive nearest-MM versus the diversity rule. The third is the recall-versus-cost frontier, where the hierarchy’s cheaper entry shows up as a curve that reaches a given recall at fewer distance computations than the flat graph it layers.

Movement 1 — the hierarchy and the level-assignment law

The hierarchy is built from one random choice per node. When a vector is inserted, it draws a maximum level

L=ln(U)mL,UUniform(0,1),mL=1lnM,L = \left\lfloor -\ln(U)\, m_L \right\rfloor, \qquad U \sim \mathrm{Uniform}(0,1), \quad m_L = \frac{1}{\ln M},

and is inserted into the graph at every layer 0,1,,L0, 1, \dots, L. The multiplier mL=1/lnMm_L = 1/\ln M is the one choice that makes the level distribution clean, and everything the hierarchy promises follows from it as exact probability — this is the provable spine of HNSW, the counterpart of Kleinberg’s theorem for the flat graph.

Theorem 1 (The level-assignment law).

With mL=1/lnMm_L = 1/\ln M, the level L=ln(U)mLL = \lfloor -\ln(U)\, m_L \rfloor of a node satisfies, for every integer 0\ell \ge 0,

P(L)=M,P(L=)=M(11M).P(L \ge \ell) = M^{-\ell}, \qquad P(L = \ell) = M^{-\ell}\left(1 - \tfrac{1}{M}\right).

Consequently the expected number of nodes present at layer \ell is nMn\,M^{-\ell} — occupancy decays geometrically by the factor 1/M1/M — the top non-empty layer holds O(1)O(1) nodes, and the expected maximum level over nn nodes is

E ⁣[max1inLi]=logMn+O(1).\mathbb{E}\!\left[\max_{1 \le i \le n} L_i\right] = \log_M n + O(1).

The expected number of layers a search must descend before reaching layer 00 therefore grows like logMn\log_M n.

Proof.

The event LL \ge \ell is ln(U)mL-\ln(U)\,m_L \ge \ell, i.e. lnU/mL=lnM\ln U \le -\ell/m_L = -\ell \ln M, i.e. UMU \le M^{-\ell}. Since UU is uniform on (0,1)(0,1) and M1M^{-\ell} \le 1, this has probability exactly MM^{-\ell}, which gives the survival law; differencing consecutive tails gives the per-level mass P(L=)=MM(+1)P(L=\ell) = M^{-\ell} - M^{-(\ell+1)}. Each of the nn nodes is present at layer \ell independently with probability MM^{-\ell}, so the expected occupancy is nMn\,M^{-\ell}; this falls below 11 once >logMn\ell > \log_M n, which is why the top layer is O(1)O(1). The maximum of nn independent geometric levels concentrates one level above where the expected count drops through 11, giving logMn+O(1)\log_M n + O(1). \blacksquare

The companion code draws three hundred thousand levels and confirms the tail law to the place where it matters: P(L1,2,3)=(0.1257,0.0155,0.0021)P(L \ge 1,2,3) = (0.1257,\, 0.0155,\, 0.0021) against the theoretical (0.125,0.0156,0.00195)=M(0.125,\, 0.0156,\, 0.00195) = M^{-\ell} for M=8M = 8. In a graph actually built on 500500 vectors the layer occupancies are 5004910500 \to 49 \to 10 — each layer about an eighth of the one below, exactly the geometric decay — and the realized top layer holds about n/Mtopn/M^{\,\text{top}} nodes, with the genuine O(1)O(1) apex sitting at the logMn\log_M n level. The scaling is the headline:

Proposition 1 (The descent depth grows logarithmically).

As the database grows from 200200 to 10001000 vectors, the mean maximum level rises from about 2.32.3 to about 3.23.2, tracking logMn\log_M n (from 2.552.55 to 3.323.32) within a constant. Because the maximum level depends only on the level draws, not on the constructed graph, this is measured from the law itself: the entry-descent depth is logarithmic in nn.

That O(logn)O(\log n) descent depth, paired with the bounded per-layer degree the construction enforces, is the intuition for HNSW’s celebrated logarithmic search. We keep the word intuition deliberately: the level law is exact, but the end-to-end search cost also depends on each layer being navigable, which on real vectors is empirical — the honest line we hold throughout.

Movement 2 — choosing a node’s neighbors

A skip list’s express lanes are trivially correct because the data is sorted. A graph has no order, so the one design choice that makes or breaks navigability is which neighbors each node keeps. Linking to the nearest MM is the obvious rule, and it is the wrong one: the nearest MM to a point tend to cluster on one side of it, so the graph fills with short, redundant edges and loses the long-range links that let a greedy walk cross the space. HNSW’s fix is a small, sharp heuristic.

Definition 1 (Heuristic neighbor selection (Malkov–Yashunin, Algorithm 4)).

Given a base node and a candidate set, scan the candidates in order of increasing distance to the base and keep a candidate cc only if, for every already-kept neighbor rr,

d(c,r)d(c,base).d(c, r) \ge d(c, \text{base}).

In words: admit cc unless some neighbor we already kept is closer to cc than cc is to the base — in which case that neighbor already “covers” cc‘s direction, and a link to cc would be redundant. Stop at MM kept neighbors.

The rule trades a little distance for a lot of diversity. On the toy cloud, the naive rule keeps the four strict-nearest neighbors of a base node — all in the same blob — while the heuristic drops the redundant near ones and keeps a spread-out set that reaches into other clusters, preserving exactly the long-range links the small-world property needs. The companion code measures the direction: the heuristic’s kept set has strictly larger mean pairwise spread than the naive set on the same candidates. It is a heuristic, with no optimality proof, and the topic flags it as such — but it is the single design decision that most distinguishes HNSW from a naive layered graph.

Movement 3 — building and searching the hierarchy

Construction and search both reuse the prerequisite’s machinery, restricted to one layer at a time.

Definition 2 (HNSW construction and search).

Construction. Insert the vectors in random order. A new point draws its level LL, greedy-descends with beam width 11 from the global entry through every layer above LL to find a query-adapted entry, then at each layer from min(L,top)\min(L, \text{top}) down to 00 runs a beam of width efconstruction\mathrm{ef}_{\text{construction}}, selects neighbors from the result by the heuristic of Definition 1, and links bidirectionally — capping degree at MM per upper layer and 2M2M at layer 00, re-running the heuristic to shrink any over-full neighborhood. If the new point’s level exceeds the current top, it becomes the entry point.

Search. From the single top entry, greedy-descend with beam width 11 through layers top,,1\text{top}, \dots, 1, handing each layer’s nearest node down as the next layer’s entry; then run a beam of width ef\mathrm{ef} at layer 00 and return the kk nearest. The total cost is the distance computations summed over all layers.

The per-layer beam is not a new algorithm — it is the prerequisite’s greedy beam search over a per-layer adjacency, seeded from the entry handed down from above. That this reuse is faithful is the cleanest correctness check in the topic:

Proposition 2 (One layer of HNSW is the flat graph's search, exactly).

Collapse the hierarchy to a single layer — force every node to level 00 — and HNSW’s per-layer beam returns the same neighbor indices and the same distance-computation count as the prerequisite’s greedy search on the identical flat adjacency, for every query and every beam width. The hierarchy is the only new thing; the search is unchanged.

What the hierarchy buys is a cheaper entry, and the payoff is robust because it is intra-graph — HNSW against the very flat NSW it layers, on the same cloud:

Proposition 3 (The hierarchy reaches a given recall at lower cost).

On the synthetic cloud of 500500 vectors, HNSW reaches recall@10 of 0.90.9 at about 9292 distance computations per query, while the flat navigable small-world graph needs about 134134 for the same recall — and at that 9292-computation budget HNSW is in fact already exact, where the flat graph still sits at 0.99750.9975 and does not reach a full 1.01.0 until roughly 184184. Sweeping the beam width ef\mathrm{ef}, the HNSW frontier sits at or below the flat-graph frontier at every recall level: the upper layers replace a long beam search from a fixed hub with a short descent to a query-adapted entry. Recall is non-decreasing in ef\mathrm{ef}, and at a wide beam both reach the exact answer.

Finally the arc closes. The inverted file and HNSW are the two families of sublinear search, and we can now run them against each other honestly — built on the same cloud, scored against the same ground truth, each measured by the distance computations it spends.

Proposition 4 (Graph versus partition, on one cloud).

On the shared cloud, sweeping HNSW’s beam width against the inverted file’s number of probed cells, both indexes reach the exact answer at full cost — HNSW’s recall climbs to 1.01.0, and the inverted file is exact once it probes every cell. At a matched budget the graph is ahead here: at the 9292 distance computations where HNSW reaches recall 1.01.0, the inverted file reaches about 77%77\%. This is a property of one synthetic low-rank cloud with a shared ground truth, not a universal ranking — on data whose geometry suits Voronoi partitioning the order can reverse.

Honest accounting

The hierarchy closes the opening the flat graph left — the arbitrary entry becomes a logarithmic descent — and with the inverted file beside it, the two families of sublinear vector search are now both on the table. What remains is the messier reality production indexes face: vectors that arrive and are deleted over time, and queries that carry a filter the index must respect. Keeping a navigable graph connected under insertion and deletion, and searching it under a predicate, is where the next topic goes.

Connections

  • HNSW is the navigable small-world graph with a hierarchy bolted on: layer zero of HNSW IS an NSW graph over all the vectors, and the per-layer beam reused at every level is exactly the prerequisite's greedy search — forced to a single layer it reproduces it to the index and the distance-computation count. The hierarchy fixes the one thing the flat graph left open, the arbitrary entry, by descending from a coarse top layer to a query-adapted entry into layer zero navigable-small-world-graphs
  • the inverted file and HNSW are the two great families of sublinear vector search — partition the space into Voronoi cells, or walk a graph — and this topic runs them head to head on one shared cloud with a single ground truth, tracing each index's recall against the distance computations it spends; the comparison is the partition-versus-graph trade the capstone's retrieval layer has to make ivf-voronoi-partitioning

References & Further Reading