Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 32

Probabilistic Layer Assignment & Skip Lists

Recommended resources

In our previous lesson, we established the core architectural advantage of HNSW: its multi-layer graph structure, inspired by skip lists, which enables logarithmic search complexity by separating long-range and short-range navigation. You learned what the structure is and why it's so effective.

This lesson drills down into the elegant mechanism that builds this hierarchy. Our goal is to describe the probabilistic layer assignment for new nodes, drawing parallels to skip lists. We will dissect the specific formula used, understand its probabilistic underpinnings, and see how it automatically generates the efficient, layered graph structure we need. This is the first crucial step in turning the HNSW concept into concrete, buildable logic.

The Skip List Principle Revisited

As we discussed, the inspiration for HNSW's hierarchy is the skip list. A skip list augments a simple sorted linked list with "express lanes" on higher levels. A search can traverse these express lanes to quickly skip over large sections of the list before dropping down to more granular levels for the final search.

A search for the value '11' in a skip list. The search starts at the top-most layer (Layer 3), makes a long jump, overshoots, and drops to Layer 2. It again overshoots at '19' and drops to Layer 1, where it finds '11'. This avoids a linear scan of the base layer.

The crucial question is: how are nodes chosen to be part of these express lanes? The answer is simple probability. When a new node is added, a virtual coin is tossed. If it's heads, the node is "promoted" to the next layer up. This process is repeated, so the probability of a node reaching a very high layer decreases exponentially. For a promotion probability p (e.g., 0.5), the chance of reaching level i is . This naturally creates sparse upper layers and a dense base layer.

The article "Understanding HNSW" provides a formal description of this principle.

Understanding HNSW — Hierarchical Navigable Small World

This reading will solidify your understanding of the probabilistic foundation that HNSW borrows from skip lists.

Please read the section titled Skip List Algorithmic Principles. Focus on how the probabilistic level assignment creates an exponential decay in the number of nodes at each level, which is the key to its O(log n) performance.


Adapting Probability for High-Dimensional Space

HNSW adopts this exact principle. However, unlike a 1D skip list where nodes have a clear order, our vectors exist in a high-dimensional space with no inherent "next" element. HNSW's insight was to apply the probabilistic layering not to an ordered list, but to the nodes of a proximity graph.

When a new vector is inserted into the graph, it is assigned a maximum layer, l, on which it will exist. (It will also exist on all layers below l, down to Layer 0). This assignment is made using a probabilistic formula defined in the original paper:

This formula might seem opaque at first, but it elegantly produces the same exponential decay we saw in skip lists. Let's break it down.

  • uniform(0,1): This is simply a random floating-point number generated between 0.0 and 1.0. It's the source of randomness for our process.
  • -ln(...): This is the core of the trick. Taking the negative natural logarithm of a uniformly distributed random variable transforms it into a variable that follows an exponential distribution. The key property here is that small output values are very likely, while large output values are very rare. This is exactly what we want: most nodes should be assigned to low layers, and only a few should reach the high layers.
  • m_L: This is a normalization factor, or "level multiplier." It scales the output of the exponential distribution to control how many layers the graph is likely to have. A larger m_L "stretches" the distribution, making it more likely for nodes to reach higher layers.
  • floor(...): Finally, we round the result down to the nearest integer to get our final layer index l.

The SDSLabs blog post on implementing HNSW gives a fantastic, code-oriented explanation of this process.

Implementing HNSW index (Part 1) - SDSLabs Blog

This article translates the mathematical formula into practical implementation details and provides clear intuition.

Read the section How Layers Are Assigned. Pay close attention to the explanation of why -ln(random()) produces an exponential distribution and how the resulting probabilities create a layered structure where each layer is exponentially sparser than the one below it. The Rust code snippet is a great example of how this is implemented in practice.


The Role of the m_L Parameter

The m_L parameter is not arbitrary. It's directly tied to M, the number of neighbors each node will connect to in a given layer. The original HNSW paper found that an optimal value for m_L is:

This establishes a clear relationship between the density of connections within a layer (M) and the distribution of nodes across layers (m_L). A graph with more connections per node (larger M) will have a smaller m_L, leading to a "flatter" hierarchy with fewer layers. Conversely, a sparser graph (smaller M) will have a larger m_L, creating more layers to ensure the graph remains navigable. This self-tuning property is part of what makes the algorithm so robust.

The video below from MongoDB provides an excellent visual demonstration of how changing m_L affects the distribution of nodes across the layers.

Deep Dive into HNSW Architecture: Solving Problems with MongoDB Vector Search

This video offers a great conceptual visualization of the layer assignment formula and its parameters.

Watch these two segments: First, a breakdown of the layering mechanism, where the formula is explained. Then, watch the explanation of the m_L parameter's effect, which shows plots of the resulting layer distributions for different m_L values.


From Theory to Practice

Let's see what this looks like in a real system. The video by James Briggs not only explains the theory but also dives into the Faiss library's C++ source code to show the random_level function. He then replicates this logic in Python and shows that it produces an almost identical distribution of nodes per layer as the production Faiss library. This is a powerful confirmation that our understanding of the theory directly maps to how high-performance vector search libraries are built.

HNSW for Vector Search Explained and Implemented with Faiss (Python)

This video provides a fantastic, code-level walkthrough of the probabilistic layer assignment as implemented in Facebook's Faiss library.

Please watch the following sections carefully: First, the explanation of the probability function used to build the graph, including the role of the mL level multiplier. Next, observe a practical demonstration in Faiss, where a graph is built and the resulting distribution of vectors per layer is inspected. You can see the exponential decay clearly in the output. Finally, the most insightful part is a deep dive into the code. The presenter shows the actual C++ logic in Faiss, translates it to Python, and proves that the simulation yields the same layer distribution. This is a perfect bridge from theory to implementation.

By assigning layers randomly with this exponentially decaying probability, HNSW achieves two critical goals:

  1. It automatically creates the hierarchical structure needed for efficient O(log N) search.
  2. It ensures the graph is well-balanced. Unlike in NSW where the first few nodes inserted could become over-centralized "hubs," in HNSW any node, regardless of when it was inserted, has the same random chance of reaching a high layer.

Conclusion

In this lesson, we have demystified the process of HNSW layer assignment. You have seen how a simple yet powerful probabilistic formula, directly inspired by skip lists, is used to construct the complex, multi-layered graph that gives HNSW its remarkable performance.

Key Takeaways:

  • Probabilistic Foundation: HNSW borrows the probabilistic layer promotion mechanism from skip lists.
  • The Formula: The assignment is governed by .
  • Exponential Distribution: The -ln(uniform(0,1)) component is key, as it transforms a uniform random number into one following an exponential distribution, making low layers common and high layers rare.
  • Normalization: The m_L parameter, typically set to 1/ln(M), scales the distribution, linking the graph's layer structure to its connection density (M).
  • Balanced Graph: This random assignment at insertion time prevents the formation of overly-important "hub" nodes and leads to a more balanced and robust graph structure.

You now understand how a node decides which layers it belongs to. The next logical question is how we represent this information in memory. In the next lesson, we will design the in-memory data structures for an HNSW graph node, considering how to efficiently store a variable number of connections across multiple layers—a classic low-level systems design challenge.