#310·turbovec

TQ loses to the FAISS PQ baseline on bimodal and one-hot-like coordinate distributions

Author: RyanCodraiCreated Jul 29, 2026Updated Jul 31, 2026
Labelsneeds-human-decision

Found by bug-hunt wave 11 (recall-quality lens). Severity: medium — the only regimes found where TQ is worse than the published baseline.

Same harness as the official recall suite, n=20,000, d=256, 300 queries, FAISS IndexPQ(m = d/4 @2bit, d/2 @4bit, nbits=8, METRIC_INNER_PRODUCT) — the exact baseline README §Recall uses.

data bits TQ R@1 FAISS R@1
bimodal (N(0,1) ± 3 per coord, normalized) 2 0.364 0.632
bimodal 4 0.772 0.900
one-hot + 0.01 noise 2 0.068 0.308
one-hot + 0.01 noise 4 0.174 0.324

(bimodal R10@10: 0.518 vs 0.721.)

This contradicts the README's "beats FAISS" framing, which is only ever evaluated on dense unimodal embeddings. One-hot data is tie-heavy so R@1 there is partly arbitrary, but the 4.5x gap vs PQ on identical data is not.

Diagnosis: TQ+ calibration maps the 5/95% quantiles onto a unimodal Beta (encode.rs:195-196), which is the wrong summary for a bimodal marginal. A user-applied dense random pre-rotation did not rescue either case (one-hot 2-bit: 0.080 raw → 0.060 pre-rotated), so this is the codebook/calibration, not rotation mixing.

For contrast, clustered data (50 clusters, σ=0.3) is brutal in absolute terms (2-bit R@1 0.080) but TQ beats FAISS there (0.026) — intrinsic difficulty, not a regression.

Also worth a doc note from the same audit: unnormalized input works correctly (norms are stored; MIPS on log-normally-scaled vectors gives 2-bit R@1 0.710, 4-bit 0.915, identical whether queries are normalized), but neither docs/api.md nor the docstrings state that inputs need not be normalized or that the metric is raw inner product rather than cosine (add.__doc__ is None).

And a measured non-issue: the calibration freeze (#284/#285) costs ≤2pp on real data — cross-corpus shift (openai→GloVe) 2-bit R@1 0.520 frozen vs 0.530 fresh; 4-bit frozen slightly ahead. The one case worth documenting is a tiny first add: first-add of 150 vectors gives 2-bit R@1 0.523 vs 0.603 with a 5,000-vector first add (~8pp) — i.e. "make your first add at least a few thousand vectors".

Generated with Claude Code