tqdb

package module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 31, 2026 License: MIT Imports: 12 Imported by: 0

README

tqdb

The quantization-native vector database. Pure Go. Single file. Embeddable.

Store vectors compressed. Search without decompressing. Open in milliseconds.

Built on Google's TurboQuant (ICLR 2026) with Hadamard rotation, HNSW graph indexing, and NEON-accelerated distance kernels.

Install

# Library
go get github.com/scotteveritt/tqdb

# CLI
go install github.com/scotteveritt/tqdb/cmd/tqdb@latest

CLI Quick Start

# Initialize a workspace with an embedding provider
tqdb init --provider ollama

# Import data (JSONL with vectors, or --embed to auto-embed text)
tqdb import --from embeddings.jsonl

# Search by text (embeds via configured provider)
tqdb search "how does authentication work"

# Search with filters
tqdb search "error handling" --top 5 --filter repo=myrepo --filter language=go

# Inspect
tqdb info
tqdb count
tqdb bench --queries 100
tqdb export | head -5

The CLI supports three embedding providers (Vertex AI, OpenAI, Ollama) as lightweight HTTP clients with no SDK dependencies. Configure once via tqdb init or ~/.config/tqdb/config.yaml.

Go Library

import (
    "github.com/scotteveritt/tqdb"
    "github.com/scotteveritt/tqdb/store"
)

// Create and populate a .tq file
s, _ := store.Create("index.tq", tqdb.StoreConfig{
    Dim: 3072, Bits: 8, Rotation: tqdb.RotationHadamard,
})
s.Add(tqdb.Document{
    ID: "doc-1", Content: "hello world", Embedding: vec,
    Data: map[string]any{"repo": "myrepo", "language": "go"},
})
s.Close() // writes the .tq file atomically

// Open (mmap, instant) and search
s, _ = store.Open("index.tq")
defer s.Close()
results := s.SearchWithOptions(query, tqdb.SearchOptions{
    TopK:   10,
    Filter: tqdb.And(tqdb.Eq("repo", "myrepo"), tqdb.Gt("stars", 10.0)),
})
In-Memory Collection with HNSW
coll, _ := store.NewCollection(tqdb.Config{
    Dim: 3072, Bits: 8, Rotation: tqdb.RotationHadamard,
})
coll.Add("id", vec, data)

// Build index (auto-selects IVF for N >= 10K, brute-force otherwise)
coll.CreateIndex(tqdb.IndexConfig{
    FilterFields: []string{"repo", "language"},
    // Type: tqdb.IndexAuto (default), tqdb.IndexIVF, tqdb.IndexHNSW, tqdb.IndexNone
})

How It Works

  1. Normalize the vector to unit length, store the magnitude separately
  2. Rotate via Randomized Walsh-Hadamard Transform (O(d log d), 65 KB memory)
  3. Quantize each coordinate with a Lloyd-Max codebook precomputed from the known Gaussian distribution (no training data needed)
  4. Index via HNSW graph for sub-linear search, or brute-force for small collections
  5. Search by rotating the query once, then traversing the graph with NEON-accelerated distance (no decompression)

The codebook depends only on (dimension, bits), not on your data. This makes quantization data-oblivious: you can add vectors one at a time without retraining.

Benchmarks

All measurements on Apple M4 Pro with NEON acceleration.

Search Performance

d=128, 8-bit:

N Brute-force IVF HNSW
10K 2,495 QPS 19,522 QPS 5,358 QPS
50K 471 QPS 4,222 QPS 2,321 QPS
100K 245 QPS 2,447 QPS 1,648 QPS

d=768, 8-bit:

N Brute-force IVF HNSW
10K 316 QPS 2,474 QPS 843 QPS
50K 60 QPS 723 QPS 518 QPS
100K 32 QPS 432 QPS 468 QPS

IVF wins at most scales. HNSW catches up at 100K+ where O(log N) beats O(sqrt(N)). Auto mode (IndexAuto, the default) selects IVF for N >= 10K, brute-force below.

vs chromem-go (25K vectors, d=3072)
Metric chromem-go tqdb Improvement
Startup 6.2s 10ms 620x
Search 72ms 700 us 103x
Disk 397 MB (25K files) 140 MB (1 file) 2.8x
Recall@10 100% (exact) ~99% (8-bit) -1%
NEON Assembly Acceleration

GoAT-generated ARM64 NEON kernels for distance computation:

Operation Pure Go NEON Speedup
Dot product (f32, d=128) 33.8 ns 8.6 ns 3.9x
Dot product (f32, d=3072) 701 ns 142 ns 5.0x
L2 distance (f32, d=128) 33.6 ns 8.5 ns 4.0x

On x86, pure Go fallbacks are used automatically.

Recall by Bit-Width
Bits Recall@10 (d=3072) Recall@10 (d=128) Compression
4 89% 86% 16x
5 93% 93% 13x
8 ~99% 99% 8x

Default is 8-bit. Indices are bit-packed in the .tq format (4-bit stores 2 indices per byte).

Standard ANN Benchmarks
Dataset Type d N 4-bit Recall@10 8-bit Recall@10
Gemini embeddings Learned 3072 25K 91.9% ~99%
GloVe-100 Learned 100 1.18M 80.8% 96.6%
SIFT-128 SIFT descriptors 128 1M 50.9% 89.3%

TurboQuant works best on modern learned embeddings (Gemini, GloVe, OpenAI).

Features

Filters

Composable filters matching Google Vector Search 2.0 syntax:

tqdb.Eq("repo", "tqdb")
tqdb.In("lang", "go", "rust", "python")
tqdb.Gt("stars", 100.0)
tqdb.And(filter1, filter2)
tqdb.Or(filter1, filter2)
tqdb.Contains("content", "vector")
CRUD
// Collection (in-memory, supports all operations)
coll.Add("id", vec, data)                   // skip if duplicate
coll.Upsert("id", vec, data)               // replace if exists
coll.Delete("id-1", "id-2")
coll.AddDocument(ctx, doc)                  // auto-embed via EmbedFunc
doc, ok := coll.GetByID("id")

// Store (file-backed, write-once)
s.Add(tqdb.Document{ID: "id", Embedding: vec, Content: "...", Data: data})
File Format

The .tq format is a single columnar file, memory-mapped for instant startup:

[Header 64B] [Indices] [Norms] [IDs] [Data] [Contents] [HNSW Graph]

Indices are bit-packed. The HNSW graph section is optional (~134 bytes/node). IDs, metadata, and content are lazily loaded on first access.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompressModelProgress

type CompressModelProgress func(tensorName string, tensorIdx, totalTensors int, rows, totalRows int)

CompressModelProgress reports compression progress. Called from the main goroutine with the current tensor and row counts.

type CompressedProdVector

type CompressedProdVector struct {
	CompressedVector
	Signs        []int8  // QJL sign bits, length = QJL projection dimension
	ResidualNorm float32 // ‖residual‖₂
}

CompressedProdVector adds QJL data for unbiased inner product estimation.

type CompressedVector

type CompressedVector struct {
	Dim     int     // original dimension
	Bits    int     // quantization bits per coordinate
	Norm    float32 // original L2 norm
	Indices []uint8 // quantization indices (one per coordinate, values 0..2^bits-1)
}

CompressedVector is the output of TurboQuantMSE.Quantize(). It stores quantization indices and the original vector norm.

func (*CompressedVector) AppendBinary

func (cv *CompressedVector) AppendBinary(dst []byte) []byte

AppendBinary appends the binary encoding to dst and returns the extended slice. Avoids allocation when dst has sufficient capacity.

func (*CompressedVector) MarshalBinary

func (cv *CompressedVector) MarshalBinary() ([]byte, error)

MarshalBinary encodes a CompressedVector to a compact binary format. Layout: [dim:2][bits:1][norm:4][packed_indices:...]

func (*CompressedVector) Size

func (cv *CompressedVector) Size() int

Size returns the serialized size in bytes.

func (*CompressedVector) UnmarshalBinary

func (cv *CompressedVector) UnmarshalBinary(data []byte) error

UnmarshalBinary decodes a CompressedVector from binary format.

type Config

type Config struct {
	Dim         int          // embedding dimension (required)
	Bits        int          // bits per coordinate: 1-8 (default: 4)
	Seed        uint64       // rotation matrix seed (default: 42)
	Rotation    RotationType // rotation algorithm (default: RotationQR)
	UseExactPDF bool         // use exact Beta PDF for codebook (default: false, uses Gaussian approx)
}

Config controls quantizer behavior.

func (Config) Validate

func (c Config) Validate() error

Validate checks that the Config fields are within valid ranges.

func (Config) WithDefaults

func (c Config) WithDefaults() Config

WithDefaults returns a copy with zero fields filled to defaults.

type Document

type Document struct {
	ID        string         // unique identifier
	Content   string         // original text for RAG (optional)
	Data      map[string]any // typed fields (VS2: data)
	Embedding []float64      // raw embedding (nil = auto-embed via EmbeddingFunc)
}

Document is a data object in a Collection. Data fields are typed (string, float64, bool, []string) matching VS2 DataObject.

type EmbeddingFunc

type EmbeddingFunc func(ctx context.Context, text string) ([]float32, error)

EmbeddingFunc converts text to an embedding vector. Same signature as chromem-go for drop-in compatibility.

type Filter

type Filter interface {
	Match(data map[string]any) bool
}

Filter is a predicate over document data fields. Matches VS2's MongoDB-style filter operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $and, $or, $contains

func And

func And(filters ...Filter) Filter

And returns a filter that matches when ALL sub-filters match (short-circuits on first false).

func Contains

func Contains(field, substr string) Filter

Contains returns a filter that matches when the string field value contains the substring.

func Eq

func Eq(field string, value any) Filter

Eq returns a filter that matches when field == value. Supports string, float64, bool, and int types.

func Gt

func Gt(field string, value float64) Filter

Gt returns a filter that matches when field > value (numeric).

func Gte

func Gte(field string, value float64) Filter

Gte returns a filter that matches when field >= value (numeric).

func In

func In(field string, values ...any) Filter

In returns a filter that matches when the field value is one of the given values.

func Lt

func Lt(field string, value float64) Filter

Lt returns a filter that matches when field < value (numeric).

func Lte

func Lte(field string, value float64) Filter

Lte returns a filter that matches when field <= value (numeric).

func Ne

func Ne(field string, value any) Filter

Ne returns a filter that matches when field != value.

func Nin

func Nin(field string, values ...any) Filter

Nin returns a filter that matches when the field value is NOT one of the given values.

func NotContains

func NotContains(field, substr string) Filter

NotContains returns a filter that matches when the string field value does NOT contain the substring.

func Or

func Or(filters ...Filter) Filter

Or returns a filter that matches when ANY sub-filter matches (short-circuits on first true).

type IndexConfig

type IndexConfig struct {
	FilterFields   []string  // fields to build inverted indexes on
	Type           IndexType // index algorithm: IndexIVF (default) or IndexHNSW
	NumPartitions  int       // IVF: partitions (0 = auto √N)
	NProbe         int       // IVF: partitions to search per query (0 = auto √NumPartitions)
	SkipIVF        bool      // IVF: if true, only build filter indexes
	M              int       // HNSW: max edges per layer (default 16)
	EfConstruction int       // HNSW: build-time beam width (default 200)
}

IndexConfig controls index creation (VS2-aligned).

type IndexType added in v0.4.1

type IndexType int

IndexType selects the ANN index algorithm.

const (
	IndexAuto IndexType = iota // Auto-select based on N and d (default)
	IndexIVF                   // ScaNN-style IVF partitioning
	IndexHNSW                  // Hierarchical Navigable Small World graph
	IndexNone                  // No ANN index (brute-force only, filter indexes still built)
)

type KVCacheConfig

type KVCacheConfig struct {
	Layers      int          // number of transformer layers
	Heads       int          // number of attention heads
	HeadDim     int          // dimension per head (typically 64 or 128)
	Bits        int          // quantization bits per coordinate (default: 4)
	PackIndices bool         // bit-pack indices in storage (default: false)
	Rotation    RotationType // rotation algorithm (default: RotationHadamard)
	Seed        uint64       // rotation seed (default: 42)
}

KVCacheConfig controls KV cache creation.

type ModelConfig

type ModelConfig struct {
	Bits     int          // bits per coordinate (default: 4)
	Rotation RotationType // rotation algorithm (default: RotationHadamard)
	Seed     uint64       // rotation seed (default: 42)
	Workers  int          // concurrent workers per tensor (default: GOMAXPROCS)
}

ModelConfig controls model weight quantization.

type QueryOptions

type QueryOptions struct {
	PageSize int    // max results (default: 100)
	Filter   Filter // required
}

QueryOptions controls filter-only retrieval (VS2: QueryDataObjectsRequest).

type Result

type Result struct {
	ID      string
	Score   float64 // inner product similarity (≈ cosine sim for unit-normalized vectors)
	Content string
	Data    map[string]any
}

Result represents a search result from a Collection or Store.

type RotationType

type RotationType int

RotationType selects the orthogonal rotation algorithm.

const (
	// RotationQR uses a Haar-random orthogonal matrix via QR decomposition.
	// Memory: O(d²). Compute: O(d²) per vector. The paper's original approach.
	RotationQR RotationType = iota

	// RotationHadamard uses the Randomized Walsh-Hadamard Transform (D₂·H̃·D₁).
	// Memory: O(d). Compute: O(d log d) per vector.
	// Empirically better quality than QR (QuaRot, ICLR 2024).
	RotationHadamard
)

type SafeTensorsFile

type SafeTensorsFile struct {
	// contains filtered or unexported fields
}

SafeTensorsFile provides read access to a SafeTensors (.safetensors) file. The file is memory-mapped for zero-copy access to tensor data.

func OpenSafeTensors

func OpenSafeTensors(path string) (*SafeTensorsFile, error)

OpenSafeTensors opens a SafeTensors file for reading.

func (*SafeTensorsFile) Close

func (sf *SafeTensorsFile) Close() error

Close releases the mmap.

func (*SafeTensorsFile) Metadata

func (sf *SafeTensorsFile) Metadata() map[string]string

Metadata returns the __metadata__ map (may be nil).

func (*SafeTensorsFile) NumTensors

func (sf *SafeTensorsFile) NumTensors() int

NumTensors returns the total number of tensors.

func (*SafeTensorsFile) ReadRowFloat64

func (sf *SafeTensorsFile) ReadRowFloat64(name string, row int) ([]float64, error)

ReadRowFloat64 reads a single row of a tensor as float64. For 2D tensors, row is the first-dimension index. Supports F32, F16, BF16 dtypes.

func (*SafeTensorsFile) Tensor

func (sf *SafeTensorsFile) Tensor(name string) (TensorInfo, bool)

Tensor returns info about a named tensor.

func (*SafeTensorsFile) TensorBytes

func (sf *SafeTensorsFile) TensorBytes(name string) ([]byte, error)

TensorBytes returns the raw bytes for a tensor.

func (*SafeTensorsFile) TensorNames

func (sf *SafeTensorsFile) TensorNames() []string

TensorNames returns all tensor names, sorted alphabetically.

type SearchOptions

type SearchOptions struct {
	TopK     int     // max results (default: 10)
	MinScore float64 // minimum similarity threshold (0 = no filter)
	Offset   int     // skip first N results (pagination)
	Filter   Filter  // data field filter
	Rescore  int     // rescore top-N with exact dequantized distance (0 = disabled, recommended: 3×TopK)
	Ef       int     // HNSW: search beam width (0 = auto, higher = better recall, slower)
}

SearchOptions controls vector similarity search (VS2: SearchDataObjectsRequest).

type StoreConfig

type StoreConfig struct {
	Dim         int          // embedding dimension (required)
	Bits        int          // bits per coordinate: 1-8 (default: 4)
	Rotation    RotationType // rotation algorithm (default: RotationQR)
	Seed        uint64       // rotation matrix seed (default: 42)
	UseExactPDF bool         // use exact Beta PDF for codebook
}

StoreConfig controls store creation.

func (StoreConfig) ToConfig

func (c StoreConfig) ToConfig() Config

ToConfig converts StoreConfig to a quantizer Config.

func (StoreConfig) WithDefaults

func (c StoreConfig) WithDefaults() StoreConfig

WithDefaults returns a copy with zero fields filled to defaults.

type StoreInfo

type StoreInfo struct {
	Path        string
	Dim         int
	WorkDim     int
	Bits        int
	Rotation    RotationType
	Seed        uint64
	NumVecs     int
	FileSize    int64
	IndexBytes  int64
	Compression float64 // ratio vs float32
}

StoreInfo contains statistics about a store.

type TQMTensorInfo

type TQMTensorInfo struct {
	Shape     []int64 `json:"shape"`
	OrigDType string  `json:"orig_dtype"`
	Rows      int     `json:"rows"`
	RowDim    int     `json:"row_dim"`
	WorkDim   int     `json:"work_dim"`
	Offset    int64   `json:"offset"`
	Size      int64   `json:"size"`
	AvgCosSim float64 `json:"avg_cos_sim"`
}

TQMTensorInfo describes a quantized tensor in a .tqm file.

type TQModelHeader

type TQModelHeader struct {
	Version  int                      `json:"version"`
	Source   string                   `json:"source,omitempty"`
	Bits     int                      `json:"bits"`
	Packed   bool                     `json:"packed"`
	Rotation string                   `json:"rotation"`
	Seed     uint64                   `json:"seed"`
	Tensors  map[string]TQMTensorInfo `json:"tensors"`
}

TQModelHeader is the JSON header of a .tqm file.

type TensorInfo

type TensorInfo struct {
	Name        string
	DType       string   `json:"dtype"`
	Shape       []int64  `json:"shape"`
	DataOffsets [2]int64 `json:"data_offsets"`
}

TensorInfo describes a single tensor in a SafeTensors file.

func (TensorInfo) BytesPerElement

func (ti TensorInfo) BytesPerElement() int

BytesPerElement returns the size of one element for the tensor's dtype.

func (TensorInfo) Cols

func (ti TensorInfo) Cols() int

Cols returns the row dimension (last dimension) for a tensor.

func (TensorInfo) NumElements

func (ti TensorInfo) NumElements() int64

NumElements returns the total number of elements in a tensor.

func (TensorInfo) Rows

func (ti TensorInfo) Rows() int

Rows returns the number of rows (first dimension) for a 2D tensor. Returns 1 for 1D tensors, 0 for empty tensors.

Directories

Path Synopsis
bench
cmd/tqdb-annbench command
tqdb-annbench: Standard ANN benchmark harness.
tqdb-annbench: Standard ANN benchmark harness.
cmd
tqdb command
tqdb-bench command
tqdb-bench: Comprehensive comparison benchmark between chromem-go (float32) and tqdb (4-bit quantized) search quality and performance.
tqdb-bench: Comprehensive comparison benchmark between chromem-go (float32) and tqdb (4-bit quantized) search quality and performance.
tqdb-debug-recall command
tqdb-debug-recall: Diagnose why recall@10 doesn't match paper expectations.
tqdb-debug-recall: Diagnose why recall@10 doesn't match paper expectations.
tqdb-prod-bench command
tqdb-prod-bench: Compare MSE-only vs Prod (MSE+QJL) recall and latency.
tqdb-prod-bench: Compare MSE-only vs Prod (MSE+QJL) recall and latency.
tqdb-recall command
tqdb-recall: Measure recall vs latency tradeoffs for IVF tuning.
tqdb-recall: Measure recall vs latency tradeoffs for IVF tuning.
tqdb-test command
tqdb-test: End-to-end integration test + benchmark against live chromem-go data.
tqdb-test: End-to-end integration test + benchmark against live chromem-go data.
internal
distancer
Package distancer provides SIMD-accelerated distance functions.
Package distancer provides SIMD-accelerated distance functions.
embed
Package embed provides lightweight embedding provider clients.
Package embed provides lightweight embedding provider clients.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL