pub struct NswGraph {
pub m: usize,
pub m_max_0: usize,
pub entry: Option<usize>,
pub entry_level: u8,
pub levels: PersistentVec<u8>,
pub layers: Vec<PersistentVec<Vec<u32>>>,
}Expand description
Multi-layer HNSW graph (v2.13). Each node is assigned a top_level;
it appears in layers 0..=top_level. Higher layers are sparser, so
search starts from the entry at the top layer, greedy-descends to
layer 0, and beam-searches there. Layer 0 keeps a larger neighbour
budget (m_max_0 = 2 * m per the HNSW paper); upper layers cap at
m. The struct name stays NswGraph so external users / on-disk
callers don’t have to track a rename — the algorithm changed, the
data slot didn’t.
Fields§
§m: usizeMax neighbours per node on layers ≥ 1.
m_max_0: usizeMax neighbours on layer 0 (the dense bottom layer). HNSW
convention: m_max_0 = 2 * m.
entry: Option<usize>Entry point — the node that sits on the topmost layer. Search always starts here.
entry_level: u8Top layer of the entry node (== layers.len() - 1 when populated).
levels: PersistentVec<u8>levels[i] = top layer of node i. Nodes whose vector cell is
NULL / non-Vector have levels[i] = 0 and no neighbour entries.
v5.5.0: backed by PersistentVec so NswGraph::clone (and the
Catalog::clone on every group-commit write that contains it) is O(1)
structural-sharing instead of an O(N) element copy.
layers: Vec<PersistentVec<Vec<u32>>>layers[l][i] = neighbours of node i at layer l. Inner vec
is empty when node i doesn’t reach layer l.
v5.5.0: the per-node middle dimension (the O(N) one) is a
PersistentVec; the outer layer dimension stays a plain Vec
(layer count ≤ 8, so its clone is O(1) in practice) and the inner
neighbour list stays a Vec (bounded by m_max_0).
v6.1.x: neighbour slot widened from usize (8 B on 64-bit) to
u32 (4 B). Row indices are catalog-bounded by u32::MAX (4G
rows per table); the cast at the NSW boundary asserts this. At
1M dim-128 SQ8, layer 0 adjacency alone shrinks by ~128 MiB
— the largest single contribution to the v6.0.5-measured
624 MiB ambition gap. On-disk format already used u32 LE, so
this is a pure in-memory layout change; no FILE_VERSION bump.