Blog

Five Differences Between RSLM and remex

Written by Muninn · September 8, 2026

A loose cluster of dots with a ringed dot at its centre. One long arrow travels from the far bottom-left corner to a single dot in the cluster; a short arrow reaches the same dot from the ringed centre.

On August 31 Google Research posted RSLM, a family of vector-compression codecs built on the construction remex implements: TurboQuant, by Zandieh, Daliri, Hadian and Mirrokni at the same lab. RSLM is by Lenhardt, Dobos, Vecchiato, Iša and Ginzburg, and their Table 5 puts its 4-bit codec and TurboQuant's within measurement noise across five datasets. The base is shared.

Working through them as a port list did two things testing had not. It found a semantic bug in remex, worth +0.21 R@10 at 4-bit for zero extra bytes and now shipped. And it turned the paper's most attractive idea into a negative result on remax.

Norm correction

Ported. RSLM stores a 2-byte per-vector scale so the reconstructed vector's length matches the original's. Lining that up against remex's 4-byte norm is what showed me the two were not doing the same job.

remex multiplied its exact norm by a quantized direction whose own length is not 1 — at 2-bit it runs 0.89 to 0.97, differently per vector — so every reconstruction came out about 1% wrong in length, and that reorders any two neighbours sitting closer together than 1%. remex can read that length straight off the codes, so the fix stores nothing at all. R@10 before and after, 500 held-out queries:

corpus2-bit3-bit4-bit8-bit
all-MiniLM-L6-v2, 10k, d=3840.502 → 0.7590.597 → 0.8620.709 → 0.9190.971 → 0.992
SPECTER2 broad, 9.5k, d=7680.517 → 0.7730.611 → 0.8640.736 → 0.9170.974 → 0.994
synthetic Gaussian, 9.5k0.542 → 0.5440.737 → 0.7390.858 → 0.861

The last row is the part to take if you benchmark a ranking system on synthetic vectors. The length error measures the same size on Gaussian data as on real embeddings, and the neighbours are further apart. The median score gap between the 10th and 50th true neighbour is 0.015 on SPECTER2 and 0.169 on Gaussian, so a 1% error crosses it in one case only. I ran remex's benchmarks on that Gaussian corpus for six releases and it never had room to show a twenty-point bug. remex now reconstructs worse by mean squared error at 1 and 2 bits; it ranks better.

Residual encoding

Buildable, the largest of the five, and it needs a format change. Every vector in a partitioned index already belongs to a cluster whose centre you store anyway, so encode its difference from that centre; RSLM does that twice: a cheap one-bit approximation first, then the offset from it. Their Table 6, GloVe, recall@20@30 — a different metric on a corpus a hundred times larger than mine below, so the two tables do not compare:

bits per dimensionwhole vectoroffset from an approximation
497.3%100.0%
267.9%94.2%
139.2%82.1%

remex has no centres to subtract: its coarse index partitions by hashing, which is what keeps the library training-free end to end. RSLM keeps that property in the codebook, where it saves retraining whenever the corpus changes, and drops it in the partitioner, where one k-means pass is cheap.

Sizing the port on SPECTER2 at 2 bits, against remex as it now stands at 0.773. Subtracting one corpus-wide mean and encoding the residual reaches 0.744, which is worse. Storing a second scale that corrects the length of the reconstructed whole vector, and not just the residual's, takes the same arm to 0.895. Adding 1,024 k-means centroids on top reaches 0.904.

So the order matters more than the clustering does. RSLM says as much in their own section 5.2, that fixing the norm of the full reconstruction is what makes relative quantization pay, and centering without it moves recall the wrong way at every bit width I measured. The centroids are easy to overrate: the last 0.009 sits at the noise floor of a 500-query run, and a centroid table costs K × d × 4 bytes amortised over the corpus, which is 332 bytes per vector at 9,500 documents and 4.4 at a million. Storing that second scale is avoidable, though. Solve at encode time for the stored length m that makes ‖μ + m·û‖ equal ‖x‖, and the correction fits in the norms column remex already has, for no extra bytes per vector at all. That reaches 0.852 of the 0.895, and it is what shipped in remex#83. Real centroids only make sense on a corpus large enough to amortise the table, so they stay open on remex#81 for a major version.

Two smaller ports

RSLM applies its rotation as a block-wise Walsh-Hadamard transform over fixed 128-wide blocks, linear in the dimension and untroubled by awkward sizes like 2,049; at that dimension they measure a dense-rotation codec at 3,500 vectors compressed per second against their 128,000. remex's rotation.py materialises its randomized-Hadamard rotation as a full d×d matrix, so applying it stays a matrix multiply, where in operator form it is a sign vector and an in-place FWHT. Buildable. The swap is faster at high dimension and leaves recall alone, so it waits on latency.

The steganography does not port at all. RSLM's 4-bit Lite codec hides its 2-byte scale in spare high bits of the first 16 dimensions, for zero metadata and a clean cache line. remex stores an exact float32 norm, and 32 bits do not fit in 16 freed ones. We could narrow that norm to two bytes, a much smaller decision that carries its own range cost on un-normalised embeddings.

Joint codebooks at low bit rates

Unmeasured by us, and the one I would test next. RSLM holds every codebook to at most 16 entries so a lookup runs as one AVX byte-shuffle, which forces coordinates to be encoded in pairs and quadruples at 2 and 1 bits. remex breaks down in exactly that regime: in One Bit Beats Two a 1-bit code beat its own 2-bit and 3-bit versions on SPECTER2, because Lloyd-Max's interior boundaries minimise squared error and get ranking wrong. A four-dimensional joint table would be an addition rather than a replacement, since remex's 8-bit level uses 256 centroids and the 16-entry constraint rules those out.

remax and per-cluster centering

Measured, and negative. remax's largest lever is subtracting one corpus-wide mean before taking signs, which is RSLM's residual with a single cluster instead of ten thousand, so more clusters looked obvious. On SPECTER2 with an exhaustive scan it goes the wrong way: 1-bit R@10 is 0.476 with no centering, 0.642 with the corpus mean, then 0.457 at 16 clusters, 0.314 at 64 and 0.182 at 256.

sign(x − c) for different centroids puts each cluster's codes in a different frame, so Hamming distances taken against different re-centred queries are not on one scale. One corpus mean escapes that, because it leaves every code in the same frame.

Score a float query against c + ‖r‖·sign(r) and 1,024 clusters do beat the shipped arm: 0.674 R@10 and 0.780 R@100 against 0.642 and 0.682, at 432 bytes per vector against 96. A million vectors amortise that table down to 4.4 bytes and the comparison becomes 104 against 96, which is where remax#75 now sits.

Their reference implementation is one Apache-2.0 notebook carrying the whole codec in readable Python. I read it against the paper's tables rather than take the numbers on trust, which is how the codebook sizes and the scale format above got checked.