Documentation
¶
Overview ¶
Package diskann implements a pure Go DiskANN (Vamana) index for disk-resident approximate nearest neighbor search on billion-scale datasets.
Key design: graph topology + PQ codes reside in RAM (~100 bytes/vector), while raw embeddings live on disk (mmap) and are fetched only for final reranking. This gives 8-16x RAM reduction compared to in-memory HNSW.
Algorithm reference: Subramanya et al., "DiskANN: Fast Accurate Billion-point Nearest Neighbor Search on a Single Node", NeurIPS 2019.
Index ¶
- Constants
- func ADCDistance(table [][]float32, codes []byte) float32
- func CosineDistance(a, b []float32) float32
- func L2DistanceSquared(a, b []float32) float32
- type BackendBuilder
- type BackendSearcher
- type Builder
- func (b *Builder) AddVectors(ctx context.Context, indexData []byte, embeddings [][]float32, startID int64) ([]byte, error)
- func (b *Builder) Build(ctx context.Context, embeddings [][]float32) ([]byte, error)
- func (b *Builder) RemoveVectors(ctx context.Context, indexData []byte, ids []int64) ([]byte, error)
- type Candidate
- type Config
- type DiskANNConfig
- type DiskIndex
- type DistanceFunc
- type DistanceMetric
- type EmbeddingRecomputer
- type Factory
- type IndexMeta
- type PQCodebook
- type Searcher
- func (s *Searcher) Close() error
- func (s *Searcher) Load(ctx context.Context, indexData []byte, meta IndexMeta) error
- func (s *Searcher) Search(ctx context.Context, query []float32, topK int) ([]int64, []float32, error)
- func (s *Searcher) SearchWithRecompute(ctx context.Context, query []float32, topK int, recompute EmbeddingRecomputer) ([]int64, []float32, error)
- type VamanaGraph
- func (g *VamanaGraph) AllNodeIDs() []int64
- func (g *VamanaGraph) Build(embeddings [][]float32)
- func (g *VamanaGraph) GetNeighbors(id int64) []int64
- func (g *VamanaGraph) GetVector(id int64) []float32
- func (g *VamanaGraph) Medoid() int64
- func (g *VamanaGraph) Search(query []float32, topK, l int) []Candidate
- func (g *VamanaGraph) Size() int
Constants ¶
const DiskANNMagic uint32 = 0x444B414E // "DKAN"
DiskANNMagic identifies a gleann DiskANN index file.
const DiskANNVersion uint32 = 1
DiskANNVersion is the current format version.
Variables ¶
This section is empty.
Functions ¶
func ADCDistance ¶
ADCDistance computes the approximate distance from a query to a PQ-encoded vector using a precomputed distance table. O(M) operations.
func CosineDistance ¶
CosineDistance computes 1 - cosine_similarity.
func L2DistanceSquared ¶
L2DistanceSquared computes the squared Euclidean distance.
Types ¶
type BackendBuilder ¶
type BackendBuilder interface {
Build(ctx context.Context, embeddings [][]float32) ([]byte, error)
AddVectors(ctx context.Context, indexData []byte, embeddings [][]float32, startID int64) ([]byte, error)
RemoveVectors(ctx context.Context, indexData []byte, ids []int64) ([]byte, error)
}
BackendBuilder constructs and serializes an index.
type BackendSearcher ¶
type BackendSearcher interface {
Load(ctx context.Context, indexData []byte, meta IndexMeta) error
Search(ctx context.Context, query []float32, topK int) ([]int64, []float32, error)
SearchWithRecompute(ctx context.Context, query []float32, topK int, recompute EmbeddingRecomputer) ([]int64, []float32, error)
Close() error
}
BackendSearcher loads and queries an index.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder builds DiskANN indexes.
func (*Builder) AddVectors ¶
type Candidate ¶
Candidate is a (id, distance) pair.
func ADCDistanceSorted ¶
ADCDistanceSorted computes ADC distances for multiple candidates and returns them sorted. Used for PQ-based candidate prefiltering.
func SearchDiskIndex ¶
func SearchDiskIndex(idx *DiskIndex, query []float32, topK, searchL, rerankK int, distFunc DistanceFunc, getVector func(int64) []float32) []Candidate
SearchDiskIndex performs DiskANN's beam search with PQ prefiltering.
Algorithm:
- Beam search on graph using PQ approximate distances (cheap, RAM-only).
- Collect top-L candidates by PQ distance.
- Rerank top candidates using exact distances (expensive, disk read).
Parameters:
- query: the query vector
- topK: number of results to return
- searchL: candidate list size (beam width)
- rerankK: how many PQ candidates to rerank (0 = 2*topK)
- getVector: callback to fetch raw embedding (from mmap/disk/recompute)
func SearchExact ¶
func SearchExact(idx *DiskIndex, query []float32, topK, searchL int, distFunc DistanceFunc) []Candidate
SearchExact performs beam search with exact distances (all embeddings in RAM). This is used when all embeddings fit in memory (small datasets or testing).
type Config ¶
type Config struct {
IndexDir string `json:"index_dir"`
Backend string `json:"backend"`
DiskANNConfig DiskANNConfig `json:"diskann_config,omitempty"`
}
Config is a minimal standalone config (mirrors gleann.Config pattern).
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
type DiskANNConfig ¶
type DiskANNConfig struct {
// R is the max out-degree per node (analogous to HNSW M).
// Default: 64.
R int `json:"r"`
// L is the candidate list size during build (analogous to efConstruction).
// Default: 100.
L int `json:"l"`
// Alpha controls the robust prune diversity parameter.
// α=1.0 = pure distance, α>1.0 = more diversity. Default: 1.2.
Alpha float64 `json:"alpha"`
// PQDim is the number of PQ sub-quantizer dimensions.
// Each sub-quantizer encodes (dims/PQDim) dimensions into 1 byte.
// Default: 0 (auto = dims/4, clamped to [1, dims]).
PQDim int `json:"pq_dim"`
// PQCentroids is the number of centroids per sub-quantizer.
// Must be ≤ 256 (stored as uint8). Default: 256.
PQCentroids int `json:"pq_centroids"`
// SearchL is the candidate list size during search.
// Default: 100.
SearchL int `json:"search_l"`
// SearchPQRerank is how many PQ candidates to rerank with full vectors.
// Default: 2 * TopK.
SearchPQRerank int `json:"search_pq_rerank"`
// DistanceMetric selects distance function. Default: "l2".
DistanceMetric DistanceMetric `json:"distance_metric,omitempty"`
// UseMmap enables memory-mapped search for raw vectors.
UseMmap bool `json:"use_mmap,omitempty"`
}
DiskANNConfig holds build and search parameters.
func (*DiskANNConfig) Defaults ¶
func (c *DiskANNConfig) Defaults(dims int)
Defaults fills zero-valued fields with sensible defaults.
type DiskIndex ¶
type DiskIndex struct {
// Header
NumNodes int64
Dims int
R int // max out-degree
Medoid int64
PQM int // number of sub-quantizers
PQK int // centroids per sub-quantizer
PQSubDim int // sub-vector dimensionality
// Graph topology: neighbors[i] has neighborOffsets[i]..neighborOffsets[i+1] entries.
NeighborOffsets []int64
Neighbors []int64
// PQ data
Codebook *PQCodebook
PQCodes [][]byte // PQCodes[nodeIdx] = M-byte code
// Raw embeddings (all in-memory for Build; mmap'd for search).
Embeddings [][]float32
}
DiskIndex is the serialized on-disk format for a DiskANN index. It combines: Vamana graph topology + PQ codebook + PQ codes + raw embeddings.
Memory layout when loaded:
- Graph neighbors (flat arrays): ~R*4 bytes/node (RAM)
- PQ codes: M bytes/node (RAM)
- PQ codebook: M*K*SubDim*4 bytes (RAM)
- Raw embeddings: dims*4 bytes/node (disk/mmap)
Total RAM: ~(R*4 + M) bytes/node. For R=64, M=32: ~288 bytes/node. vs HNSW in-memory: ~(M*2*4 + dims*4) bytes/node. For M=32, dims=128: ~768 bytes/node.
func BuildDiskIndex ¶
func BuildDiskIndex(embeddings [][]float32, cfg DiskANNConfig) (*DiskIndex, error)
BuildDiskIndex constructs a full DiskANN index from embeddings.
func ReadDiskIndex ¶
ReadDiskIndex deserializes a DiskIndex from a reader.
func (*DiskIndex) GetNeighbors ¶
GetNeighbors returns the neighbors of node i from the flat arrays.
func (*DiskIndex) WriteTo ¶
WriteTo serializes the DiskIndex to a writer. Format:
[magic:4][version:4][numNodes:8][dims:4][R:4][medoid:8] [pqM:4][pqK:4][pqSubDim:4] [neighborOffsets: (N+1)*8 bytes] [neighbors: totalEdges*8 bytes] [PQ centroids: M*K*SubDim*4 bytes] [PQ codes: N*M bytes] [raw embeddings: N*dims*4 bytes]
type DistanceFunc ¶
DistanceFunc computes distance between two vectors. Lower values = more similar.
func GetDistanceFunc ¶
func GetDistanceFunc(metric string) DistanceFunc
GetDistanceFunc returns a DistanceFunc for the given metric name.
type DistanceMetric ¶
type DistanceMetric string
DistanceMetric selects the distance function.
const ( DistanceL2 DistanceMetric = "l2" DistanceCosine DistanceMetric = "cosine" )
type EmbeddingRecomputer ¶
EmbeddingRecomputer retrieves embeddings for given IDs from storage.
type Factory ¶
type Factory struct{}
Factory creates DiskANN builders and searchers.
func (*Factory) NewBuilder ¶
func (f *Factory) NewBuilder(config Config) BackendBuilder
func (*Factory) NewSearcher ¶
func (f *Factory) NewSearcher(config Config) BackendSearcher
type IndexMeta ¶
type IndexMeta struct {
Name string `json:"name"`
Backend string `json:"backend"`
EmbeddingModel string `json:"embedding_model"`
Dimensions int `json:"dimensions"`
NumPassages int `json:"num_passages"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Version string `json:"version"`
}
IndexMeta stores metadata about a built index.
type PQCodebook ¶
type PQCodebook struct {
// M is the number of sub-quantizers.
M int
// K is the number of centroids per sub-quantizer.
K int
// SubDim is the dimensionality of each sub-vector.
SubDim int
// Dims is the original vector dimensionality (M * SubDim).
Dims int
// Centroids[m][k] is the k-th centroid for the m-th sub-quantizer.
// Shape: [M][K][SubDim]
Centroids [][][]float32
}
PQCodebook implements Product Quantization for approximate distance computation. It splits D-dimensional vectors into M sub-vectors, each quantized independently using k-means with K centroids. A vector is encoded as M bytes (when K≤256).
Memory: M*K*subDim float32 centroids (~256KB for M=32, K=256, subDim=4). Encoded vector: M bytes (vs D*4 bytes raw).
func TrainPQ ¶
func TrainPQ(vectors [][]float32, m, k int) (*PQCodebook, error)
TrainPQ trains a PQ codebook from training vectors using k-means.
func (*PQCodebook) BuildDistanceTable ¶
func (pq *PQCodebook) BuildDistanceTable(query []float32, distFunc DistanceFunc) [][]float32
BuildDistanceTable precomputes distances from a query to all centroids. Returns table[m][k] = distance from query sub-vector m to centroid k. This enables Asymmetric Distance Computation (ADC): O(M) per candidate.
func (*PQCodebook) Encode ¶
func (pq *PQCodebook) Encode(vec []float32) []byte
Encode encodes a vector into M PQ codes (one byte per sub-quantizer).
func (*PQCodebook) EncodeAll ¶
func (pq *PQCodebook) EncodeAll(vectors [][]float32) [][]byte
EncodeAll encodes multiple vectors.
type Searcher ¶
type Searcher struct {
// contains filtered or unexported fields
}
Searcher loads and queries a DiskANN index.
type VamanaGraph ¶
type VamanaGraph struct {
// contains filtered or unexported fields
}
VamanaGraph is a single-level navigable graph built with the Vamana algorithm. Unlike HNSW's multi-level structure, Vamana uses one flat graph with robust pruning (α-parameter) for diversity-aware neighbor selection.
func NewVamanaGraph ¶
func NewVamanaGraph(r, l, dims int, alpha float64, distFunc DistanceFunc) *VamanaGraph
NewVamanaGraph creates an empty Vamana graph.
func (*VamanaGraph) AllNodeIDs ¶
func (g *VamanaGraph) AllNodeIDs() []int64
AllNodeIDs returns all node IDs in insertion order.
func (*VamanaGraph) Build ¶
func (g *VamanaGraph) Build(embeddings [][]float32)
Build constructs the Vamana graph from a batch of embeddings. This is the standard two-pass Vamana algorithm: Pass 1: random-order insertion with α=1.0 (greedy) Pass 2: re-insertion with α>1.0 (diverse)
func (*VamanaGraph) GetNeighbors ¶
func (g *VamanaGraph) GetNeighbors(id int64) []int64
GetNeighbors returns the neighbors of a node.
func (*VamanaGraph) GetVector ¶
func (g *VamanaGraph) GetVector(id int64) []float32
GetVector returns the vector for a node.
func (*VamanaGraph) Medoid ¶
func (g *VamanaGraph) Medoid() int64
Medoid returns the entry point ID.