# Quantization & Sparse Vectors

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

---

LLMS index: [llms.txt](/llms.txt)

---

`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 + 8\) bytes |
| `halfvec(n)` | 16-bit float (`float16`) | 4,000 | \(2n + 8\) bytes |
| `bit(n)` | 1-bit binary (`0` / `1`) | 64,000 | \(\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 \times \text{nnz} + 16\) bytes |

> [!TIP] RaBitQ Quantization in `vchord`
> In addition to `pgvector`'s built-in quantization types, [`vchord`](/docs/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](/docs/vchord/usage/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`:

```sql
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`:

```sql
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**:

```sql
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` otherwise) in an expression index, then re-rank the top candidates using the exact `vector` column:

```sql
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`](/docs/bestmatch/)).

### Sparse Literal Format

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

```sql
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:

```sql
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;
```
