Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
797e39c
add Qwen3.5 support
zzz0906 May 24, 2026
3ccc260
gemma-4-12B-it: 2-bit (INT2) OSCAR KV cache — matches BF16 on GPQA
zzz0906 Jun 5, 2026
7c76bad
Eval runner: add HumanEval task, long-eval timeout, single-stream
zzz0906 Jun 7, 2026
afd9a8e
Merge remote-tracking branch 'oscar/zhongzhu/realswe' into zhongzhu/h…
zzz0906 Jun 7, 2026
5670215
gemma-4-12B-it: optional vision (multimodal) serving — INT2 OSCAR + M…
zzz0906 Jun 8, 2026
237dd77
Merge remote-tracking branch 'oscar/main' into zhongzhu/gemma4-12b
zzz0906 Jun 8, 2026
a84d935
README: add Gemma 4 12B (gemma4_unified) to configured models + lates…
zzz0906 Jun 8, 2026
d5b6620
Merge oscar/main; add per-model GPQA (BF16 vs OSCAR INT2) table
zzz0906 Jun 8, 2026
f49f408
Merge remote-tracking branch 'oscar/main' into zhongzhu/gemma4-12b
zzz0906 Jun 8, 2026
16cab7f
Qwen3.5 multibench + Qwen3-32B GSM8K + GLM-5.2 MLA-latent pipeline
zzz0906 Jul 16, 2026
3c53f84
GLM-5.2 real-INT2 MLA latent kernel prototype (pack + fused decode)
zzz0906 Jul 17, 2026
3c24342
Bring in MiniMax-M3 OSCAR support from the m3oscar worktree
zzz0906 Jul 20, 2026
f1c3e8b
Archive MiniMax-M3 OSCAR evidence before task-dir cleanup
zzz0906 Jul 21, 2026
a3ae46e
GLM-5.2 GPQA cells + deterministic evidence; harness timeout; repo hy…
zzz0906 Jul 21, 2026
67a12ab
GLM-5.2 bf16 HumanEval x3 seeds (92.68/90.24/91.46 -> 91.5±1.0)
zzz0906 Jul 21, 2026
9f563d9
GLM-5.2 bf16 AIME25 x3 seeds (76.67/80.00/73.33 -> 76.7±3.3)
zzz0906 Jul 22, 2026
863c2ad
GLM-5.2 pure-INT2 GPQA x3 (70.2/69.2/66.7 -> 68.7±1.8; answered-only …
zzz0906 Jul 22, 2026
f4c2af2
GLM-5.2 32K matrix complete (25 cells): INT2 humaneval 88.2±1.5, aime…
zzz0906 Jul 24, 2026
03bba7c
BFCL Qwen3-8B multiturn: bf16 baseline (45.0/42.0/36.5/30.0) vs int2 …
zzz0906 Jul 24, 2026
1660d2b
BFCL int2 legs: radix-OFF recovers 14.6->30.5; +graphs-ON 33.6 (graph…
zzz0906 Jul 24, 2026
007219b
BFCL final matrix: int2+LM+recent512 (graphs ON) = 37.1 vs bf16 38.4;…
zzz0906 Jul 24, 2026
f1d1645
BFCL long_ctx levers: noclip=17.5 catastrophic (clip essential); g64/…
zzz0906 Jul 25, 2026
acdd95b
BFCL Qwen3-32B: bf16 49.1 vs int2-best 44.4 (-4.7); 32B int2 long_ctx…
zzz0906 Jul 26, 2026
7e049a3
BFCL Qwen3.5-4B: bf16 50.1 vs int2-best 47.6 (-2.5); NO long_ctx tax …
zzz0906 Jul 26, 2026
efc8664
M3 multibench complete (32K, hadamard): bf16 93.1/77.8/79.7 vs int2 9…
zzz0906 Jul 27, 2026
4890af4
GLM-5.2 bf16@64K seed1 = 84.34 (seed0 87.88)
zzz0906 Jul 28, 2026
9dc6e8a
Fix Qwen3MoE missing V-rotation absorb wiring (issue #16): env=1 stor…
zzz0906 Jul 30, 2026
dc251c3
GLM-5.2 bf16@64K complete: 87.88/84.34/89.39 = 87.2±2.6 (3 seeds)
zzz0906 Jul 30, 2026
a9c3339
Disable fused set_kv_buffer for OSCAR mixed-KV pools (issue #16 root …
zzz0906 Jul 30, 2026
b865d10
V2 groundwork: per-head rotation fitter + validated fake-quant PPL ha…
zzz0906 Aug 3, 2026
43948dc
Rename worktree dir CoQuant -> oscar; update path references in scrip…
zzz0906 Aug 3, 2026
01b57a0
Merge zhongzhu/gemma4-12b into hybrid-model: Gemma-4-12B heterogeneou…
zzz0906 Aug 4, 2026
07f5749
Per-KV-head Oscar rotations (V2), auto-detected from the checkpoint
zzz0906 Aug 5, 2026
35e6303
Add CPU equivalence tests for per-head rotations
zzz0906 Aug 5, 2026
b129f91
Merge remote-tracking branch 'oscar/main' into zhongzhu/perhead-rotation
zzz0906 Aug 5, 2026
c8695fa
Fix per-head rotation: einsum result must be contiguous
zzz0906 Aug 6, 2026
28e7739
Shard per-head rotations by TP rank
zzz0906 Aug 6, 2026
f906515
Fix Qwen3.5 hybrid regression from the gemma merge
zzz0906 Aug 6, 2026
9199aea
Put the hybrid per-layer geometry overrides on the right class
zzz0906 Aug 6, 2026
3ebd3bb
Add Qwen3-30B-A3B INT2 KV reference recipe
zzz0906 Aug 6, 2026
28ec83d
Add a pytest-free runner for the CPU KV-pool checks
zzz0906 Aug 6, 2026
9786849
Shard bare 3-D per-head rotations by TP rank
zzz0906 Aug 6, 2026
010c401
Revert the bare-3D head sharding; it broke every V1 model
zzz0906 Aug 6, 2026
d30e645
Add the post-merge per-model smoke harness
zzz0906 Aug 6, 2026
f38d292
V2.md: per-head serving is implemented, not pending
zzz0906 Aug 6, 2026
a0cabff
Drop V2.md from the branch
zzz0906 Aug 6, 2026
6f9af58
Refuse V-rotation absorption with per-head rotations
zzz0906 Aug 7, 2026
a963bdc
Add Qwen3-30B-A3B accuracy sweep: BF16 vs shared vs per-head
zzz0906 Aug 7, 2026
5bb5906
bench: drop the in-pod git merge
zzz0906 Aug 7, 2026
adf238e
bench matrix: let the caller override V-rotation absorption
zzz0906 Aug 7, 2026
99a699c
Fix HumanEval extraction for thinking models
zzz0906 Aug 7, 2026
82fb745
Add offline HumanEval re-scoring from saved io_log
zzz0906 Aug 7, 2026
357a9df
Generate the bench manifests instead of checking in 12 copies
zzz0906 Aug 7, 2026
4bcec6b
Strip experiment and eval material for the PR
zzz0906 Aug 7, 2026
c642564
Point fit_perhead.sh at the moved fitter
zzz0906 Aug 8, 2026
6710873
Trim duplicated rotation scripts and drop personal paths
zzz0906 Aug 9, 2026
86f8d7d
Add real packed-INT2 kernels for the MLA latent, plus an equivalence …
zzz0906 Aug 12, 2026
9254b1b
Make MLA dequant bit-exact and judge the test on relative error
zzz0906 Aug 12, 2026
fcb8965
Report the kernel-vs-simulation gap against the quantization error
zzz0906 Aug 12, 2026
813e818
Add uniform-mode divergence diagnostic
zzz0906 Aug 12, 2026
f68b5a1
Fix off-by-one rounding in the MLA quantizer
zzz0906 Aug 12, 2026
e93fdcd
Add element-level code-mismatch diagnostic
zzz0906 Aug 12, 2026
de60c6b
Use IEEE round-to-nearest division in the MLA quantizer
zzz0906 Aug 12, 2026
1c0bff1
Force true division for scale and LM statistics too
zzz0906 Aug 12, 2026
2cdafb4
Judge MLA kernel on the production path; document the tie-boundary limit
zzz0906 Aug 12, 2026
bf3364e
fdiv needs float operands: cast the constexpr group_size
zzz0906 Aug 12, 2026
21b416e
Wire the packed-INT2 MLA kernels into the pool behind a flag
zzz0906 Aug 12, 2026
4b8325a
Import Kimi-K3 INT2 support from the clean worktree
zzz0906 Aug 13, 2026
724c2c3
Merge Kimi-K3 INT2 support into the hybrid-model branch
zzz0906 Aug 13, 2026
599cc81
Add a block-tiled fused MLA quant kernel
zzz0906 Aug 13, 2026
d2d1837
Fused kernel: re-read lanes strided (triton cannot index a reshaped 3…
zzz0906 Aug 13, 2026
857845a
Use the fused MLA kernel in the pool; default GROUPS_PER_BLOCK=4
zzz0906 Aug 13, 2026
476cc92
Add an overhead breakdown for the fused MLA kernel
zzz0906 Aug 13, 2026
4eebc96
Reuse scratch buffers in the MLA kernel wrapper
zzz0906 Aug 13, 2026
5d388b6
Pool uses the scratch-reuse MLA kernel (2.6x the torch fake-quant)
zzz0906 Aug 13, 2026
00e2145
Fix HybridLinearKVPool _R_k regression from the Kimi merge
zzz0906 Aug 13, 2026
dce5e11
Build the sliding-window mixed-KV indices under CUDA graph
zzz0906 Aug 13, 2026
816869d
Give sliding layers their own stage-1 scratch under CUDA graph
zzz0906 Aug 13, 2026
b68a845
Let a model declare the PP proxy tensors CUDA graph must allocate
zzz0906 Aug 14, 2026
3fbbe43
Fall back to SDPA for int2 prefill when FlashAttention is unavailable
zzz0906 Aug 14, 2026
f8b2429
Add an unfused decode flush for non-power-of-two head dims
zzz0906 Aug 14, 2026
08eb038
Drop throwaway MLA kernel diagnostics
zzz0906 Aug 14, 2026
03b0733
Keep the MLA kernel test, drop the rest of rotation/investigation
zzz0906 Aug 14, 2026
438e37b
Drop the Kimi wikitext2 eval scripts
zzz0906 Aug 14, 2026
4141e2e
Add rotation recipes for MiniMax-M2.7, MiniMax-M3 and GLM-5.2-FP8
zzz0906 Aug 14, 2026
e920110
Default the MiniMax layer counts instead of requiring them
zzz0906 Aug 14, 2026
4326ee9
Fix the flag names in the MiniMax compute_rotation wrappers
zzz0906 Aug 14, 2026
36ac38c
Ship the GPQA eval harness as a per-model example
zzz0906 Aug 14, 2026
0bc2f16
Initialise the simple_evals submodule before a GPQA eval
zzz0906 Aug 14, 2026
a1a01ca
Stop forcing V-rotation absorption in the GPQA eval
zzz0906 Aug 14, 2026
a62e17b
Point the Qwen3-30B-A3B eval example at its per-head rotations
zzz0906 Aug 15, 2026
03f648a
Wire the MLA latent rotation through the GPQA eval
zzz0906 Aug 15, 2026
0f68a51
Do not force int2 kv-cache-dtype on the MLA eval path
zzz0906 Aug 15, 2026
b9890e3
Drop --kv-cache-quant-group-size on the MLA eval path too
zzz0906 Aug 15, 2026
7d4e9cf
Build the optional server flags as arrays, not strings
zzz0906 Aug 15, 2026
bc4762e
Let the GPQA example run multi-node
zzz0906 Aug 16, 2026
8754c9b
Define GROUP_SIZE before the branch that reads it
zzz0906 Aug 17, 2026
02057ff
Respect a caller-activated venv in the GPQA eval
zzz0906 Aug 17, 2026
82f8a7e
Lower Gemma-4's default KV pool cap to 65536 tokens
zzz0906 Aug 17, 2026
d9564c1
Set the global attention backend for Gemma-4, not just prefill/decode
zzz0906 Aug 17, 2026
8e09118
Non-zero ranks serve only; do not drive the eval
zzz0906 Aug 17, 2026
3db0b3b
Make the eval's attention backend overridable, and set it for Gemma-4
zzz0906 Aug 17, 2026
d8760c1
Pin Lloyd-Max off in the GPQA eval instead of inheriting it
zzz0906 Aug 17, 2026
61b8842
Turn Lloyd-Max on for Qwen3.5-35B-A3B's eval
zzz0906 Aug 17, 2026
bdfb297
Keep c_kv's dtype when re-joining it with k_pe
zzz0906 Aug 18, 2026
27d4948
Widen k_pe instead of narrowing the quantized c_kv
zzz0906 Aug 18, 2026
271e231
Remove dead dtype-widening branch in MLA int2 setter
zzz0906 Aug 18, 2026
bf3c51a
Copy the MLA quantizer result out of shared scratch
zzz0906 Aug 19, 2026
7e57ee9
Restore --disable-radix-cache in the GPQA eval harness
zzz0906 Aug 19, 2026
c4f6524
Rotate the MLA latent in a dtype that can hold a rotation
zzz0906 Aug 19, 2026
4724a06
Pin the MLA latent cache dtype instead of inheriting the GPU generation
zzz0906 Aug 19, 2026
c468c95
Make the radix cache safe for mixed-KV tiering
zzz0906 Aug 20, 2026
a5bdf79
Keep the scheduler capacity in step with the reserved HP-prefix sink …
zzz0906 Aug 20, 2026
b2a706a
Tidy the mixed-KV radix test helpers
zzz0906 Aug 20, 2026
42177db
Name both radix/mixed-KV defects in the eval harness comment
zzz0906 Aug 20, 2026
4b06c05
Document the mixed-KV auditor's runtime cost
zzz0906 Aug 20, 2026
c864999
Give the MLA/NSA latent pool the BF16 sink and recent windows
zzz0906 Aug 20, 2026
28bd64e
Read the latent tier in the basis the levels actually live in
zzz0906 Aug 20, 2026
c0f1fb5
Default the latent BF16 windows to the 64/256 INT2 floor
zzz0906 Aug 20, 2026
dbd000f
Replay the latent demote through a real CUDA graph in the test
zzz0906 Aug 20, 2026
c2101cd
Stop telling readers the latent path has no BF16 windows
zzz0906 Aug 20, 2026
65e071e
Prove the latent auditor can see a broken window
zzz0906 Aug 20, 2026
c882851
Stop the demote when the write path could not place the windows
zzz0906 Aug 20, 2026
dcedbfd
List all seven invariants the latent window test now pins
zzz0906 Aug 20, 2026
83749bb
Log every latent write path, with the window config on the line
zzz0906 Aug 20, 2026
982c48f
Refuse the extend demote under capture, and build its index once
zzz0906 Aug 20, 2026
c6483f7
MambaRadixCache: accept any base allocator, refuse mixed-KV loudly
zzz0906 Aug 20, 2026
3f61a73
Share the mixed-KV tier invariants with MambaRadixCache
zzz0906 Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 18 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,26 @@ rotation/*/MATH/
rotation/*/MMLU/
rotation/*/Hadamard/
rotation/*/qkv_dumps*/
rotation/*/rotations*/
rotation/*/_eval_*/
rotation/*/investigation/

# MiniMax-M2.7 is internal-only for now (per-investigation work-in-progress).
rotation/MiniMax-M2.7/
rotation/*/bench_matrix/
rotation/*/results/

# Triton / CUDA JIT caches
triton_cache/
deepgemm_cache/
*.cubin
*.ptx
*.llir
*.ttir
*.ttgir

# Eval run dirs (smoke tests, wandb run artifacts)
rotation/*/_*_smoke/
rotation/*/runs/

# Anonymous mirror dir
anonymous/

# Python caches
__pycache__/
Expand Down
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,25 @@ OSCAR is built directly into the open-source SGLang framework (main branch), lla
<details>
<summary><b>Qwen3.5-4B, Qwen3.5-35B-A3B, MiniMax 2.7 Preview</b> </summary>

Qwen3.5
| Model | Mode | GPQA (198) | Δ vs BF16 |
|-------|------|------------|-----------|
| Qwen3.5-4B | baseline | **75.25%** | — |
| Qwen3.5-4B | OSCAR | **74.75%** | −0.50 pp |
| Qwen3.5-35B-A3B | baseline | **80.30%** | — |
| Qwen3.5-35B-A3B | OSCAR | **82.32%** | +2.02 pp |
Qwen3.5 — BF16 vs OSCAR INT2 KV (2-bit, sink 64 / recent 256), mean ± std over 3 seeds (35B-A3B AIME: 8 seeds, N=30 is high-variance). OSCAR quantizer per model best: 4B uniform, 35B-A3B Lloyd-Max.

**Qwen3.5-4B**
| Benchmark | BF16 | OSCAR | Δ vs BF16 |
|---|:---:|:---:|:---:|
| GPQA-Diamond | 76.9 ± 1.3 | **75.8 ± 1.6** | −1.2 |
| HumanEval | 81.7 ± 1.8 | **83.9 ± 1.0** | +2.2 |
| AIME 2025 | 47.8 ± 3.1 | **46.7 ± 0.0** | −1.1 |
| MATH500 | 89.5 ± 0.6 | **88.0 ± 0.6** | −1.5 |

**Qwen3.5-35B-A3B**
| Benchmark | BF16 | OSCAR | Δ vs BF16 |
|---|:---:|:---:|:---:|
| GPQA-Diamond | 83.3 ± 1.8 | **84.0 ± 1.3** | +0.7 |
| HumanEval | 83.9 ± 0.6 | **86.6 ± 1.8** | +2.6 |
| AIME 2025 † | 66.7 ± 5.3 | **62.1 ± 4.7** | −4.6 |
| MATH500 | 92.8 ± 0.2 | **91.7 ± 0.4** | −1.1 |

<sub>† AIME N=30 is high-variance; measured over 8 seeds. The −4.6 gap is not statistically significant (Welch t=1.72). At 3 seeds it read −6.7, inflated by a favorable BF16 draw.</sub>

MiniMax2.7
| Benchmark | BF16 | OSCAR (LM_RATIO=1.16) | Δ |
Expand Down Expand Up @@ -301,6 +313,16 @@ Calibration-pipeline folders included on this branch (`rotation/<model>/`):
| `rotation/qwen3-8B/` | `Qwen/Qwen3-8B` | 1 | 1 | |
| `rotation/qwen3-32B/` | `Qwen/Qwen3-32B` | 2-4 | 4 | |
| `rotation/GLM-4.7/` | `zai-org/GLM-4.7-FP8` | 8 | 8 | FP8 weights, 92 layers |
| `rotation/gemma-4-12B-it/` | `google/gemma-4-12B-it` | 1 | 1 | `gemma4_unified` hybrid-SWA, dual head_dim (sliding 8×256 / full 1×512), all INT2; needs transformers ≥5.5; INT2 ≈ BF16 on GPQA (62.63%); optional vision via `--enable-multimodal` |

### Per-model GPQA: BF16 vs OSCAR INT2

Single-seed GPQA-Diamond (198) — full-precision baseline vs OSCAR INT2 KV cache, with the calibration tag used.

| Model | Calibration | GPQA (BF16) | GPQA (OSCAR INT2) |
|---|---|---|---|
| `Qwen/Qwen3-4B-Thinking-2507` | `seq20000_prompt83_group128` | 67.27 | 67.17 |
| `google/gemma-4-12B-it` | `seq30000_prompt134_group128` | 62.63 | 62.63 |

> MiniMax-M2.7 / Qwen3.5 calibration scripts live on `zhongzhu/hybrid-model`; pre-fit rotations for several models are available on the [RotationZoo](https://huggingface.co/Zhongzhu/OSCAR-RotationZoo).

Expand Down
15 changes: 15 additions & 0 deletions rotation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
io_log.jsonl
server.log
runner.log
pod.log
triton_cache/
triton_cache_head/
__pycache__/
bench_matrix/**/lmeval/
eval.log
GLM-5.1-FP8/
qwen3-32B/rotations/
bench_bf16_vs_int2/
investigation/*/upproj_rotations/
investigation/*/hp128_subspace/
investigation/*/qkv_dumps*/
112 changes: 112 additions & 0 deletions rotation/GLM-5.2-FP8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# GLM-5.2-FP8 — INT2 KV recipe (MLA shared latent)

MLA stores one compressed latent `c_kv` (512-d) shared by all heads, plus a
positional `k_pe`. Only `c_kv` is quantized; `k_pe` stays BF16. Per-head
rotations do not apply here — there is nothing per-head to rotate.

Rotation is `Rcov · P · Hblock` (covariance eigenvectors, bit-reversal
permutation, per-group Hadamard) with **Lloyd-Max** on, group 128.

The BF16 sink and recent windows apply here like everywhere else: sink 64,
recent 256, on by default. That was not true until `NSAInt2HPKVPool` /
`MLAInt2HPKVPool` learned to read `SGLANG_MIXED_KV_PREFIX_TOKENS` and
`SGLANG_MIXED_KV_RECENT_TOKENS` — before that they quantized every latent
token, including the attention sink and the newest ones, and this file told
you setting those vars changed nothing. **Every number below predates the
windows**, so read them as the no-window arm, not as the recipe.

| Benchmark | BF16 | INT2 (no window) | Δ |
|---|:---:|:---:|:---:|
| GPQA @32K | 81.3 ± 0.0 | 68.7 ± 1.8 | −12.6 |
| HumanEval (≤16K) | 91.5 ± 1.2 | 88.2 ± 1.5 | −3.3 |
| AIME 2025 | 76.7 ± 3.3 | 75.6 ± 6.9 | −1.1 (n.s.) |
| MATH500 | 94.8 ± 0.8 | 93.8 ± 0.3 | −1.0 |

**The −12.6 at 32K is truncation, not wrong answers.** On questions both arms
answer, they pick the same option 97.1 % of the time; INT2 simply thinks 1.69×
longer, so the truncation rate goes 14 % → 28 %. BF16 itself gains 6.6 points
moving from a 32K to a 64K budget, which confirms the metric is budget-bound.
Give this model a generous `max_tokens` before reading a regression into it.
The INT2 @64K cell is not measured.

## Do not expect a better rotation to close it

Measured on a real `c_kv` dump (functional error, lower is better):

| variant | error | vs no rotation |
|---|:---:|:---:|
| no rotation | 0.0845 | — |
| Hadamard | 0.0839 | ≈ 0 |
| **`Rcov·P·Hblock`** | **0.0523** | −0.72 dB |
| Hadamard + HP128 | 0.0447 | −2.74 dB |

Only the last row reaches BF16 parity, and it costs ~6.5 bits per element, which
defeats the point of 2-bit. The latent is a *trained* compressed representation —
it has no outlier structure for a rotation to flatten. `Rcov·P·Hblock` is the
best of the cheap options; take it and move on.

## Steps

```bash
# 1. dump c_kv on GPQA (TP=16, 2 nodes)
bash save_ckv_glm52.sh

# 2. fit the per-layer latent rotation
bash compute_rotation.sh # -> rotations/layer_*.pt
```

Fitted rotations are published as
`Zhongzhu/OSCAR-RotationZoo/GLM-5.2-FP8/c_kv_rcov_phblock_g128/` — download
rather than re-fit unless you are changing the recipe.

## Serving

```bash
SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1 \
SGLANG_LLOYD_MAX=1 \
SGLANG_OSCAR_MLA_KV_ROTATION_PATH=<rotations-dir> \
SGLANG_OSCAR_MLA_KV_GROUP_SIZE=128 \
python -m sglang.launch_server --model-path zai-org/GLM-5.2-FP8 \
--tensor-parallel-size 16 --nnodes 2 --node-rank <0|1> \
--dist-init-addr <head>:20000 \
--kv-cache-dtype bfloat16 --disable-radix-cache \
--mem-fraction-static 0.85
```

Three flags in that command are load-bearing and were each wrong in an earlier
version of this file:

* `SGLANG_OSCAR_MLA_KV_ROTATION_PATH` is the name the pool reads. It is also
what *creates* the pool — misspell it (this file said
`SGLANG_OSCAR_MLA_ROTATION_DIR`) and the server starts, serves, and scores
with a plain BF16 latent cache and no rotation at all.
* `--kv-cache-dtype bfloat16`, not `int2`. `int2` aborts at argument validation
on a DSA model; the latent is fake-quantized into a float cache, so that
cache stays float. Leaving it at `auto` is worse than either: sglang picks
fp8_e4m3 on SM100+ and bfloat16 on Hopper and below, which silently changes
the method with the GPU generation. Pinning it is what makes a B200 run
comparable to the H100 numbers in the table above.
* No `--page-size`. A DSA model forces 64; the 8 this file used to pass was
ignored.

The BF16 windows need no flag — 64/256 is the default the pool applies when
`SGLANG_MIXED_KV_PREFIX_TOKENS` / `SGLANG_MIXED_KV_RECENT_TOKENS` are unset,
because the two vars' own defaults are the per-head pool's older 32/128 and
inheriting those would put this path below the floor. Raise the recent window
for a model that needs more (Gemma-4 and Qwen3-8B use 512). Setting both to 0
turns the windows off; the server logs a warning when you do, since that is an
A/B arm and not a serving configuration. Confirm the line

```
[Int2HPKVPool] BF16 latent windows: sink=64 recent=256 ...
```

in the server log — its absence means the pool fell back to quantizing every
latent token. `SGLANG_MIXED_KV_AUDIT=1` additionally checks each live
request's tiers against the windows every 25 decode steps (it syncs the decode
path — never leave it on for a timed run).

Set `SGLANG_OSCAR_MLA_KV_REAL_KERNEL=1` to use the packed-INT2 latent kernel
instead of the fake-quant path: bit-identical packing, decode cosine 1.000000,
1024 → 160 B/token (**6.4×**). It is correct but not yet fast — measure before
enabling it for throughput.
27 changes: 27 additions & 0 deletions rotation/GLM-5.2-FP8/compute_rotation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Fit the per-layer MLA latent rotation (Rcov . P . Hblock) for GLM-5.2-FP8.
#
# Input is a c_kv dump: one tensor per layer of the 512-d compressed latent,
# captured while serving. Point DUMP_PATH at it. Unlike the MHA models there is
# no per-head structure here -- the latent is shared across heads.
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
COMPUTE_SCRIPT="${SCRIPT_DIR}/../compute_mla_oscar_rotation.py"

DUMP_PATH="${DUMP_PATH:?set DUMP_PATH to the c_kv dump directory}"
OUTPUT_DIR="${OUTPUT_DIR:-${SCRIPT_DIR}/GPQA/_rcov_phblock/rotations}"
KV_LORA_RANK="${KV_LORA_RANK:-512}"
GROUP_SIZE="${GROUP_SIZE:-128}"
MAX_TOKENS="${MAX_TOKENS:-8192}"
mkdir -p "${OUTPUT_DIR}"

python3 "${COMPUTE_SCRIPT}" \
--dump-path "${DUMP_PATH}" \
--output-dir "${OUTPUT_DIR}" \
--kv-lora-rank "${KV_LORA_RANK}" \
--group-size "${GROUP_SIZE}" \
--max-tokens "${MAX_TOKENS}" \
"$@"

echo "rotations -> ${OUTPUT_DIR}"
ls "${OUTPUT_DIR}" | head
22 changes: 22 additions & 0 deletions rotation/GLM-5.2-FP8/eval_gpqa.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# GPQA eval wrapper for zai-org/GLM-5.2-FP8, CUDA graph on (the configuration all published
# OSCAR GPQA numbers were measured under). Override CUDA_GRAPH_MAX_BS to change it.
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"

export MODEL="${MODEL:-zai-org/GLM-5.2-FP8}"
# MLA: the rotation is per-layer latent files (layer_*.pt), not a k/v pair, and
# is passed through MLA_ROT_PATH -- ROT_DIR/K_ROT_FILENAME do not apply here.
export MLA_ROT_PATH="${MLA_ROT_PATH:-${SCRIPT_DIR}/GPQA/_rcov_phblock/rotations}"
export ROT_DIR="${ROT_DIR:-${MLA_ROT_PATH}}"
export RUN_DIR="${RUN_DIR:-$(dirname "${ROT_DIR}")/_eval_gpqa_oscar}"
export TP_SIZE="${TP_SIZE:-16}"
export GROUP_SIZE="${GROUP_SIZE:-128}"
export K_CLIP="${K_CLIP:-0.96}"
export V_CLIP="${V_CLIP:-0.92}"
export CUDA_GRAPH_MAX_BS="${CUDA_GRAPH_MAX_BS:-32}"
export LLOYD_MAX="${LLOYD_MAX:-1}" # GLM-5.2 recipe uses Lloyd-Max
export NAME="${NAME:-gpqa_oscar_glm_5_2}"
# MLA latent path: rotations are per-layer c_kv files, not k/v pairs.
export MEM_FRACTION_STATIC="${MEM_FRACTION_STATIC:-0.85}"
exec bash "${SCRIPT_DIR}/../eval_oscar_gpqa.sh"
59 changes: 59 additions & 0 deletions rotation/MiniMax-M2.7/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# MiniMax-M2.7 — INT2 KV recipe

Dense MHA. Shared per-layer rotation (`qqt_r_h_pbr` for K, `sst_r_h_pbr` for V)
with the **uniform** quantizer — Lloyd-Max off.

| Benchmark | BF16 | INT2 | Δ |
|---|:---:|:---:|:---:|
| GPQA | 78.28 | 80.3 | +2.0 |
| HumanEval | 88.17 | 92.1 | +3.9 |
| AIME 2025 (95K budget) | 76.67 | 90.0 | +13.3 |
| MATH500 | 93.79 | 94.6 | +0.8 |
| SWE-bench-Verified | – | 70.8 | – |
| LiveCodeBench v6 (95K) | – | 68.4 | – |

Read the deltas with care: the BF16 column came from the model authors under a
different harness and seed count, so these are **not** paired measurements. The
honest claim is that INT2 lands in the same band as BF16 on all four, not that
it beats it. LCB 68.4 is a hard-window effect, not quantization — GLM-4.7 in
BF16 scores 65.5 on the same window.

## Two settings that are not optional

- **Mixed-KV windows must be on.** With `SGLANG_ENABLE_MIXED_KV_WINDOWS=0` this
model degenerates into repetition loops.
- **Budget 95K generation tokens.** M2.7 is a long-thinking model; a smaller
budget measures truncation, not accuracy. AIME and LCB above both use 95K.

## Steps

```bash
# 1. dump post-RoPE Q/K/V on GPQA (TP=4)
bash save_qkv_m27.sh

# 2. fit
bash compute_rotation.sh # qqt_sst / r_h_pbr
```

## Serving

```bash
SGLANG_ENABLE_MIXED_KV_WINDOWS=1 SGLANG_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1 \
SGLANG_MIXED_KV_PREFIX_TOKENS=64 SGLANG_MIXED_KV_RECENT_TOKENS=256 \
SGLANG_MIXED_KV_HP_DTYPE=bfloat16 SGLANG_MIXED_KV_SCALE_DTYPE=float32 \
SGLANG_OSCAR_ABSORB_V_ROTATION=0 SGLANG_LLOYD_MAX=0 \
SGLANG_OSCAR_K_CLIP_RATIO=0.96 SGLANG_OSCAR_V_CLIP_RATIO=0.92 \
SGLANG_OSCAR_K_ROTATION_PATH=<out>/k_rotation_qqt_r_h_pbr.pt \
SGLANG_OSCAR_V_ROTATION_PATH=<out>/v_rotation_sst_r_h_pbr.pt \
python -m sglang.launch_server --model-path MiniMaxAI/MiniMax-M2.7 \
--tensor-parallel-size 4 \
--kv-cache-dtype int2 --page-size 8 --disable-radix-cache \
--tool-call-parser minimax-m2
```

## Evaluating it

If you serve with `--cuda-graph-max-bs 1`, drive the eval client with **one**
thread. Two threads collapse throughput from ~115 tok/s to ~8 tok/s, because
every concurrent batch misses the single captured graph shape and falls back to
eager. This looks like a hung eval rather than a configuration mistake.
42 changes: 42 additions & 0 deletions rotation/MiniMax-M2.7/compute_rotation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Fit the shared per-layer K/V rotation for MiniMaxAI/MiniMax-M2.7.
#
# METHOD=qqt_sst (default) calibrated from a Q/K/V dump (DUMP_PATH)
# METHOD=hadamard data-free, no dump needed
#
# Outputs into OUTPUT_DIR:
# qqt_sst -> k_rotation_qqt_r_h_pbr.pt, v_rotation_sst_r_h_pbr.pt
# hadamard -> k_rotation_hadamard.pt, v_rotation_hadamard.pt
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
COMPUTE_SCRIPT="${SCRIPT_DIR}/../compute_kv_rotation.py"

METHOD="${METHOD:-qqt_sst}"
COMPOSITION="${COMPOSITION:-r_h_pbr}"
HEAD_DIM="${HEAD_DIM:-128}"
NUM_LAYERS="${NUM_LAYERS:-62}" # MiniMax-M2.7

DATASET="${DATASET:-GPQA}"
CALIB_DIR="${CALIB_DIR:-${SCRIPT_DIR}/${DATASET}/latest}"
DUMP_PATH="${DUMP_PATH:-${CALIB_DIR}/qkv_dumps_merged}"
OUTPUT_DIR="${OUTPUT_DIR:-${CALIB_DIR}/rotations}"
mkdir -p "${OUTPUT_DIR}"

ARGS=(--method "${METHOD}"
--num-layers "${NUM_LAYERS}"
--head-dim "${HEAD_DIM}"
--output-dir "${OUTPUT_DIR}")

# hadamard is data-free; only the calibrated method reads a dump.
if [[ "${METHOD}" != "hadamard" ]]; then
if [[ ! -d "${DUMP_PATH}" ]]; then
echo "no dump at ${DUMP_PATH} -- run save_qkv_m27.sh first" >&2
exit 1
fi
ARGS+=(--dump-path "${DUMP_PATH}" --composition "${COMPOSITION}")
fi

python3 "${COMPUTE_SCRIPT}" "${ARGS[@]}" "$@"

echo "rotations -> ${OUTPUT_DIR}"
ls -la "${OUTPUT_DIR}"
20 changes: 20 additions & 0 deletions rotation/MiniMax-M2.7/eval_gpqa.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# GPQA eval wrapper for MiniMaxAI/MiniMax-M2.7, CUDA graph on (the configuration all published
# OSCAR GPQA numbers were measured under). Override CUDA_GRAPH_MAX_BS to change it.
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"

export MODEL="${MODEL:-MiniMaxAI/MiniMax-M2.7}"
export ROT_DIR="${ROT_DIR:-${SCRIPT_DIR}/rotations}"
export RUN_DIR="${RUN_DIR:-$(dirname "${ROT_DIR}")/_eval_gpqa_oscar}"
export TP_SIZE="${TP_SIZE:-4}"
export GROUP_SIZE="${GROUP_SIZE:-128}"
export K_CLIP="${K_CLIP:-0.96}"
export V_CLIP="${V_CLIP:-0.92}"
export CUDA_GRAPH_MAX_BS="${CUDA_GRAPH_MAX_BS:-1}"
export NAME="${NAME:-gpqa_oscar_minimax_m27}"
# M2.7 is a long-thinking model: budget generation, and with a single captured
# graph shape the eval client must be single-threaded (115 tok/s vs 8 at two).
export MAX_NEW_TOKENS="${MAX_NEW_TOKENS:-95000}"
export NUM_WORKERS="${NUM_WORKERS:-1}"
exec bash "${SCRIPT_DIR}/../eval_oscar_gpqa.sh"
Loading