Skip to content

Quantization & Sparse Vectors

Half-precision vectors (halfvec), binary quantization (bit), sparse vectors (sparsevec), and Matryoshka sub-vector indexing in pgvector.

pgvector supports four vector representations so you can balance memory footprint, dimensionality limits, and recall:

Data Type Element Type Max Indexed Dimensions Storage Size
vector(n) 32-bit float (float32) 2,000 4n+84n + 8 bytes
halfvec(n) 16-bit float (float16) 4,000 2n+82n + 8 bytes
bit(n) 1-bit binary (0 / 1) 64,000 ⌈n/8⌉+8\lceil n / 8 \rceil + 8 bytes
sparsevec(n) Non-zero index:float32 pairs 1,000 non-zero elements (up to 1,000,000 dims) 8×nnz+168 \times \text{nnz} + 16 bytes
RaBitQ Quantization in vchord

In addition to pgvector’s built-in quantization types, vchord provides rabitq8 (8-bit) and rabitq4 (4-bit) quantized vector types and supports indexing up to 60,000 dimensions in vchordrq. See vchord Quantization Types.


Half-Precision Vectors (halfvec)

Use the halfvec type (pgvector 0.7.0+) to store 16-bit floating-point vectors, cutting table and index footprint in half compared to vector:

CREATE TABLE items (
    id bigserial PRIMARY KEY,
    embedding halfvec(3)
);

CREATE INDEX ON items USING hnsw (embedding halfvec_l2_ops);

Half-Precision Expression Indexing (Scalar Quantization)

If you want to keep full-precision vector values in the table (or index vectors between 2,001 and 4,000 dimensions, such as OpenAI text-embedding-3-large at 3,072 dimensions), build an expression index casting vector to halfvec:

CREATE TABLE items (
    id bigserial PRIMARY KEY,
    embedding vector(3072)
);

CREATE INDEX ON items USING hnsw ((embedding::halfvec(3072)) halfvec_l2_ops);

-- Query using the exact expression from the index
SELECT * FROM items
ORDER BY embedding::halfvec(3072) <-> '[1,2,3,...]'
LIMIT 5;

Binary Vectors (bit) & Binary Quantization

Use PostgreSQL’s built-in bit(n) type with pgvector’s Hamming (<~>) and Jaccard (<%>) operators to store and index binary vectors up to 64,000 bits:

CREATE TABLE items (
    id bigserial PRIMARY KEY,
    embedding bit(3)
);

INSERT INTO items (embedding) VALUES ('000'), ('111');

-- Nearest neighbors by Hamming distance
SELECT * FROM items ORDER BY embedding <~> '101' LIMIT 5;

-- Index with HNSW
CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);

Binary Quantization with Full-Precision Re-Ranking

Use binary_quantize(vector) to compress 32-bit floats to 1 bit per dimension (1 if >0> 0, 0 otherwise) in an expression index, then re-rank the top candidates using the exact vector column:

CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);

SELECT * FROM (
    SELECT * FROM items
    ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]')
    LIMIT 20
) AS candidates
ORDER BY embedding <=> '[1,-2,3]'
LIMIT 5;

Sparse Vectors (sparsevec)

Use sparsevec (pgvector 0.7.0+) to store vectors where most dimensions are zero (such as SPLADE, BGE-M3 sparse outputs, or BM25 term vectors generated by pg_bestmatch).

Sparse Literal Format

Format: '{index1:value1,index2:value2}/dimensions', where indices are 1-based:

CREATE TABLE items (
    id bigserial PRIMARY KEY,
    embedding sparsevec(5)
);

INSERT INTO items (embedding) VALUES ('{1:1,3:2,5:3}/5'), ('{1:4,3:5,5:6}/5');

-- Query nearest neighbors
SELECT * FROM items ORDER BY embedding <-> '{1:3,3:1,5:2}/5' LIMIT 5;

-- Create an HNSW index (up to 1,000 non-zero elements)
CREATE INDEX ON items USING hnsw (embedding sparsevec_l2_ops);

Matryoshka Sub-Vector Indexing (subvector)

For models trained with Matryoshka Representation Learning (MRL), you can index only the leading dimensions with subvector(vector, start, count) and re-rank on the full vector:

CREATE INDEX ON items USING hnsw ((subvector(embedding, 1, 512)::vector(512)) vector_cosine_ops);

SELECT * FROM (
    SELECT * FROM items
    ORDER BY subvector(embedding, 1, 512)::vector(512) <=> subvector('[1,2,3,...]'::vector, 1, 512)
    LIMIT 20
) AS candidates
ORDER BY embedding <=> '[1,2,3,...]'
LIMIT 5;