Skip to content

Commit f93676f

Browse files
committed
docs: add VASQ-4 to README, docs, and create roadmap page
README: - Add VASQ-4 to features, programmatic API example, configuration table - Update architecture differentiators and compression highlights - Update roadmap: VASQ-4 done, add pending items (#2-#6) Docs: - vasq-deep-dive.md: add full VASQ-4 section (memory layout, calibration, SIMD kernel, usage tabs, expected recall table) - quantization-comparison.md: update Spector approach, tables, recall tiers - roadmap.md: new detailed roadmap page covering all 10 items with status, projected savings, and implementation scope - mkdocs.yml: add Roadmap to nav
1 parent 6e1dd60 commit f93676f

6 files changed

Lines changed: 391 additions & 18 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ Desktop.ini
2929
dependency-reduced-pom.xml
3030
buildNumber.properties
3131
.mvn/timing.properties
32+
.mvn/maven.config
3233
.mvn/wrapper/maven-wrapper.jar
34+
.mvn
3335

3436
# ──────────── Logs ────────────
3537
*.log

README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- **⚡ Sub-Millisecond Queries** — Branchless SIMD kernels with masked tail handling
1717
- **🗜️ Multi-Level Quantization** — INT8 (4×), INT4 (8×), and INT2 (16×) scalar quantization with non-uniform calibration and configurable rescore
1818
- **🗜️ VASQ Quantization** — FWHT-rotated affine INT8 quantization with exact-norm header for high-accuracy zero-copy compression (retaining 99.5%+ recall)
19+
- **🗜️ VASQ-4 Quantization** — INT4 nibble-packed variant of VASQ achieving 6–8× compression vs float32 with 97–99% recall (with 3× rescore)
1920
- **🎯 SpectorIndex (IVF-HNSW-VASQ)** — Multi-level adaptive vector index yielding 99.5%–100% recall on real text embeddings at aggressive 3% partition scanning rates
2021
- **🗜️ IVF-PQ Index** — Inverted file with product quantization for 32× memory compression at billion scale
2122
- **🤖 LLM Re-ranking** — Listwise relevance scoring via Ollama for precision-critical retrieval
@@ -177,6 +178,22 @@ try (var engine = new SpectorEngine(config)) {
177178
}
178179
```
179180

181+
### VASQ-4 Quantization (6–8× Compression)
182+
183+
```java
184+
// Fluent builder with VASQ-4 quantization
185+
var engine = SpectorEngine.builder()
186+
.dimensions(4096) // e.g., qwen3-embedding
187+
.capacity(500_000)
188+
.vasq4() // INT4 FWHT-rotated, 3× rescore default
189+
.build();
190+
191+
// Or with explicit oversampling
192+
var config = SpectorConfig.DEFAULT
193+
.withDimensions(768)
194+
.withVasq4(5); // 5× oversampling for higher recall
195+
```
196+
180197
## ⚙️ Configuration
181198

182199
| Parameter | Default | Description |
@@ -191,7 +208,7 @@ try (var engine = new SpectorEngine(config)) {
191208
| `b` | 0.75 | BM25 document length normalization |
192209
| `RRF k` | 60 | Reciprocal Rank Fusion constant |
193210
| `gpuEnabled` | false | Enable CUDA GPU acceleration |
194-
| `quantization` | NONE | Quantization type: NONE, SCALAR_INT8, SCALAR_INT4, SCALAR_INT2 |
211+
| `quantization` | NONE | Quantization type: NONE, SCALAR_INT8, SCALAR_INT4, SCALAR_INT2, VASQ, VASQ_4 |
195212
| `oversamplingFactor` | auto | Rescore oversampling (INT4→3, INT2→5, INT8→1). Higher = better recall |
196213
| `rerankerEnabled` | false | Enable LLM re-ranking via Ollama |
197214
| `rerankerModel` || Ollama model name (e.g., "llama3.2") |
@@ -319,7 +336,7 @@ All comparisons below use **100K documents, 128 dimensions, top-10 retrieval** a
319336
| **Off-Heap Vectors** | ✅ Panama MemorySegment | ✅ Lucene MMapDir | ✅ MMapDir | ❌ Heap-only | ✅ Mmap | ✅ Mmap |
320337
| **Virtual Threads** | ✅ Native Loom | ❌ Platform threads | N/A | N/A | N/A | N/A |
321338
| **Zero Dependencies** | ✅ JDK only | ❌ Heavy stack | ✅ Standalone | ✅ Header-only | ❌ Tokio runtime | ❌ etcd, MinIO, Pulsar |
322-
| **Quantization** | ✅ Scalar INT8/INT4/INT2 + PQ | ✅ BBQ/Scalar | ✅ Scalar | ❌ None | ✅ Scalar/Binary | ✅ PQ/SQ |
339+
| **Quantization** | ✅ Scalar INT8/INT4/INT2 + VASQ/VASQ-4 + PQ | ✅ BBQ/Scalar | ✅ Scalar | ❌ None | ✅ Scalar/Binary | ✅ PQ/SQ |
323340
| **Disk-based Index** | ✅ HNSW serialization | ✅ Segment merge | ✅ MMap | ❌ In-memory | ✅ On-disk HNSW | ✅ DiskANN |
324341
| **IVF-PQ** | ✅ 32× compression | ❌ None | ❌ None | ❌ None | ❌ None | ✅ IVF_PQ |
325342
| **GPU Acceleration** | ✅ CUDA (Panama FFM) | ❌ None | ❌ None | ❌ None | ❌ None | ✅ GPU |
@@ -334,7 +351,7 @@ All comparisons below use **100K documents, 128 dimensions, top-10 retrieval** a
334351
- **📦 Zero-dependency embedded**: Drop-in JAR, no external infrastructure needed
335352
- **⚡ 7.6K+ ops/sec concurrent**: 7,635 hybrid searches/sec at 16 threads (128-dim)
336353
- **🎯 23K+ vector QPS**: 23,726 vector queries/sec at 10K docs
337-
- **🗜️ IVF-PQ + VASQ + TurboQuant**: 8–32× memory reduction for large-scale datasets with high-accuracy calibration
354+
- **🗜️ IVF-PQ + VASQ + VASQ-4 + TurboQuant**: 6–32× memory reduction for large-scale datasets with high-accuracy calibration
338355
- **🔬 99.5%+ Recall**: IVF-HNSW-VASQ (`SpectorIndex`) achieves near-perfect recall on real semantic embeddings scanning just 3% of the clusters
339356
- **🤖 LLM re-ranking**: Listwise Ollama-powered relevance scoring
340357
- **🖥️ GPU acceleration**: CUDA kernel launcher + SIMD batch similarity via Panama FFM
@@ -378,11 +395,19 @@ All comparisons below use **100K documents, 128 dimensions, top-10 retrieval** a
378395
- [x] Document deletion
379396
- [x] Auto-embed + bulk ingest endpoints
380397
- [x] gRPC TLS support
398+
- [x] VASQ-4 quantization (FWHT-rotated INT4, nibble-packed — 6–8× compression vs float32)
399+
- [ ] Padding-aware storage (skip zero-padded dims — 25% savings for non-pow2 dimensions)
400+
- [ ] Norm header compression (float32 → float16 — 2 bytes/vector savings)
401+
- [ ] VASQ-PQ hybrid (FWHT rotation + product quantization — 16–32× compression)
402+
- [ ] Flat-mode VASQ (VASQ compression of flat-shard residuals — 3× on flat shards)
403+
- [ ] Adaptive bit-width VASQ (per-dimension variable bit allocation)
381404
- [ ] GPU kernel dispatch (CUDA compute for batch similarity — requires CUDA Toolkit)
382405
- [ ] NPU acceleration (Intel/AMD NPU for INT8 batch operations via OpenVINO or DirectML)
383406
- [ ] WASM runtime for edge deployment
384407
- [ ] Structured concurrency (JEP 462) for fan-out search with cancellation propagation
385408

409+
> See the [detailed Roadmap](docs/docs/roadmap.md) for in-depth descriptions, projected savings, and implementation plans.
410+
386411
## 🤝 Contributing
387412

388413
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

docs/docs/deep-dives/quantization-comparison.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ DiskBBQ (introduced experimentally) adds IVF-like partitioning on top of BBQ:
7777

7878
---
7979

80-
## 🔵 Spector's Approach: Scalar INT8 + IVF-PQ
80+
## 🔵 Spector's Approach: Scalar + VASQ + VASQ-4 + IVF-PQ
8181

8282
### Why These Two?
8383

@@ -88,7 +88,9 @@ The two-method strategy covers the full spectrum:
8888
| Need | Solution | Compression | Recall |
8989
|------|----------|-------------|--------|
9090
| Quality-first (≤50M vectors) | Scalar INT8 || 95–99% |
91+
| Quality + rotation (≤50M) | **VASQ INT8** || **97–99.5%** |
9192
| Balanced (10M–100M vectors) | Scalar INT4 || 85–95% |
93+
| Balanced + rotation (10M–100M) | **VASQ-4** | **6–8×** | **95–99%** |
9294
| Memory-constrained (50M–500M) | Scalar INT2 | 16× | 75–90% |
9395
| Scale-first (100M–1B+ vectors) | IVF-PQ | 32× | 75–90% |
9496

@@ -231,7 +233,7 @@ Which quantization methods are available in each engine:
231233

232234
| Engine | Scalar INT8 | Scalar INT4/INT2 | Binary | Product Quantization | IVF-PQ | DiskANN | Rescoring |
233235
|--------|:-----------:|:----------------:|:------:|:-------------------:|:------:|:-------:|:---------:|
234-
| **Spector Search** || ✅ (non-uniform) || ✅ (via IVF-PQ) ||| ✅ (configurable oversampling) |
236+
| **Spector Search** || ✅ (non-uniform) || ✅ (via IVF-PQ) ||| ✅ (VASQ/VASQ-4 + configurable oversampling) |
235237
| **Elasticsearch** ||| ✅ (BBQ) |||| ✅ (asymmetric) |
236238
| **Milvus** | ✅ (IVF-SQ8) |||||||
237239
| **Qdrant** ||||||| ✅ (oversampling) |
@@ -242,7 +244,7 @@ Which quantization methods are available in each engine:
242244

243245
| Engine | 4× (Scalar) Recall | 8× (INT4) Recall | 16× (INT2) Recall | 32× (Best Method) Recall | Architecture Constraint |
244246
|--------|:------------------:|:-----------------:|:------------------:|:------------------------:|------------------------|
245-
| **Spector** | 95–99% | 85–95% (INT4+rescore) | 75–90% (INT2+rescore) | 80–92% (IVF-PQ) | None (purpose-built) |
247+
| **Spector** | 97–99.5% (VASQ) | 95–99% (VASQ-4+rescore) | 75–90% (INT2+rescore) | 80–92% (IVF-PQ) | None (purpose-built) |
246248
| **Elasticsearch** | 95–99% ||| 70–90% (BBQ + rescore) | Lucene segments |
247249
| **Milvus** | 95–99% ||| 80–92% (IVF-PQ) | Distributed complexity |
248250
| **Qdrant** | 95–99% ||| 65–85% (Binary + oversample) | Per-segment quantization |

docs/docs/deep-dives/vasq-deep-dive.md

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -183,49 +183,152 @@ graph LR
183183
| Float32 (baseline) || 100% || Reference |
184184
| **Scalar INT8** || 95-99% | ⚡⚡ | Simple, good baseline |
185185
| **VASQ INT8** | ~| **97-99.5%** | ⚡⚡ | FWHT rotation removes outlier impact |
186+
| **VASQ-4 (INT4)** | **6-8×** | **95-99%** | ⚡⚡ | Nibble-packed FWHT + 3× rescore recommended |
186187
| Scalar INT4 || 85-95% | ⚡⚡ | Aggressive, needs rescore |
187188
| Product Quantization | 32× | 80-92% || Complex, requires training |
188189

189190
VASQ achieves the compression of standard INT8 with recall approaching float32 — because the FWHT rotation ensures every dimension contributes equally to the quantized distance.
190191

191192
---
192193

194+
## 🔢 VASQ-4: INT4 Nibble-Packed Quantization
195+
196+
VASQ-4 extends the VASQ pipeline to 4-bit quantization, achieving **~2× additional compression** over VASQ-8 (6–8× total vs float32).
197+
198+
### Why It Works
199+
200+
The FWHT rotation that makes VASQ-8 work is equally beneficial for INT4:
201+
202+
- After FWHT, all dimensions contribute equally → INT4 quantization noise is **isotropic**
203+
- With IVF residuals, the tight range means INT4 on residuals ≈ INT6–INT7 on absolute vectors
204+
- 15 quantization levels (vs 255 for INT8) is sufficient for ranking with oversampling rescore
205+
206+
### Memory Layout
207+
208+
```
209+
[float32 normSq (4 bytes)] [INT4 × paddedDim nibble-packed (paddedDim/2 bytes)]
210+
```
211+
212+
Two 4-bit values are packed per byte using **offset encoding** (shifting [-7, 7] to [0, 14]):
213+
214+
```
215+
byte = (hiNibble << 4) | loNibble
216+
```
217+
218+
| Dims | Float32 | VASQ-8 | VASQ-4 | VASQ-4 Compression |
219+
|------|---------|--------|--------|-------------------|
220+
| 384 → 512 | 1,536 B | 516 B | 260 B | **5.9×** |
221+
| 768 → 1024 | 3,072 B | 1,028 B | 516 B | **6.0×** |
222+
| 4096 | 16,384 B | 4,100 B | 2,052 B | **8.0×** |
223+
224+
### Calibration
225+
226+
VASQ-4 uses **tighter clipping** than VASQ-8 (2.5σ vs 3.0σ) to optimize for 15 quantization levels:
227+
228+
```java
229+
VasqParams params = VasqCalibrator.calibrate4bit(corpus, dimensions, seed);
230+
// params.bitWidth() == 4
231+
// params.bytesPerVector() == 4 + paddedDim / 2
232+
```
233+
234+
### SIMD Kernel
235+
236+
The `Vasq4SimdKernel` extracts nibbles via shift+mask in each loop iteration, providing natural instruction-level parallelism:
237+
238+
```java
239+
// Load VL packed bytes = 2×VL dimensions
240+
ByteVector packed = ByteVector.fromMemorySegment(B_SPECIES, segment, offset, nativeOrder);
241+
242+
// Extract high nibbles (even dims) and low nibbles (odd dims)
243+
ByteVector hi = packed.lanewise(LSHR, 4).and(0x0F); // → [0, 14]
244+
ByteVector lo = packed.and(0x0F); // → [0, 14]
245+
246+
// Widen to float32 and FMA with deinterleaved query arrays
247+
accHi = ((FloatVector) hi.castShape(F_SPECIES, 0)).fma(qTildeHi[i], accHi);
248+
accLo = ((FloatVector) lo.castShape(F_SPECIES, 0)).fma(qTildeLo[i], accLo);
249+
```
250+
251+
The hi/lo split gives the CPU two independent FMA chains — one for even dimensions and one for odd — maximizing pipeline utilization.
252+
253+
### Usage
254+
255+
=== "Builder API"
256+
257+
```java
258+
SpectorEngine engine = SpectorEngine.builder()
259+
.dimensions(768)
260+
.capacity(500_000)
261+
.vasq4() // VASQ-4 with default 3× rescore
262+
.build();
263+
```
264+
265+
=== "Config API"
266+
267+
```java
268+
SpectorConfig config = SpectorConfig.DEFAULT
269+
.withDimensions(768)
270+
.withVasq4(5); // 5× oversampling for higher recall
271+
```
272+
273+
=== "Direct Index API"
274+
275+
```java
276+
QuantizedHnswIndex index = QuantizedHnswIndex.vasq4(
277+
768, 100_000, SimilarityFunction.COSINE, HnswParams.DEFAULT, 3);
278+
```
279+
280+
### Expected Recall
281+
282+
| Configuration | Recall@10 | Notes |
283+
|--------------|-----------|-------|
284+
| VASQ-4 (no rescore) | ~95–97% | Direct quantized distance only |
285+
| VASQ-4 (2× rescore) | ~96–98% | Moderate oversampling |
286+
| **VASQ-4 (3× rescore)** | **~97–99%** | **Recommended default** |
287+
| VASQ-4 (5× rescore) | ~98–99% | Higher latency, diminishing returns |
288+
| VASQ-8 (no rescore) | ~97–99.5% | For comparison |
289+
290+
---
291+
193292
## 💻 Implementation in Spector Search
194293

195294
### VasqCalibrator
196295

197296
Calibrates min/max statistics per dimension from a representative sample:
198297

199298
```java
200-
VasqParams params = VasqCalibrator.calibrate(flatData, sampleSize, dimensions);
201-
// params contains: paddedDim, mins[], scales[], normalization constants
299+
// VASQ-8 calibration
300+
VasqParams params8 = VasqCalibrator.calibrate(flatData, sampleSize, dimensions);
301+
302+
// VASQ-4 calibration (tighter clipping for 15 levels)
303+
VasqParams params4 = VasqCalibrator.calibrate4bit(flatData, sampleSize, dimensions);
202304
```
203305

204-
### VasqStrategy
306+
### VasqStrategy / Vasq4Strategy
205307

206308
Encodes vectors and computes asymmetric distances:
207309

208310
```java
311+
// VASQ-8
209312
VasqStrategy strategy = new VasqStrategy(params, SimilarityFunction.EUCLIDEAN);
210313

211-
// Encode a residual vector to INT8
212-
byte[] encoded = strategy.encode(residualVector);
213-
214-
// Prepare a query for fast scanning
215-
VasqQueryState qs = strategy.prepareQuery(residualQuery);
314+
// VASQ-4
315+
Vasq4Strategy strategy4 = new Vasq4Strategy(params4, SimilarityFunction.EUCLIDEAN);
216316

217-
// Compute approximate distance (SIMD-accelerated)
317+
// Both implement QuantizationStrategy — same API
318+
byte[] encoded = strategy.encode(residualVector);
218319
float dist = strategy.computeDistance(segment, offset, qs);
219320
```
220321

221-
### VasqSimdKernel
322+
### VasqSimdKernel / Vasq4SimdKernel
222323

223324
The Panama SIMD kernel that computes VASQ distances directly from off-heap memory:
224325

225326
```java
226-
// Zero-copy: reads INT8 codes directly from MemorySegment
327+
// VASQ-8: Zero-copy INT8 codes from MemorySegment
227328
float l2Dist = VasqSimdKernel.computeL2(segment, offset, paddedDim, queryState);
228-
float dotDist = VasqSimdKernel.computeDot(segment, offset, paddedDim, queryState);
329+
330+
// VASQ-4: Zero-copy nibble-packed INT4 codes from MemorySegment
331+
float l2Dist4 = Vasq4SimdKernel.computeL2(segment, offset, halfPaddedDim, queryState4);
229332
```
230333

231334
---
@@ -249,6 +352,7 @@ The quantization error is now distributed uniformly across all dimensions (becau
249352
## 🔗 See Also
250353

251354
- [Large-Scale Benchmarks](real-embedding-benchmarks.md) — Empirical sweeps for real embeddings and HNSW shard promotions.
355+
- [Roadmap](../roadmap.md) — Future compression improvements (VASQ-PQ, padding-aware storage, norm f16)
252356
- [Understanding Quantization](understanding-quantization.md) — All quantization techniques compared
253357
- [SpectorIndex Architecture](spector-index-architecture.md) — How VASQ fits into the IVF-HNSW index
254358
- [VASQ Whitepaper](vasq-spectorindex-whitepaper.md) — Academic treatment with proofs and benchmarks

0 commit comments

Comments
 (0)