| """ |
| turbo_benchmark.py — faithful TurboQuant baseline (Zandieh et al., ICLR 2026). |
| |
| Haar rotation + per-coordinate Lloyd-Max codebooks + optional QJL residual for |
| unbiased inner-product estimation. Separate from benchmark.py; writes |
| artifacts/turbo_codebooks.pt. |
| |
| Usage: |
| python benchmark.py --stage dump # or use existing calib_caches.pt |
| python turbo_benchmark.py --stage fit |
| python turbo_benchmark.py --stage cheap |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import os |
| import time |
|
|
| import torch |
|
|
| from vqkv.metrics import (key_cosine, cache_mse, inner_product_distortion, |
| attention_output, attn_output_cosine, attn_output_error) |
|
|
| ARTIFACT_DIR = os.environ.get("VQKV_ARTIFACTS", "./artifacts") |
| MODEL_ID = os.environ.get("LAGUNA_ID", "poolside/Laguna-XS.2") |
| CALIB_DATASET = os.environ.get("CALIB_DATASET", "SWE-bench/SWE-smith-trajectories") |
| CALIB_SPLIT = os.environ.get("CALIB_SPLIT", "tool") |
| EVAL_SOURCE = os.environ.get("EVAL_SOURCE", "swesmith") |
|
|
| _HOTPOTQA_PROMPT = ( |
| "Answer the question based on the given passages. " |
| "Only give me the answer and do not output any other words.\n\n" |
| "The following are given passages.\n{context}\n\n" |
| "Answer the question based on the given passages. " |
| "Only give me the answer and do not output any other words.\n\n" |
| "Question: {input}\nAnswer:" |
| ) |
|
|
|
|
| |
| |
| |
| def lloyd_max_1d(samples: torch.Tensor, n_levels: int, iters: int = 30, |
| seed: int = 0) -> torch.Tensor: |
| """Optimal scalar (Lloyd-Max) codebook for a 1-D distribution given samples. |
| |
| samples: (M,) 1-D values drawn from the (concentrated) coordinate dist. |
| returns: (n_levels,) sorted reconstruction levels. |
| |
| This is 1-D k-means; for the rotated unit-norm coordinates the distribution |
| is the same across coordinates in high dim, so one shared codebook per |
| bit-width suffices (matching the paper's single precomputed Beta codebook). |
| """ |
| s = samples.flatten() |
| s = s[torch.isfinite(s)] |
| |
| qs = torch.linspace(0.0, 1.0, n_levels + 2, device=s.device)[1:-1] |
| levels = torch.quantile(s, qs) |
| for _ in range(iters): |
| |
| idx = torch.bucketize(s, (levels[1:] + levels[:-1]) / 2) |
| new = levels.clone() |
| for j in range(n_levels): |
| m = idx == j |
| if m.any(): |
| new[j] = s[m].mean() |
| shift = (new - levels).abs().max() |
| levels = new |
| if shift < 1e-6: |
| break |
| return torch.sort(levels).values |
|
|
|
|
| def _haar_rotation(d: int, seed: int = 0, device=None) -> torch.Tensor: |
| g = torch.Generator().manual_seed(seed) |
| a = torch.randn(d, d, generator=g) |
| q, r = torch.linalg.qr(a) |
| |
| q = q * torch.sign(torch.diag(r)).unsqueeze(0) |
| if device is not None: |
| q = q.to(device) |
| return q |
|
|
|
|
| |
| |
| |
| class TurboQuantMSE: |
| def __init__(self, nbits: int = 3, seed: int = 0): |
| self.nbits = nbits |
| self.seed = seed |
| self._rot = None |
| self._levels = None |
|
|
| def fit(self, calib: torch.Tensor): |
| """calib: (N, d). Fit rotation + shared per-coordinate level set.""" |
| d = calib.shape[-1] |
| self._rot = _haar_rotation(d, self.seed, calib.device) |
| xn = calib / calib.norm(dim=-1, keepdim=True).clamp_min(1e-8) |
| r = xn @ self._rot |
| |
| pool = r.flatten() |
| if pool.numel() > 2_000_000: |
| pool = pool[torch.randperm(pool.numel(), device=pool.device)[:2_000_000]] |
| self._levels = lloyd_max_1d(pool, 1 << self.nbits) |
| return self |
|
|
| def _quantize(self, x): |
| norms = x.norm(dim=-1, keepdim=True).clamp_min(1e-8) |
| xn = x / norms |
| y = xn @ self._rot |
| edges = (self._levels[1:] + self._levels[:-1]) / 2 |
| idx = torch.bucketize(y, edges).clamp(0, self._levels.numel() - 1) |
| return idx, norms |
|
|
| def roundtrip(self, x): |
| idx, norms = self._quantize(x) |
| y_hat = self._levels[idx] |
| x_hat = y_hat @ self._rot.T |
| return x_hat * norms |
|
|
| def to(self, device): |
| self._rot = self._rot.to(device) |
| self._levels = self._levels.to(device) |
| return self |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class QJLResidualIP: |
| def __init__(self, mse: "TurboQuantMSE", qjl_rows: int = None, seed: int = 0): |
| self.mse = mse |
| self.seed = seed |
| self.qjl_rows = qjl_rows |
| self._G = None |
|
|
| def fit(self, calib: torch.Tensor): |
| d = calib.shape[-1] |
| m = self.qjl_rows or d |
| g = torch.Generator().manual_seed(self.seed + 7) |
| self._G = torch.randn(m, d, generator=g).to(calib.device) |
| return self |
|
|
| def estimate_ip(self, q, k): |
| """Unbiased estimate of rowwise <q, k> using MSE stage + QJL residual. |
| q, k: (N, d). Returns (N,) inner-product estimates.""" |
| m = self._G.shape[0] |
| |
| knorm = k.norm(dim=-1, keepdim=True).clamp_min(1e-8) |
| kn = k / knorm |
| idx, _ = self.mse._quantize(k) |
| kn_hat = (self.mse._levels[idx]) @ self.mse._rot.T |
| score1 = (q * (kn_hat * knorm)).sum(-1) |
| |
| r = (kn - kn_hat) |
| rnorm = r.norm(dim=-1, keepdim=True).clamp_min(1e-12) |
| rn = r / rnorm |
| qg = q @ self._G.T |
| rs = torch.sign(rn @ self._G.T) |
| score2 = ((torch.pi / 2) ** 0.5 / m) * (qg * rs).sum(-1) * rnorm.squeeze(-1) * knorm.squeeze(-1) |
| return score1 + score2 |
|
|
| def to(self, device): |
| self.mse.to(device) |
| self._G = self._G.to(device) |
| return self |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def turbo_configs(bits_list): |
| cfgs = [] |
| for b in bits_list: |
| cfgs.append((f"turbo-mse-{b}b", b, False)) |
| cfgs.append((f"turbo-prod-{b}b (K:+qjl)", b, True)) |
| return cfgs |
|
|
|
|
| |
| |
| |
| def stage_fit(bits_list): |
| path = os.path.join(ARTIFACT_DIR, "calib_caches.pt") |
| if not os.path.exists(path): |
| raise FileNotFoundError( |
| f"{path} not found. Run `python benchmark.py --stage dump` first " |
| f"to produce the calibration caches this script reuses.") |
| blob = torch.load(path, weights_only=False) |
| calib, meta = blob["calib"], blob["meta"] |
| hd = meta["head_dim"] |
| dev = "cuda" if torch.cuda.is_available() else "cpu" |
|
|
| fitted = {} |
| for name, b, use_qjl in turbo_configs(bits_list): |
| per_layer = {} |
| t0 = time.time() |
| for i, c in calib.items(): |
| kf = c["k"].reshape(-1, hd)[:200_000].to(dev) |
| vf = c["v"].reshape(-1, hd)[:200_000].to(dev) |
| kq = TurboQuantMSE(nbits=b).fit(kf) |
| vq = TurboQuantMSE(nbits=b).fit(vf) |
| qjl = QJLResidualIP(kq).fit(kf) if use_qjl else None |
| per_layer[i] = {"kq": kq, "vq": vq, "qjl": qjl} |
| fitted[name] = per_layer |
| print(f"[fit] {name}: {len(per_layer)} layer-codebooks in {time.time()-t0:.1f}s") |
| torch.save({"fitted": fitted, "meta": meta, "bits": bits_list}, |
| os.path.join(ARTIFACT_DIR, "turbo_codebooks.pt")) |
| print(f"[fit] saved -> {ARTIFACT_DIR}/turbo_codebooks.pt") |
|
|
|
|
| |
| |
| |
| def flatten_trace(example, tok) -> str: |
| raw = (example.get("messages") or example.get("trajectory") |
| or example.get("conversations")) |
| if raw is None: |
| return json.dumps(example)[:200_000] |
| if isinstance(raw, str): |
| try: |
| raw = json.loads(raw) |
| except json.JSONDecodeError: |
| return raw[:200_000] |
| norm = [] |
| for m in raw: |
| role = m.get("role") or m.get("from") or "user" |
| content = m.get("content") or m.get("value") or "" |
| if isinstance(content, list): |
| content = "\n".join( |
| it.get("text", str(it)) if isinstance(it, dict) else str(it) |
| for it in content) |
| tc = m.get("tool_calls") |
| if tc: |
| tct = json.dumps(tc, ensure_ascii=False) |
| content = (content + "\n" + tct).strip() if content else tct |
| role = {"human": "user", "gpt": "assistant", "tool": "user"}.get(role, role) |
| if not content.strip(): |
| continue |
| norm.append({"role": role, "content": content}) |
| merged = [] |
| for m in norm: |
| if merged and merged[-1]["role"] == m["role"]: |
| merged[-1]["content"] += "\n\n" + m["content"] |
| else: |
| merged.append(dict(m)) |
| try: |
| return tok.apply_chat_template(merged, tokenize=False, |
| add_generation_prompt=False) |
| except Exception: |
| return "\n\n".join(f"{m['role']}: {m['content']}" for m in merged) |
|
|
|
|
| def flatten_longbench(example) -> str: |
| """Format a LongBench hotpotqa example (context + input) as a plain string.""" |
| return _HOTPOTQA_PROMPT.format( |
| context=example["context"], input=example["input"]) |
|
|
|
|
| def _load_longbench_hotpotqa(): |
| """Load THUDM/LongBench hotpotqa, bypassing the deprecated dataset script.""" |
| from datasets import load_dataset as _ld |
| for fname in ("hotpotqa_e.jsonl", "hotpotqa.jsonl"): |
| try: |
| return _ld( |
| "json", |
| data_files=f"hf://datasets/THUDM/LongBench/data/{fname}", |
| split="train", |
| ) |
| except Exception: |
| continue |
| return _ld("THUDM/LongBench", name="hotpotqa", split="test") |
|
|
|
|
| def _load_cheap_eval_dataset(n_eval: int, eval_source: str, tok): |
| """Return (dataset_slice, get_text, max_len, label) for stage_cheap.""" |
| from datasets import load_dataset |
|
|
| if eval_source == "longbench-hotpotqa": |
| ds = _load_longbench_hotpotqa() |
| |
| end = min(n_eval, len(ds)) |
| ds = ds.select(range(0, end)) |
| label = f"LongBench hotpotqa (rows 0–{end - 1})" |
| return ds, flatten_longbench, 32768, label |
|
|
| ds = load_dataset(CALIB_DATASET, split=CALIB_SPLIT) |
| start = 500 |
| end = min(start + n_eval, len(ds)) |
| ds = ds.select(range(start, end)) |
| label = f"{CALIB_DATASET} split={CALIB_SPLIT} (rows {start}–{end - 1})" |
| return ds, lambda ex: flatten_trace(ex, tok), 16384, label |
|
|
|
|
| def load_model_and_meta(): |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| tok = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True) |
| model = AutoModelForCausalLM.from_pretrained( |
| MODEL_ID, dtype=torch.bfloat16, device_map="cuda", trust_remote_code=True) |
| model.eval() |
| cfg = model.config |
| full = [i for i, t in enumerate(cfg.layer_types) if t == "full_attention"] |
| meta = {"full_layers": full, "n_kv_heads": cfg.num_key_value_heads, |
| "n_q_heads": cfg.num_attention_heads, |
| "head_dim": cfg.head_dim, "n_layers": cfg.num_hidden_layers} |
| print(f"[meta] full-attention layers ({len(full)}): {full}") |
| return model, tok, meta |
|
|
|
|
| |
| |
| |
| def stage_cheap(n_eval=64, max_len=None, eval_source: str | None = None, |
| min_len=2048): |
| import collections |
| from tqdm import tqdm |
| from transformers.cache_utils import DynamicCache |
|
|
| eval_source = eval_source or EVAL_SOURCE |
| blob = torch.load(os.path.join(ARTIFACT_DIR, "turbo_codebooks.pt"), |
| weights_only=False) |
| fitted, meta = blob["fitted"], blob["meta"] |
| full = meta["full_layers"] |
| hd = meta["head_dim"] |
| n_q = meta.get("n_q_heads", 48) |
|
|
| |
| ATTN_WIN = 512 |
|
|
| |
| cfg_lookup = {name: (b, use_qjl) for name, b, use_qjl in turbo_configs(blob["bits"])} |
|
|
| model, tok, _ = load_model_and_meta() |
| dev = model.device |
| if str(dev) == "meta": |
| dev = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") |
|
|
| ds, get_text, default_max_len, source_label = _load_cheap_eval_dataset( |
| n_eval, eval_source, tok) |
| if max_len is None: |
| max_len = default_max_len |
| print(f"[turbo-cheap] eval_source={eval_source} {source_label} " |
| f"rows={len(ds)} max_len={max_len}") |
|
|
| for per_layer in fitted.values(): |
| for entry in per_layer.values(): |
| entry["kq"].to(dev); entry["vq"].to(dev) |
| if entry["qjl"] is not None: |
| entry["qjl"].to(dev) |
|
|
| class EvalDump(DynamicCache): |
| def __init__(self): |
| super().__init__(); self.d = {i: {} for i in full} |
| def update(self, ks, vs, li, ck=None): |
| if li in set(full): |
| self.d[li]["k"] = ks.detach()[0].permute(1, 0, 2).float() |
| self.d[li]["v"] = vs.detach()[0].permute(1, 0, 2).float() |
| return super().update(ks, vs, li, ck) |
|
|
| trace_rows = [] |
| n_used = 0 |
|
|
| for ex in tqdm(ds, desc=f"turbo-cheap/{eval_source}"): |
| text = get_text(ex) |
| ids = tok(text, return_tensors="pt", truncation=True, |
| max_length=max_len).to(dev) |
| if ids["input_ids"].shape[1] < min_len: |
| continue |
| cache = EvalDump() |
| with torch.no_grad(): |
| model.model(**ids, past_key_values=cache, use_cache=True) |
| n_used += 1 |
|
|
| |
| |
| |
| |
| synth_q = {} |
| for i in full: |
| s = cache.d[i]["k"].shape[0] |
| win = min(s, ATTN_WIN) |
| q_rand = torch.randn(win, n_q, hd, device=dev) |
| synth_q[i] = q_rand / q_rand.norm(dim=-1, keepdim=True).clamp_min(1e-8) |
|
|
| for name, per_layer in fitted.items(): |
| acc = collections.defaultdict(float) |
| nL = 0 |
| for i in full: |
| k = cache.d[i]["k"]; v = cache.d[i]["v"] |
| s, h, d = k.shape |
| e = per_layer[i] |
| k_hat = e["kq"].roundtrip(k.reshape(-1, d)).reshape(s, h, d) |
| v_hat = e["vq"].roundtrip(v.reshape(-1, d)).reshape(s, h, d) |
|
|
| acc["key_cos"] += key_cosine(k, k_hat) |
| acc["val_cos"] += key_cosine(v, v_hat) |
| acc["key_mse"] += cache_mse(k, k_hat) |
| acc["val_mse"] += cache_mse(v, v_hat) |
|
|
| |
| win = min(s, ATTN_WIN) |
| kw, kw_hat = k[-win:], k_hat[-win:] |
| vw, vw_hat = v[-win:], v_hat[-win:] |
| q_syn = synth_q[i] |
|
|
| out_ref, _ = attention_output(q_syn, kw, vw, n_q) |
| out_hat, _ = attention_output(q_syn, kw_hat, vw_hat, n_q) |
| acc["attn_cos"] += attn_output_cosine(out_ref, out_hat) |
| acc["attn_output_error"] += attn_output_error(out_ref, out_hat) |
|
|
| |
| |
| |
| q0 = q_syn[:, 0, :] |
| kr0 = kw[:, 0, :] |
| kh0 = kw_hat[:, 0, :] |
| qi = torch.randint(0, win, (4096,), device=dev) |
| ki = torch.randint(0, win, (4096,), device=dev) |
| ip_ref = (q0[qi] * kr0[ki]).sum(-1) |
| if e["qjl"] is not None: |
| ip_hat = e["qjl"].estimate_ip(q0[qi], kr0[ki]) |
| else: |
| ip_hat = (q0[qi] * kh0[ki]).sum(-1) |
| acc["ip_bias"] += (ip_hat - ip_ref).mean().item() |
| acc["ip_rel"] += ((ip_hat - ip_ref).abs() / |
| ip_ref.abs().clamp_min(1e-6)).mean().item() |
| nL += 1 |
|
|
| trace_rows.append({ |
| "trace_len": ids["input_ids"].shape[1], |
| "config": name, |
| "key_cos": acc["key_cos"] / nL, |
| "val_cos": acc["val_cos"] / nL, |
| "key_mse": acc["key_mse"] / nL, |
| "val_mse": acc["val_mse"] / nL, |
| "attn_cos": acc["attn_cos"] / nL, |
| "attn_output_error": acc["attn_output_error"] / nL, |
| "ip_rel": acc["ip_rel"] / nL, |
| "ip_bias": acc["ip_bias"] / nL, |
| }) |
|
|
| |
| agg = collections.defaultdict(lambda: collections.defaultdict(list)) |
| for r in trace_rows: |
| for col in ("key_cos", "val_cos", "key_mse", "val_mse", |
| "attn_cos", "attn_output_error", "ip_rel", "ip_bias"): |
| agg[r["config"]][col].append(r[col]) |
|
|
| COLS = ("key_cos", "val_cos", "key_mse", "val_mse", |
| "attn_cos", "attn_output_error", "ip_rel", "ip_bias") |
|
|
| summary = [] |
| for name in cfg_lookup: |
| if name not in agg: |
| continue |
| b, use_qjl = cfg_lookup[name] |
| |
| bpe = b + (1 if use_qjl else 0) + 16.0 / hd |
| cols = agg[name] |
| n = len(cols["key_cos"]) |
| row = {"config": name, "bits_per_elt": round(bpe, 4), |
| "n_traces": n, "eval_source": eval_source} |
| for col in COLS: |
| row[col] = round(sum(cols[col]) / n, 5) |
| summary.append(row) |
|
|
| print(f"\n[turbo-cheap] mean metrics over {n_used} traces ({eval_source}):") |
| print(f" {'config':30s} {'bpe':>5} {'key_cos':>8} {'val_cos':>8} " |
| f"{'key_mse':>9} {'val_mse':>9} {'attn_cos':>9} {'attn_err':>9} " |
| f"{'ip_rel':>8} {'ip_bias':>9}") |
| for row in summary: |
| print(f" {row['config']:30s} {row['bits_per_elt']:5.2f} " |
| f"{row['key_cos']:8.4f} {row['val_cos']:8.4f} " |
| f"{row['key_mse']:9.5f} {row['val_mse']:9.5f} " |
| f"{row['attn_cos']:9.4f} {row['attn_output_error']:9.4f} " |
| f"{row['ip_rel']:8.5f} {row['ip_bias']:9.6f}") |
|
|
| out_name = ("turbo_cheap_metrics_hotpotqa.json" |
| if eval_source == "longbench-hotpotqa" |
| else "turbo_cheap_metrics.json") |
| out_path = os.path.join(ARTIFACT_DIR, out_name) |
| json.dump(summary, open(out_path, "w"), indent=2) |
| print(f"[turbo-cheap] saved -> {out_path}") |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser(description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter) |
| ap.add_argument("--stage", required=True, choices=["fit", "cheap"]) |
| ap.add_argument("--bits", type=int, nargs="+", default=[4, 2, 1], |
| help="bit-widths to sweep (default: 4 2 1)") |
| ap.add_argument("--n_eval", type=int, default=64) |
| ap.add_argument( |
| "--eval_source", type=str, default=None, |
| choices=["swesmith", "longbench-hotpotqa"], |
| help="cheap stage: eval corpus (default: EVAL_SOURCE env or swesmith)") |
| ap.add_argument("--max_len", type=int, default=None, |
| help="cheap stage: max prompt tokens (default: 16384 swesmith, " |
| "32768 longbench-hotpotqa)") |
| args = ap.parse_args() |
| if args.stage == "fit": |
| stage_fit(args.bits) |
| elif args.stage == "cheap": |
| stage_cheap(n_eval=args.n_eval, max_len=args.max_len, |
| eval_source=args.eval_source) |
|
|
|
|
| if __name__ == "__main__": |
| main() |