mlx-bun 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/README.md +71 -588
  2. package/docs/README.md +91 -0
  3. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  4. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  5. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  6. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  7. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  8. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  9. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  10. package/docs/design/batching.md +569 -0
  11. package/docs/design/decode-speed-program.md +3178 -169
  12. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  13. package/docs/design/generic-model-support.md +579 -334
  14. package/docs/design/kv-cache.md +680 -0
  15. package/docs/design/orpo-training.md +673 -1239
  16. package/docs/design/speculative-decoding.md +1627 -0
  17. package/docs/design/turboquant.md +2102 -0
  18. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  19. package/docs/design/web-chat-redesign.md +666 -1083
  20. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  21. package/docs/planning/release-notes-v0.3.0.md +35 -0
  22. package/docs/planning/video-series.md +2 -2
  23. package/docs/reference/benchmarks.md +1162 -0
  24. package/docs/reference/cli.md +394 -159
  25. package/docs/reference/distribution.md +321 -182
  26. package/docs/reference/environment.md +348 -0
  27. package/docs/reference/glossary.md +39 -0
  28. package/docs/reference/library-api.md +566 -168
  29. package/docs/reference/memory.md +227 -64
  30. package/docs/reference/models.md +104 -6
  31. package/docs/reference/server-api.md +886 -425
  32. package/docs/reference/server-config.md +638 -330
  33. package/docs/reference/training.md +393 -254
  34. package/docs/reference/troubleshooting.md +166 -0
  35. package/package.json +9 -4
  36. package/src/audio/decode.ts +2 -2
  37. package/src/audio/features.ts +2 -2
  38. package/src/backends/mlx/autoregressive.ts +108 -0
  39. package/src/backends/mlx/batch-group.ts +1646 -0
  40. package/src/backends/mlx/diffusion.ts +39 -0
  41. package/src/backends/mlx/draft-target.ts +47 -0
  42. package/src/backends/mlx/evaluation.ts +105 -0
  43. package/src/backends/mlx/fill-append.ts +45 -0
  44. package/src/backends/mlx/gateway-binding.ts +71 -0
  45. package/src/backends/mlx/graph.ts +38 -0
  46. package/src/backends/mlx/memory-client.ts +189 -0
  47. package/src/backends/mlx/memory-guard.ts +40 -0
  48. package/src/backends/mlx/methods.ts +90 -0
  49. package/src/backends/mlx/model-prompt.ts +164 -0
  50. package/src/backends/mlx/model-serving.ts +79 -0
  51. package/src/backends/mlx/prefill.ts +33 -0
  52. package/src/backends/mlx/request-policy.ts +20 -0
  53. package/src/backends/mlx/rollback.ts +51 -0
  54. package/src/backends/mlx/serial-executor.ts +273 -0
  55. package/src/backends/mlx/speculative.ts +114 -0
  56. package/src/backends/mlx/state-views.ts +26 -0
  57. package/src/backends/mlx/text-engine.ts +73 -0
  58. package/src/cli.ts +58 -25
  59. package/src/client.ts +66 -0
  60. package/src/config.ts +21 -4
  61. package/src/contracts/completion.ts +16 -0
  62. package/src/contracts/execution.ts +49 -0
  63. package/src/contracts/generation.ts +103 -0
  64. package/src/contracts/host.ts +7 -0
  65. package/src/contracts/jobs.ts +66 -0
  66. package/src/contracts/pi-provider.ts +30 -0
  67. package/src/contracts/pi.ts +259 -0
  68. package/src/contracts/resources.ts +18 -0
  69. package/src/contracts/scheduling.ts +27 -0
  70. package/src/contracts/tsconfig.json +14 -0
  71. package/src/diffusion/diffusion-generate.ts +218 -153
  72. package/src/embed.ts +1 -1
  73. package/src/engine/admission.ts +59 -0
  74. package/src/engine/cancellation.ts +38 -0
  75. package/src/engine/engine.ts +56 -0
  76. package/src/engine/execution-coordinator.ts +70 -0
  77. package/src/engine/execution-plan.ts +51 -0
  78. package/src/engine/resources.ts +42 -0
  79. package/src/engine/scheduler.ts +67 -0
  80. package/src/engine/session.ts +247 -0
  81. package/src/eval/README.md +44 -1
  82. package/src/eval/kl.ts +236 -3
  83. package/src/eval/reference-logits.ts +207 -0
  84. package/src/eval/runner.ts +8 -96
  85. package/src/fill/echo-index.ts +188 -0
  86. package/src/fill/fill-session.ts +452 -0
  87. package/src/fill/proposal.ts +68 -0
  88. package/src/fill/schema-rows.ts +506 -0
  89. package/src/fill/tool-boundary.ts +103 -0
  90. package/src/fit.ts +1 -1
  91. package/src/generate.ts +656 -268
  92. package/src/grammar.ts +12 -7
  93. package/src/harness-pi.ts +14 -33
  94. package/src/hub-rest.ts +4 -4
  95. package/src/index.ts +7 -0
  96. package/src/inference/denoising.ts +17 -0
  97. package/src/inference/graph.ts +33 -0
  98. package/src/inference/prefill.ts +39 -0
  99. package/src/inference/rollback.ts +7 -0
  100. package/src/jobs/db.ts +4 -0
  101. package/src/jobs/index.ts +2 -0
  102. package/src/jobs/job-entry.ts +12 -12
  103. package/src/jobs/runner.ts +98 -13
  104. package/src/jobs/task-client.ts +19 -0
  105. package/src/jobs/types.ts +2 -58
  106. package/src/kv-store.ts +109 -49
  107. package/src/lab/README.md +18 -0
  108. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  109. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  110. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
  111. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  112. package/src/library.ts +40 -0
  113. package/src/lora.ts +18 -0
  114. package/src/media-fetch.ts +1 -1
  115. package/src/memory/article.ts +1 -1
  116. package/src/memory/chunk.ts +1 -1
  117. package/src/memory/model.ts +30 -169
  118. package/src/memory/prompts.ts +1 -1
  119. package/src/memory/rest.ts +1 -1
  120. package/src/memory/schedule.ts +1 -1
  121. package/src/memory/stages.ts +4 -1
  122. package/src/memory/vault.ts +1 -3
  123. package/src/mlx/array.ts +9 -10
  124. package/src/mlx/compile.ts +12 -2
  125. package/src/mlx/ffi.ts +58 -11
  126. package/src/mlx/materialize.ts +23 -0
  127. package/src/mlx/metal-kernel.ts +10 -11
  128. package/src/mlx/ops.ts +33 -13
  129. package/src/mlx/quantized-rows.ts +36 -0
  130. package/src/mlx/token-bitmask.ts +52 -0
  131. package/src/mlx/turboquant-kv-decode.ts +82 -0
  132. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  133. package/src/mlx/turboquant-ops.ts +33 -3
  134. package/src/mlx/turboquant-tables.ts +2 -2
  135. package/src/model/batched-mask.ts +8 -2
  136. package/src/model/batched-quant.ts +1 -1
  137. package/src/model/batched-rotating-quant.ts +2 -2
  138. package/src/model/batched-rotating.ts +1 -1
  139. package/src/model/compiled-decode.ts +1 -1
  140. package/src/model/diffusion-gemma.ts +8 -8
  141. package/src/model/factory.ts +76 -33
  142. package/src/model/fingerprint.ts +1 -1
  143. package/src/model/flash-attention.ts +3 -3
  144. package/src/model/gemma4-base.ts +26 -9
  145. package/src/model/gemma4.ts +6 -6
  146. package/src/model/generated/gemma4-12b.ts +2 -2
  147. package/src/model/generated/gemma4-26b.ts +2 -2
  148. package/src/model/generated/gemma4-e4b.ts +2 -2
  149. package/src/model/glm52-dsa.ts +13 -9
  150. package/src/model/glm52-memory.ts +1 -1
  151. package/src/model/implementation.ts +69 -0
  152. package/src/model/minicpm5.ts +6 -8
  153. package/src/model/profile.ts +12 -2
  154. package/src/model/qwen-append.ts +12 -0
  155. package/src/model/qwen-conv.ts +52 -0
  156. package/src/model/qwen3_5-checkpoint.ts +164 -0
  157. package/src/model/qwen3_5.ts +140 -56
  158. package/src/model/support.ts +1 -1
  159. package/src/model/trellis-balanced-scatter.ts +51 -0
  160. package/src/model/trellis-linear.ts +672 -0
  161. package/src/model/trellis-shared-m.ts +151 -0
  162. package/src/model/trellis-shared-scatter.ts +144 -0
  163. package/src/model/trellis-splitk-prefill.ts +158 -0
  164. package/src/model/trellis-tiled-prefill.ts +136 -0
  165. package/src/model/trellis-vector-expand.ts +62 -0
  166. package/src/model/trellis-wide-prefill.ts +94 -0
  167. package/src/model/universal/dense.ts +1 -1
  168. package/src/model/universal/modules.ts +8 -1
  169. package/src/native-pack.ts +6 -5
  170. package/src/pi-provider.ts +6 -64
  171. package/src/pi-web.ts +8 -315
  172. package/src/prompt-cache.ts +96 -53
  173. package/src/quantize/allocator.ts +1 -1
  174. package/src/quantize/config-writer.ts +10 -3
  175. package/src/quantize/rotate.ts +4 -4
  176. package/src/quantize/trellis.ts +586 -0
  177. package/src/responses.ts +28 -1
  178. package/src/runtime-config.ts +18 -4
  179. package/src/sampler.ts +4 -4
  180. package/src/serve/batch-scheduler.ts +8 -1671
  181. package/src/serve/chat-request.ts +391 -0
  182. package/src/serve/chat-stage.ts +195 -0
  183. package/src/serve/checkpoint-identity.ts +64 -0
  184. package/src/serve/completion-executor.ts +15 -7
  185. package/src/serve/discovery-routes.ts +12 -13
  186. package/src/serve/generation-gateway.ts +154 -193
  187. package/src/serve/http.ts +159 -0
  188. package/src/serve/inference-request.ts +79 -0
  189. package/src/serve/isolate.ts +297 -105
  190. package/src/serve/lab-routes.ts +3 -0
  191. package/src/serve/lane-registry.ts +2 -1
  192. package/src/serve/model-admin-routes.ts +7 -5
  193. package/src/serve/model-binding.ts +43 -0
  194. package/src/serve/model-host.ts +512 -0
  195. package/src/serve/openai-wire.ts +103 -0
  196. package/src/serve/parent-application.ts +56 -0
  197. package/src/serve/pipeline.ts +33 -0
  198. package/src/serve/preparation.ts +33 -0
  199. package/src/serve/request-plan.ts +19 -15
  200. package/src/serve/request-prep.ts +346 -0
  201. package/src/serve/response-lease.ts +24 -0
  202. package/src/serve/responses-client.ts +69 -0
  203. package/src/serve/session-completion-engine.ts +110 -0
  204. package/src/serve/session-search.ts +1 -1
  205. package/src/serve/static-routes.ts +5 -5
  206. package/src/serve/text-completion-stage.ts +104 -0
  207. package/src/serve/token-streams.ts +519 -0
  208. package/src/server.ts +317 -2649
  209. package/src/spec/assistant-source.ts +10 -44
  210. package/src/spec/deepspec-source.ts +6 -6
  211. package/src/spec/dflash-source.ts +8 -8
  212. package/src/spec/dspark/ab-stats.ts +3 -3
  213. package/src/spec/dspark/calibration.ts +2 -2
  214. package/src/spec/dspark/deepspec-module.ts +2 -2
  215. package/src/spec/dspark/generate-dflash.ts +2 -2
  216. package/src/spec/dspark/generate.ts +2 -2
  217. package/src/spec/dspark/loader.ts +1 -1
  218. package/src/spec/dspark/module-dflash.ts +4 -3
  219. package/src/spec/dspark/module.ts +3 -3
  220. package/src/spec/dspark/quantize-drafter.ts +1 -1
  221. package/src/spec/generate.ts +1 -1
  222. package/src/spec/glm52-mtp-source.ts +1 -1
  223. package/src/spec/ngram-source.ts +1 -1
  224. package/src/spec/prefix-state.ts +45 -0
  225. package/src/spec/qwen-mtp-source.ts +166 -69
  226. package/src/spec/serve-loop.ts +154 -156
  227. package/src/spec/source.ts +42 -10
  228. package/src/spec/two-model.ts +1 -1
  229. package/src/ssd-cache.ts +80 -12
  230. package/src/ssd-durability.ts +25 -13
  231. package/src/tool-call.ts +51 -14
  232. package/src/train/flash-cce.ts +2 -2
  233. package/src/train/forward.ts +6 -0
  234. package/src/train/loss.ts +1 -1
  235. package/src/train/prefix-shared.ts +5 -1
  236. package/src/train/segmented.ts +5 -3
  237. package/src/train/trainer.ts +6 -6
  238. package/src/vision/prompt.ts +1 -1
  239. package/src/vision/qwen3vl-tower.ts +52 -10
  240. package/src/web/app.html +1 -1
  241. package/src/web/app.js +5 -3
  242. package/src/web/src/adapters-panel.ts +3 -3
  243. package/src/web/src/assistant.ts +2 -36
  244. package/src/web/src/chat.ts +4 -4
  245. package/src/web/src/composer.ts +4 -4
  246. package/src/web/src/hub.ts +1 -1
  247. package/src/web/src/markdown.ts +0 -0
  248. package/src/web/src/memory-panel.ts +1 -1
  249. package/src/web/src/model-picker.ts +1 -1
  250. package/src/web/src/palette.ts +1 -1
  251. package/src/web/src/protocol.ts +3 -39
  252. package/src/web/src/sessions.ts +1 -1
  253. package/src/web/src/ui-catalog.ts +4 -18
  254. package/src/web-assets.ts +47 -0
  255. package/src/weights.ts +55 -9
  256. package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
  257. package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
  258. package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
  259. package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
  260. package/docs/archive/investigations/hlg-ab.html +0 -34
  261. package/docs/archive/investigations/hlg-explorer.html +0 -238
  262. package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
  263. package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
  264. package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
  265. package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
  266. package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
  267. package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
  268. package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
  269. package/docs/archive/investigations/hlg-report.html +0 -262
  270. package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
  271. package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  272. package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
  273. package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
  274. package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
  275. package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
  276. package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
  277. package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
  278. package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
  279. package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
  280. package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  281. package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
  282. package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
  283. package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
  284. package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  285. package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  286. package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  287. package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  288. package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  289. package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  290. package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  291. package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
  292. package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
  293. package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
  294. package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
  295. package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
  296. package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  297. package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
  298. package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
  299. package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
  300. package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
  301. package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
  302. package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  303. package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
  304. package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  305. package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  306. package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  307. package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  308. package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  309. package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  310. package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
  311. package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  312. package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
  313. package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  314. package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  315. package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  316. package/docs/archive/mlx-bun-lab-report.html +0 -275
  317. package/docs/archive/wwdc-mlx-bun.html +0 -848
  318. package/docs/design/adapters-end-to-end.md +0 -205
  319. package/docs/design/batching-perf-path.md +0 -255
  320. package/docs/design/batching-v2-plan.md +0 -529
  321. package/docs/design/bucketing-stage.md +0 -197
  322. package/docs/design/compat-cli-surface-design.md +0 -230
  323. package/docs/design/docs-reorg-plan.md +0 -165
  324. package/docs/design/dspark-serving-program.md +0 -371
  325. package/docs/design/dspark-speculative-decoding.md +0 -318
  326. package/docs/design/grammar-spec-batching-integration.md +0 -301
  327. package/docs/design/memory-inference-path.md +0 -422
  328. package/docs/design/memory-synthesis.md +0 -287
  329. package/docs/design/memory-system.md +0 -237
  330. package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
  331. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  332. package/docs/design/omlx-adoption-map.md +0 -98
  333. package/docs/design/orpo-dynamic-lambda.md +0 -220
  334. package/docs/design/orpo-future-enhancements.md +0 -340
  335. package/docs/design/paged-kv-cache.md +0 -214
  336. package/docs/design/parallel-slots.md +0 -567
  337. package/docs/design/parity-tier-dag.md +0 -161
  338. package/docs/design/repo-cleanup-plan.md +0 -231
  339. package/docs/design/runtime-isolation.md +0 -263
  340. package/docs/design/segmented-backward-training.md +0 -543
  341. package/docs/design/spec-decode-larger-targets.md +0 -229
  342. package/docs/design/ssd-kv-cold-tier.md +0 -415
  343. package/docs/design/structured-output.md +0 -484
  344. package/docs/design/tauri-desktop-app.md +0 -127
  345. package/docs/design/the-dreaming-master-plan.md +0 -445
  346. package/docs/design/turboquant-kv.md +0 -235
  347. package/docs/design/turboquant-weights.md +0 -467
  348. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  349. package/docs/design/web-chat-beat-matrix.md +0 -315
  350. package/docs/design/write-pipeline-entity.md +0 -468
  351. package/docs/planning/IDEAS.md +0 -69
  352. package/docs/reference/embedding.md +0 -197
  353. package/docs/reference/features-matrix.md +0 -101
  354. package/docs/reference/orpo-quickstart.md +0 -135
  355. package/src/flags.ts +0 -11
  356. /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
  357. /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
  358. /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
  359. /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
  360. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  361. /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
  362. /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
  363. /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
  364. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -0,0 +1,2102 @@
1
+ ---
2
+ status: active
3
+ axis: ON
4
+ canonical-for: turboquant
5
+ plan-anchor: "Phase: TurboQuant weights — rotation-folded quantization, Qwen3.8-27B target `[ ]` (opened 2026-08-17)"
6
+ last-verified: 2026-08-23
7
+ ---
8
+
9
+ # TurboQuant — rotation-based quantization (KV leg landed, weights leg open)
10
+
11
+ One canonical doc for both legs of the rotate-then-quantize program.
12
+ Sources folded in: `docs/design/turboquant.md` (KV, landed 2026-07-06)
13
+ and `docs/design/turboquant.md` (weights, open). Status/changelog
14
+ prose lives in PLAN.md under the plan-anchor above (weights) and the closed
15
+ Phase 13 entry (KV); this doc keeps mechanism, invariants, decisions with
16
+ rationale, measured curves, and open items.
17
+
18
+ ## Naming: TurboQuant is a KV method by origin
19
+
20
+ TurboQuant (arXiv:2504.19874, Google) is a KV-cache quantizer: rotate the
21
+ value vectors so their marginals are near-Gaussian, then Lloyd-Max quantize.
22
+ That is the leg we ship under `--kv-quant turbo`. The **weights leg is this
23
+ repo's extension** of the same mechanism to weight matrices — mechanically
24
+ it is the QuaRot/SpinQuant family (rotation folded offline into adjacent
25
+ weights, then quantized with mlx's stock formats), not anything the
26
+ TurboQuant paper describes. The shared name records the shared idea
27
+ (rotate → Gaussian marginal → better codebook coverage) and that both are
28
+ the QUANTIZER axis of the mixed-precision program: they compose with, and
29
+ never replace, per-layer allocation (OptiQ-style) and calibration.
30
+
31
+ | leg | what is rotated | when | oracle | state |
32
+ |---|---|---|---|---|
33
+ | KV | value vectors (keys affine, unrotated) | online, per appended token | vllm-metal `turboquant.py` | landed 2026-07-06 |
34
+ | weights | residual-stream basis of every linear | offline, folded into weights | QuaRot + SpinQuant fold code; mlx-lm GPTQ | open (PLAN "TurboQuant weights") |
35
+
36
+ ---
37
+
38
+ # Part A — KV leg (`--kv-quant turbo`)
39
+
40
+ ## What it is
41
+
42
+ Rotation-based KV-cache quantization: signed int8 affine keys + FWHT-rotated
43
+ 3-bit Lloyd-Max values, per-32-group fp16 scales. More usable context at the
44
+ same memory (2.56× KV compression at the k8/v3 default, head_dim 128). Owner
45
+ seam: L2 cache format, L1-unmodified attention (dequantize-on-fetch), opt-in
46
+ only — the naked default stays L1 (bf16 KV).
47
+
48
+ ## Oracle chain (decided 2026-07-06)
49
+
50
+ Two unrelated implementations share the name; do not conflate:
51
+
52
+ - **The paper** (arXiv:2504.19874): dense QR random rotation, no groups, no
53
+ K/V asymmetry, optional QJL residual stage. Anchors the WHY (rotate →
54
+ Gaussian marginal → Lloyd-Max optimal; 4^-b distortion law), not the layout.
55
+ - **vllm-metal** (`vllm-project/vllm-metal`,
56
+ `vllm_metal/attention/caches/turboquant.py` + `kernels_v2/turboquant.metal`):
57
+ the engineering variant. optiq's `runtime/mtp/turboquant.py` config layer is
58
+ lifted verbatim from it (byte-identical `CENTROIDS_3BIT`, matching op
59
+ signatures in optiq `cache_state.py`). **This is our oracle** — "the oracle
60
+ is whoever ships it." Its Python reference is pure MLX; we vendor it at
61
+ `lab/repro/vllm-metal-turboquant/turboquant_reference.py` (fetch provenance
62
+ in `PROVENANCE.md` beside it) and generate bit-exact goldens through the
63
+ oracle venv. Quirks are preserved on purpose: the port matches what the file
64
+ computes, not what its comments say.
65
+ - mlx-vlm's module is a THIRD design (whole-vector norms, MSE/Prod codecs,
66
+ keys rotated too) — not our target; its unit-vector MSE anchors
67
+ (≈{0.36, 0.117, 0.03} at bits {1,2,3}) remain useful sanity checks.
68
+ - vllm-metal's rendered docs claim keys are rotated; the code never rotates
69
+ keys. Trust the source.
70
+
71
+ ## Algorithm (vllm-metal reference semantics, exact)
72
+
73
+ Per token, per kv-head, groups of 32 along head_dim (head_dim ∈
74
+ {64,128,256,512} — `TURBOQUANT_HEAD_DIMS` in `src/mlx/turboquant-tables.ts`).
75
+
76
+ **Keys — NO rotation, asymmetric affine, per-32-group:**
77
+ - signed (int8, bits=8): `max_val=127; scale=(max−min)/(2·max_val);
78
+ zero=round((max+min)/(2·scale)); idx=clip(round(x/scale − zero), −127, 127)`,
79
+ one byte per element (no packing at 8 bits).
80
+ - unsigned (bits ∈ {2,4,5}): `max_val=2^bits−1; scale=(max−min)/max_val;
81
+ zero=round(min/scale); idx=clip(round(x/scale − zero), 0, max_val)`, packed.
82
+ - Dequant (both): `x̂=(idx+zero)·scale`; scale/zero fp16, one pair per group.
83
+ - The reference routes intermediates through fp16 and deliberately drops the
84
+ `+1e-8` scale epsilon (it underflows in fp16). Our port upcasts the affine
85
+ arithmetic to fp32 (production feeds bf16 K; bf16 arithmetic shifted
86
+ rounding-boundary indices vs the reference) and casts only scale/zero to
87
+ fp16 at the end — reproducing the reference's real defect that a
88
+ constant-valued block overflows zero_point to ±Inf. The golden test asserts
89
+ it (`src/mlx/turboquant-ops.ts` header).
90
+
91
+ **Values — FWHT + Lloyd-Max, per-32-group RMS scale:**
92
+ - `y = FWHT(x · signs)`, `signs ∈ {±1}^d` from
93
+ `mx.random.randint(0,2,(d,), key=mx.random.key(42))`; FWHT normalizes by 1/√d.
94
+ - `scale = sqrt(mean(y²))` per 32-group (RMS, not min/max); `y_n = y/(scale+1e-8)`.
95
+ - `idx = searchsorted(BOUNDARIES, y_n)`. 3-bit table: centroids
96
+ ±{0.24509, 0.75601, 1.34391, 2.15195}, boundaries ±{0.50055, 1.04996,
97
+ 1.74793}, 0. Other widths (2/4/5/8): the reference's iterative Lloyd-Max on
98
+ a unit normal (key(0), 500 iterations), generated ONCE and hardcoded —
99
+ parity by construction, no runtime iteration.
100
+ - Pack: flat little-endian bitstream, element i at bits [i·b, i·b+b); values
101
+ may straddle bytes (3-bit: 8 values → 3 bytes).
102
+ - Dequant: `x̂ = InvFWHT(centroids[idx] · scale · signs⁻¹)`.
103
+
104
+ **Bytes per token per head (k8v3, d=128):** 128 (K int8) + 8+8 (K
105
+ scale/zero) + 48 (V packed) + 8 (V scale) = 200 vs 512 bf16 → 2.56×. This
106
+ reproduces optiq's `compression_ratio`. Sign vectors are hardcoded constants
107
+ per head_dim (what vllm-metal does in its .metal source) — deterministic
108
+ across machines and spill/restore.
109
+
110
+ ## Architecture in mlx-bun (verified 2026-08-23)
111
+
112
+ Dequantize-on-fetch: `TurboQuantKVCache implements Cache`
113
+ (`src/model/gemma4-base.ts`; does NOT subclass KVCache, which keeps it
114
+ auto-excluded from batching). `updateAndFetch(k,v)` quantizes only the newly
115
+ appended tokens into packed storage and returns the dequantized bf16 active
116
+ window, so stock `ops.sdpa` runs unmodified — the pattern optiq's
117
+ `runtime/kv/rotating.py` uses. A novel cache class fails every generated-file
118
+ `#matches()` guard and falls back to the monolith by design. Quantization
119
+ groups run along head_dim only — token-axis slicing stays byte-safe
120
+ (standing invariant).
121
+
122
+ Code map:
123
+ - `src/mlx/turboquant-ops.ts` — fwht (binds `mlx_hadamard_transform`),
124
+ encode/decode keys + values, pack/unpack, `unrotateValues`.
125
+ - `src/mlx/turboquant-tables.ts` — GENERATED by `bun scripts/regen.ts
126
+ turboquant` (job file `scripts/regen/turboquant.ts`, which shells out to
127
+ `lab/repro/vllm-metal-turboquant/gen_goldens.py` under the oracle venv):
128
+ sign vectors per head_dim + Lloyd-Max tables per bit-width; also writes
129
+ `goldens/turboquant.json` (machine-independent, flat goldens set).
130
+ - `src/config.ts` — `TurboQuantScheme {kBits, vBits}`,
131
+ `parseTurboQuantScheme` (`turbo` = k8v3; `TURBOQUANT_VALID_KBITS`
132
+ {2,4,5,8}, `TURBOQUANT_VALID_VBITS` {2,3,4,5,8}).
133
+ - `src/cli.ts` — `--kv-quant turbo[:k<bits>v<bits>]` on serve/generate/bench.
134
+ - `src/kv-scheme.ts` — `KvScheme` kind `"turbo"`; `cacheKey` =
135
+ `turbo-k<k>v<v>` (the SSD-cache / prompt-cache anti-collision key);
136
+ `bytesAt` bills bf16 (conservative — no projector for the packed layout
137
+ yet); `fitOptions` is empty for turbo for the same reason.
138
+ - `src/generate.ts` `maybeQuantizeKv()` → `maybeTurboQuantizeKv()` — the
139
+ single conversion chokepoint (serve serial lane + generate + eval all flow
140
+ through it); `TurboQuantKVCache.fromKVCache`; `RotatingKVCache` layers stay
141
+ bf16 with a one-time warning; keeps the `quantizedKvStart` semantics.
142
+ - `src/server.ts` — startup refusals: `--batch N` + turbo (serial-only);
143
+ head_dim not in `TURBOQUANT_HEAD_DIMS` (fail-fast at `createServer`, not on
144
+ first append — 2026-07-07 review); `--draft-model` + any quantized KV warns
145
+ and those requests decode serially WITHOUT speculation (spec-eligibility
146
+ gate excludes `turboQuant`; the spec loop builds fresh bf16 caches and would
147
+ otherwise silently drop the scheme). `turboQuant` and `kvQuant` are
148
+ mutually exclusive (turbo wins with a warning).
149
+ - `src/serve/generation-gateway.ts` — explicit solo-only refusal in
150
+ placement (belt + braces on top of the instanceof exclusion).
151
+ - `src/kv-store.ts` — `CacheKind "turboquant"`; header carries kBits/vBits
152
+ + head_dim; snapshot/restore/clone branches.
153
+ - `src/model/fingerprint.ts` — NOT touched, by symmetry: uniform
154
+ `--kv-quant 4|8` is a CLI-only runtime lever that never feeds
155
+ `configFingerprint()` either; the anti-collision mechanism for this class
156
+ of lever is the scheme key above, not the fingerprint.
157
+ - Tests: `tests/turboquant-ops.test.ts` (golden bit-exactness vs the
158
+ vendored reference + model-free math props), `tests/turboquant-cache.test.ts`
159
+ (cache invariants, growth/trim, persistence roundtrip; synthetic arrays).
160
+ - Curve script: `scripts/turboquant/eval-turboquant-curve.ts` — bf16
161
+ baseline + {k8v8,k8v4,k8v3,k8v2,k4v3,k4v2} sequentially, reusing
162
+ `src/eval/kl.ts`'s `evaluateKlKvArm` (teacher-forced serving-decode KL,
163
+ real `fromKVCache` conversion) and `src/eval/perplexity.ts`'s `evalPpl`.
164
+ - Reference docs: server-config.md, cli.md, docs/reference/server-config.md carry the
165
+ flag surface.
166
+
167
+ ## Deferred inverse FWHT (landed 2026-07-06, post-v1)
168
+
169
+ Attention is linear in V, so `InvFWHT(Σᵢ wᵢ·v̂ᵢ) = Σᵢ wᵢ·InvFWHT(v̂ᵢ)`:
170
+ `updateAndFetchDeferredV` returns the V window still in the rotated domain
171
+ and the attention site un-rotates the OUTPUT once per query row
172
+ (`tq.unrotateValues`) — O(q·d log d) per step instead of O(T·d log d).
173
+ Opt-in per attention site via `SharedKv.vRotated` (monolith `gemma4.ts` —
174
+ KV-shared consumer layers inherit the flag through `sharedIn` — and
175
+ `minicpm5.ts`); every other consumer keeps calling `updateAndFetch` (eager)
176
+ and stays correct. Not bit-identical to eager (bf16 rounding in the rotated
177
+ domain before the transform); measured k8v3 KL 0.0338 vs eager 0.0325 —
178
+ same quality. Paired fetch-path A/B on a LOADED M1 Max: never slower,
179
+ ~10-20% off fetch cost at 4k ctx, noise-level at 8k (dequant gather
180
+ dominates both) — no speed claim until a quiet-machine
181
+ `scripts/bench-serve.ts all` run; the win is removing the O(T) transform,
182
+ which matters more once the gather itself is fused. (The class-level doc
183
+ comment on `TurboQuantKVCache` still calls deferred-InvFWHT a non-goal; the
184
+ method exists two screens below it — the comment is stale, the code is
185
+ authoritative.)
186
+
187
+ ## Packed-value decode fusion investigation
188
+
189
+ The Mac-quant video follow-up in decode-speed-program §7.11 exposed an
190
+ unpack/gather/scale chain that also exists in our shipped KV decoder. A
191
+ research-only Metal prototype reads the byte-packed values and fp16 group
192
+ scales directly from the active cache window, gathers the existing f32
193
+ Lloyd-Max centroids and multiplies in f32. The eager path keeps the native
194
+ inverse Hadamard, sign multiplication and bf16 cast. The deferred path casts
195
+ the same f32 values to bf16. Keys and the quantized representation stay the
196
+ same. The four-dimensional launch reads cache strides without copying its
197
+ capacity slice; context length, capacity and head count remain runtime inputs.
198
+ There is no specialization for each new token position.
199
+
200
+ On the M4 Pro 24 GB, the initial 140 cases preserve 280 flat/grid comparisons
201
+ across value bits 2/3/4/5/8, head dimensions 64/128/256/512, multiple batch/head
202
+ counts, strided cache windows and both rotation paths. Three six-process
203
+ repeats preserve another 1,440 candidate comparisons and every cross-process
204
+ fixture/output hash. These add fp16 scales and the production 32-value group
205
+ to the initial bf16/f32 and 64-value-group operation coverage. Every process
206
+ returns to its primed native sign/centroid-cache allocation. The all-dimension
207
+ screen retains 5,104 bytes; each D256 repeat retains 2,288 bytes. The generated
208
+ kernel keys stay constant across the three tested context lengths.
209
+
210
+ The last repeat uses synthetic [1,8,N,256] caches with fp16 scales and groups
211
+ of 32. For eager V3 decode, paired median operation time falls 46.72% at N128,
212
+ 77.43% at N2048 and 81.37% at N8192. V5 falls 54.66%, 82.12% and 84.93%. Every
213
+ V3/V5 pair improves. At N8192, peak temporary allocation falls about 250 MiB
214
+ for V3 and 282 MiB for V5. The repeat checks outputs before timing and after
215
+ the last rep of each arm; the earlier screen read the output on the CPU
216
+ between every rep, which changed the timing regime. Raw screens are retained.
217
+ These are operation diagnostics on a loaded machine, not RSS or full-model
218
+ speed claims. This Qwen artifact has four KV heads, so the next gate runs the
219
+ actual model. The experimental full-model branch keeps V8 on its native path
220
+ while the larger low-bit gain is assessed.
221
+
222
+ Evidence in reports/qwen38-rd: tq-value-grid.json,
223
+ tq-value-repeat{,-review}.json, tq-value-f16-repeat{,-review}.json and
224
+ tq-value-production-repeat{,-review}.json. Full-model logits, live cache and
225
+ recurrent state, seeded generation, actual HTTP timing, fallback/lifetime and
226
+ quiet acceptance are required before production integration or a default.
227
+
228
+ The first Qwen k8v3 full-model pair now preserves all 20 forward/state and
229
+ continuation comparisons plus four greedy/seeded-sampled generations at
230
+ 128/2048 input tokens. Both arms make 4,880 cache-value calls. The candidate
231
+ uses one D256/V3/group32/fp16-scale specialization across every context length;
232
+ final active allocation is identical at 1,016,898 bytes. Timing is mixed:
233
+ single-token forwards improve, while three of four complete generations lose.
234
+ During the candidate process, one-minute load rises from 2.80 to 7.57 and
235
+ system swap grows about 323 MiB, so this pair cannot isolate the cause of the
236
+ slowdown. It is retained in full, and no production/default decision follows.
237
+ Evidence: tq-value-full{,-review}.json. Actual Bun HTTP comparisons follow.
238
+
239
+ An additional 270 finite-scale-boundary cases preserve 540 comparisons,
240
+ including zero, signed zero, fp16 subnormals and large scales at every head
241
+ dimension, with the actual four-KV-head shape included. All allocations return
242
+ to the primed 5,104-byte global cache. Evidence: tq-value-edge.json. The first
243
+ actual HTTP pair preserves all five responses, call maps and final allocation.
244
+ Measured complete time falls 0.77% for the 58-token code prompt and 1.38% for
245
+ the 1,806-token inventory prompt. The latter improves decode throughput 7.57%
246
+ but worsens TTFT 3.80%; it is a screening pair, not acceptance. Six alternating
247
+ fresh-server pairs now preserve all 30 paired responses, call maps, source
248
+ hashes and process exits. Each server ends with the same 11,936,577,410 active
249
+ bytes, which includes the retained model. Paired median complete time falls
250
+ 0.82% on the short prompt, all six pairs, and 2.87% on the long prompt, four of
251
+ six pairs. Long-prompt decode throughput rises 7.33%, five of six pairs; TTFT
252
+ worsens 0.75% in the paired median and is split three wins/three losses. Per-
253
+ request sampled RSS differences are small and inconsistent, so this is not a
254
+ physical-memory claim. Retain both losing long-prompt pairs. Evidence:
255
+ tq-value-http-repeat{,-review}.json. Test decode-only eligibility to separate
256
+ prefill scheduling from decode, then broader context/quant/cache and quiet
257
+ acceptance. No production/default change yet.
258
+
259
+ A separate decode-only V arm also preserves 20 forward/state cases and four
260
+ generations. Exact call maps show 4,480 fused M1 reads and 400 native
261
+ multi-token reads; active allocation remains 1,016,898 bytes. Evidence:
262
+ tq-value-decode-full{,-review}.json. A first timing pair improves complete
263
+ generation, but it is not the policy decision.
264
+
265
+ The next prototype decodes packed K and V in one launch, retaining K's f32
266
+ addition before multiplication and V's f32 centroid multiplication. It passes
267
+ 720 initial pair comparisons. An expanded screen checks all 20 served K/V bit
268
+ combinations with distinct key/value scales, including finite boundary values,
269
+ and preserves all 2,400 comparisons. Equal scale values in the initial
270
+ synthetic fixtures could not detect a swapped scale input; the expanded
271
+ fixtures close that gap. Actual-model scales were already independent.
272
+
273
+ Six fresh-process operation repeats preserve another 432 comparisons. With
274
+ actual four-head D256 caches, fp16 scales and group32, joint k8v3 decoding is
275
+ 9.60%, 22.80% and 43.19% faster than V-only fusion at N128/2048/8192 in the
276
+ paired median, all six pairs at each context. These include the native inverse
277
+ rotation. Peak temporary allocation falls another 0.53/8.5/34 MiB. The full
278
+ Qwen pair preserves 20 forward/state comparisons and four generations, all
279
+ 4,880 cache calls, one generated specialization and identical final active
280
+ allocation. Source and raw evidence: tq-kv-screen.json,
281
+ tq-kv-independent.json, tq-kv-repeat{,-review}.json and
282
+ tq-kv-full{,-review}.json.
283
+
284
+ The actual Bun HTTP follow-up completes all six arm orders across 18 fresh
285
+ servers. Its 90 requests preserve all 60 candidate/control response comparisons,
286
+ call and eligibility maps, process exits and source hashes. Every server retains
287
+ the same 11,936,577,410 active bytes. Each candidate uses one shader specialization
288
+ across both prompt lengths. These runs use the same R6 weights, k8v3 cache,
289
+ serial execution, greedy seed42 and no prompt reuse or speculative draft.
290
+
291
+ | Candidate versus original | 58-token prompt: complete time | 1,806-token prompt: complete time | Long-prompt decode throughput | Long-prompt TTFT |
292
+ |---|---|---|---|---|
293
+ | V fusion only on M1 appends | −0.81%, 6/6 wins | −3.42%, 5/6 wins | +7.53%, 5/6 wins | −0.14%, 5/6 wins |
294
+ | Joint K/V at all append shapes | −0.76%, 5/6 wins | −4.57%, 6/6 wins | +10.35%, 6/6 wins | −0.17%, 5/6 wins |
295
+
296
+ Values are medians of paired changes, not ratios of unpaired medians. Joint
297
+ decoding also improves long-prompt complete time over decode-only V by 1.20%,
298
+ all six pairs; its short-prompt median is 0.08% slower. The first joint short
299
+ request loses 3.70%, and the first decode-only long request loses 2.30%; both
300
+ remain in the report. TTFT is effectively flat. Observed RSS deltas remain
301
+ small and do not establish physical-memory savings. Evidence:
302
+ tq-kv-policy-http{,-review}.json. Extend the exact gates to 8,192 context tokens,
303
+ RTN4 and deferred-rotation consumers, then check integrated lifetime/fallback
304
+ behavior. These are M4 Pro diagnostics with existing system swap; quiet
305
+ acceptance and a production/default decision remain open.
306
+
307
+ The 8,192-token R6 gate also passes: six full forward/state/continuation cases
308
+ at M1/4/128 and two 64-token greedy/sampled generations are exact. Both arms
309
+ make 2,960 cache calls and finish at 1,016,898 active bytes; the candidate still
310
+ uses one shader key. In this single pair, complete generation time falls 9.11%
311
+ and 7.43%, and native peak allocation falls 126.97 MiB. The measured M1 forward
312
+ falls from 200.84 to 118.87 ms. These are screening timings, not repeated HTTP
313
+ acceptance or physical-memory savings. Evidence: tq-kv-long-full{,-review}.json.
314
+
315
+ A further prototype folds inverse Hadamard, normalization, signs and bf16 cast
316
+ into the joint decoder. It copies the exact radix-16 butterfly and indexing from
317
+ MLX 0.31.2's MIT-licensed Metal Hadamard implementation. All 480 shape/bit/dtype/
318
+ finite-boundary cases preserve both candidate comparisons. Six fresh processes
319
+ then preserve all 36 performance cells, but the literal one-row threadgroup
320
+ layout loses to joint decode plus native rotation: k8v3 paired median operation
321
+ time rises 12.76%/20.11%/24.67% at N128/2048/8192. Lower temporary allocation
322
+ alone does not justify this version. The follow-up tests independent rows in
323
+ one threadgroup, preserving each row's arithmetic and participation in barriers
324
+ at partial tails. Evidence: tq-kv-rotation-screen.json and
325
+ tq-kv-rotation-repeat{,-review}.json. No full-model run is justified for the
326
+ losing layout.
327
+
328
+ Grouping independent rows into one threadgroup preserves the same 960 boundary
329
+ comparisons, including partially occupied tail groups. Six fresh-process repeats
330
+ preserve all 36 timing cells. At N8192, this version improves over joint decode
331
+ plus native rotation by 22.28% for k8v3 and 23.20% for k4v3, all six pairs, while
332
+ halving the operation's peak temporary allocation from 64 to 32 MiB. It remains
333
+ slower at N128 and N2048; for k8v3 the paired medians are +4.08% and +20.43%.
334
+ Only a long-context candidate is justified for full-model/request testing.
335
+ Evidence: tq-kv-rotation-rows-screen.json and
336
+ tq-kv-rotation-rows-repeat{,-review}.json. The baseline layout does not advance.
337
+
338
+ Joint decoding also passes the RTN4 full-model counterpart: 18 forward/state/
339
+ continuation cases at context128/2048/8192 and four generations are exact, with
340
+ 5,536 matching cache calls, one specialization and 1,000,514 final active bytes
341
+ per arm. Its first paired generation timings improve, but repeated RTN4 HTTP
342
+ acceptance remains. A nonfinite follow-up preserves 960 additional comparisons
343
+ across every served bit pair, all head dimensions and scale dtypes, including
344
+ NaNs, infinities and arbitrary f32 zero-point fractions. This covers the
345
+ reference codec's documented zero-scale/infinite-fp16-zero behavior.
346
+
347
+ The MiniCPM5-1B counterpart exercises deferred V rotation: all 20 forward/state
348
+ cases, four generations and 7,320 cache calls match. Both arms finish at 524,860
349
+ active bytes; the candidate uses one D128 specialization. These are correctness
350
+ and first-pair timing gates, not repeated cross-model serving claims. Evidence:
351
+ tq-kv-rtn4-full{,-review}.json, tq-kv-nonfinite.json and
352
+ tq-kv-minicpm-full{,-review}.json. Gemma D512 and integrated operation/fallback/
353
+ lifetime checks follow.
354
+
355
+ Gemma-4-e4b's D512 deferred consumer also passes: 20 forward/state cases, four
356
+ generations and 1,220 matching cache reads, one specialization and 1,057,864
357
+ final active bytes per arm. Evidence: tq-kv-gemma-e4b-full{,-review}.json.
358
+
359
+ The joint decoder is now integrated as an experimental opt-in. The environment
360
+ setting and supported inputs are documented in server-config.md. A shared
361
+ `src/mlx/turboquant-kv-decode.ts` owns only the packed Metal operation; the existing
362
+ codec owns inverse rotation and the cache captures operation selection once.
363
+ Request/execution interfaces, quantization and persistence remain unchanged.
364
+ The original codec remains the fallback and correctness reference. Five new
365
+ operation tests pass 1,102 assertions across served bit combinations, head
366
+ dimensions, independent group sizes, nonfinite values, strided storage, fallback
367
+ and shapeless-trace behavior, borrowing and exact post-disposal allocation.
368
+ All three typechecks pass. With fusion enabled, the existing 26 codec/cache
369
+ tests also pass, including growth, trim and mixed-cache persistence. Their
370
+ snapshot helpers now release temporary casts and owned state views instead of
371
+ letting test-only allocations obscure the lifecycle checks. The integrated
372
+ model/HTTP evidence follows; prototype timings above are not automatically
373
+ attributed to the production dispatch.
374
+
375
+ The integrated R6 gate now preserves all 20 forward/state/continuation cases,
376
+ four generations, 4,880 cache reads and 1,016,898 final active bytes per arm.
377
+ Passive observation confirms that every enabled read reaches the production
378
+ kernel, with one specialization. The full model-free tier passes 1,873 tests
379
+ with 10 skips; typechecks and pure hygiene also pass. Evidence:
380
+ tq-kv-integrated-full{,-review}.json and tq-kv-integrated-code-checks.json.
381
+ The integrated RTN4 counterpart also preserves all 20 forward/state cases,
382
+ four generations, 4,880 reads and 1,000,514 final active bytes. Its first-pair
383
+ timings are mixed: three complete-generation cells regress 0.44–1.88%, while
384
+ the 2,048-token sampled cell improves 4.33%. Exactness alone does not establish
385
+ a speed gain. Evidence: tq-kv-integrated-rtn4-full{,-review}.json.
386
+
387
+ Six integrated R6 HTTP pairs preserve all 30 response comparisons, call maps,
388
+ one candidate specialization and equal post-exit active allocation. Paired
389
+ medians for the 58-token prompt are −0.74% complete time and +0.75% decode,
390
+ both six wins. At 1,806 prompt tokens they are −3.61% complete time, four wins,
391
+ and +10.33% decode, five wins. Long-prompt TTFT is +1.81%, three wins. Both
392
+ complete-time losses, +0.55% and +10.95%, remain in the result. This repeat is
393
+ noisier than the prototype, with no established TTFT improvement.
394
+
395
+ The same run stops after its first RTN4 pair on a 10,944-byte difference in
396
+ post-exit active allocation, with less retained in the candidate. All five
397
+ responses and the cache call maps match. The original run remains marked
398
+ incomplete; the later allocation investigation and revised assessment appear
399
+ below. Every child exits
400
+ cleanly and all 547 recorded source hashes remain unchanged. Evidence:
401
+ tq-kv-integrated-http{,-review}.json. A separate instrumented allocation/GC
402
+ diagnostic follows; its timings cannot be compared with ordinary serving.
403
+
404
+ The HTTP extension keeps the existing RSS collector and adds the calibrated
405
+ native process reader. R6's whole-server sampled physical-footprint maximum
406
+ falls in every pair, with a median difference of 2.04 GiB; this includes
407
+ startup, warmups and both prompt shapes. Measured-request RSS is nearly flat.
408
+ These observations do not identify per-request physical peaks or prove
409
+ savings across models. Longer-context, pressure, repeated deferred-model
410
+ serving and quiet acceptance remain before any default decision.
411
+
412
+ A fresh six-pair RTN4 repeat completes all 60 requests with all 30 response
413
+ comparisons and cache call maps exact. Both prompt shapes improve complete
414
+ time and decode throughput in every pair. At 58 prompt tokens the paired
415
+ medians are −3.07% complete time and +3.35% decode; at 1,806 they are −5.95%
416
+ and +15.47%. TTFT medians are about −0.63% in both cells. These use k8v3 KV,
417
+ serial execution and 128 generated tokens, without a speculative draft.
418
+
419
+ The repeat still fails its exact process-exit allocation gate. The candidate
420
+ retains 15,135,029,250 bytes in all six servers; four controls match, while
421
+ the last two retain 18,624 and 6,976 fewer bytes. Together with the earlier
422
+ smaller candidate observation, this varies in both arms. An array-tracked
423
+ diagnostic preserves 1,851 live wrappers and identical allocation before and
424
+ after explicit GC, but does not reproduce the variation. It establishes no
425
+ GC fix or kernel leak. The lighter observation below resolves the acceptance
426
+ metric, while preserving these failed reports. Evidence:
427
+ tq-kv-integrated-rtn4-http{,-review}.json and tq-kv-memory{,-review}.json.
428
+
429
+ The lighter array observer reproduces a 2,560-byte capacity difference while
430
+ both servers retain exactly 1,851 wrappers with matching dtype/shape counts
431
+ and logical bytes, before and after GC. This exposes a flaw in the exact-byte
432
+ gate: MLX's Metal allocator counts `MTLBuffer.length`, and its cache can reuse
433
+ a larger buffer for a smaller request. A controlled native reproduction gives
434
+ identical 4,096-byte arrays with either 4,096 or 6,144 active bytes. Clearing
435
+ unused buffers preserves that difference; disposing the live array releases
436
+ it completely. See the pinned allocator.cpp and buffer_cache.h plus
437
+ allocator-capacity-repro.json. The server's individual backing capacities were
438
+ not enumerated, so reuse explains the metric's limitation without attributing
439
+ every byte. Ownership acceptance uses the matching live-array inventory and
440
+ the exact native/cache disposal gates; active capacity remains a reported
441
+ measurement. Original failed reports remain unchanged. The revised assessment
442
+ is tq-kv-integrated-rtn4-http-acceptance.json.
443
+
444
+ The integrated 8K R6 gate also passes all six forward/state/continuation cases
445
+ and both 64-token greedy/sampled generations, with 2,960 matching cache reads,
446
+ one specialization and equal final allocation. In the first three-arm block,
447
+ joint decoding improves complete generation by 8.63%/7.59% versus the original.
448
+ Adding row-grouped inverse rotation only at N≥8192 preserves all results and
449
+ improves a further 1.25%/0.90% over integrated joint decoding. Its 2,256 rotation
450
+ calls exactly match eligibility, with 704 ordinary joint calls. The operation's
451
+ lower temporary allocation does not reduce the whole generation's peak further.
452
+ These are screening timings; the repeated long-context HTTP result follows. Evidence:
453
+ tq-kv-rotation-long{,-review}.json.
454
+
455
+ The six-block long-context HTTP repeat completes all 18 fresh servers and 54
456
+ requests in all six arm orders. At 8,846 prompt tokens and 128 generated tokens,
457
+ joint decoding versus the original codec improves paired-median decode by
458
+ 83.87% and complete time by 12.77%. Adding row-grouped inverse rotation improves
459
+ a further 5.39% decode and 1.05% complete time over the integrated joint decoder.
460
+ Every paired complete-time and decode comparison improves. The incremental
461
+ TTFT median is −0.24%, with four of six wins; sampled request RSS is slightly
462
+ higher, so this establishes no RSS saving. All response text, usage, finish
463
+ reasons, call/eligibility maps and retained capacity match; all children exit
464
+ cleanly and all 547 recorded source hashes remain fixed. These are R6/k8v3,
465
+ serial, greedy seed42 diagnostics on the M4 Pro, without a draft. They do not
466
+ apply to bf16-cache rows. Evidence: tq-kv-rotation-long-http{,-review}.json.
467
+
468
+ That result justifies integrating the inverse operation into the same opt-in.
469
+ The numeric module `src/mlx/turboquant-kv-inverse.ts` owns a single D256 shader,
470
+ with 16 rows per threadgroup, unrolled radix16 butterflies and exact 1/16
471
+ normalization. The codec selects it only for the measured eager k8v3, B1/H4,
472
+ fp16/group32 regime at N≥8192; other calls retain joint decoding or the original
473
+ codec. Packed arrays and codec-table strides are explicit. N changes the grid,
474
+ never the shader specialization, and padded rows participate in every barrier.
475
+ The updated operation suite passes nine tests and 1,226 assertions, including
476
+ threshold/partial-row, nonfinite, strided-table, fallback and ownership checks.
477
+ Actual integrated model and serving acceptance against the saved prior joint
478
+ codec remain separate from the prototype's timing evidence. The R6 native
479
+ integration gate passes twelve forward/state/continuation cases and four
480
+ 64-token greedy/sampled generations at contexts 2048/8192. Logits, tokens,
481
+ logprobs, complete cache states, prefix snapshots and usage are identical.
482
+ The candidate's 2,256 inverse calls match eligibility exactly, with 3,088 joint
483
+ calls; both arms release to the same retained allocation. Source hashes remain
484
+ fixed and both children exit cleanly. A brief concurrent CPU build prevents
485
+ accepting this gate's timing. Evidence:
486
+ tq-kv-inverse-integrated-r6-full{,-review}.json.
487
+
488
+ The MLX 0.32.2 controls now pass the same integrated model gate on both R6 and
489
+ RTN4. Every full logit/state/continuation case and greedy/sampled generation is
490
+ exact, with matching call maps, equal final active allocation and clean exits.
491
+ The version-specific records are mlx-upgrade-0.32.2/{r6,rtn4}-kv-inverse-full.json.
492
+ The integrated serving repeat now completes six usable timing pairs per quant
493
+ on the M4 Pro 24 GB, with the same 8,846-token prompt, k8v3, greedy seed42 and
494
+ 128 generated tokens. Inverse rotation improves the paired-median HTTP decode
495
+ rate by 4.69% on R6 and 5.98% on RTN4; every decode pair improves. Complete
496
+ request time improves 0.79% on R6, with all six pairs improving. RTN4's complete
497
+ time is inconclusive: its median improves 0.38%, but only three pairs improve
498
+ and prefill variation exceeds the incremental saving. Neither result establishes
499
+ a general RSS reduction. All 78 responses across the original and replacement
500
+ runs match text, usage and finish reason. Call/eligibility maps match, all 26
501
+ servers exit cleanly and source/library hashes stay fixed. One original R6
502
+ timing pair was excluded after an MLX unit test overlapped its warmup; the
503
+ predeclared replacement supplies that pair. These are loaded-machine
504
+ diagnostics, not canonical acceptance. Evidence:
505
+ mlx-upgrade-0.32.2/inverse-kv-http{,-replacement,-review,-isolation}.json.
506
+ Combined pressure and quiet acceptance remain.
507
+
508
+ The consolidated 0.32.2 runtime also passes six paired actual HTTP blocks per
509
+ MiniCPM5-1B and Gemma4-e4b with the joint decoder off/on. Their deferred value
510
+ paths use D128 and D512 respectively. All 120 responses, including warmups,
511
+ match in text, usage and finish reason; every call map matches and all 24
512
+ servers exit cleanly. Source and native-library hashes remain fixed. Both
513
+ models improve complete request time in every measured pair. MiniCPM improves
514
+ substantially at both tested prompt lengths; Gemma's benefit is larger on the
515
+ longer prompt. Sampled RSS establishes no reduction. The server-exit observer
516
+ still sees resident model arrays, so its active capacity is not a disposal-to-
517
+ zero test. Native operation and full-model ownership gates remain separate.
518
+ These are M4 Pro 24 GB diagnostics with k8v3, greedy seed 42 and 128 generated
519
+ tokens. TurboQuant KV requires serial placement in the existing execution
520
+ planner. Evidence: mlx-upgrade-0.32.2/deferred-kv-http{,-review}.json. This closes
521
+ the deferred-model serving comparison; broader pressure and quiet gates remain.
522
+
523
+ Direct packed-key attention is closed as an additional speed candidate. A
524
+ research port of MLX 0.31.2's two-pass vector attention replaces each K load
525
+ with the existing signed-k8 zero/scale arithmetic and bf16 rounding. It keeps
526
+ the native block selection, score reduction, online softmax, bf16 partial
527
+ output and final reduction order. The dense source port and packed-key variant
528
+ both match all 72 operation cases at D256, one query, GQA 6 and N1024 through
529
+ 16384, including group32/64, fp16/bf16/f32 metadata and strided arrays. Every
530
+ owned allocation is released. Scope is M4 Pro, no mask or sinks, with eager
531
+ V already decoded; this does not cover arbitrary SDPA inputs.
532
+
533
+ Six fresh-process repeats then measure the complete eager k8v3 decode plus
534
+ attention using actual encoder output. All 72 cells preserve their inputs
535
+ and both candidate outputs, with 3,888 timed operations and matching source
536
+ hashes. Fixture allocations return to the same warmed codec-table baseline
537
+ after every cell. Direct key loads with the earlier fused V decoder improve
538
+ over the original operations, but lose against the integrated joint K/V
539
+ decoder plus native attention. Paired median regressions are about 14% at
540
+ N1024/1025, 19% at N2048, 6% at N8192/8193 and 8% at N16384, for contiguous
541
+ and strided caches. All six pairs lose except two noisy N2048 contiguous
542
+ cells. Peak temporary allocation falls by the removed bf16 K window, about
543
+ 16 MiB at N8192 and 32 MiB at N16384, without a measured whole-model memory
544
+ benefit. The initial key-only improvement therefore does not justify a new
545
+ model/cache dispatch or production kernel. Evidence: tq-key-sdpa-screen.json
546
+ and tq-key-sdpa-complete-repeat{,-review}.json. The pinned source is MLX's
547
+ MIT-licensed sdpa_vector.h and scaled_dot_product_attention.cpp under
548
+ upstream/mlx-0.31.2; the closed research helpers are removed.
549
+
550
+ ## Gates — all passed 2026-07-06 (M1 Max 32 GB)
551
+
552
+ 1. **Quantizer parity (bit-exact, the hard gate):** encode indices, scales,
553
+ zeros, packed bytes, and dequant outputs match the vendored reference on
554
+ golden vectors (head_dim 64/128; configs k8v3/k4v3/k4v2/k8v4/k2v5/k5v8 —
555
+ every kBits/vBits value the CLI accepts).
556
+ 2. **Math sanity (model-free):** FWHT involution/orthogonality; 3-bit
557
+ roundtrip MSE on unit-normal data in the paper's D_mse≈0.03 band.
558
+ 3. **Quality-vs-bpw curve (Phase 13 exit criterion)** — MiniCPM5-1B,
559
+ teacher-forced serving-decode KL vs bf16, 8 prompts × 128 tokens, 32
560
+ decode steps; affine baselines via the same harness:
561
+
562
+ | scheme | effective KV bits | mean KL vs bf16 |
563
+ |---|---|---|
564
+ | bf16 | 16.00 | 0 (control) |
565
+ | uniform kv8 (affine g64) | 8.50 | 0.00246 |
566
+ | turbo k8v8 | 8.75 | 0.00214 |
567
+ | turbo k8v4 | 6.75 | 0.00936 |
568
+ | **turbo k8v3 (default)** | **6.25** | **0.0325** |
569
+ | turbo k8v2 | 5.75 | 0.175 |
570
+ | uniform kv4 (affine g64) | 4.50 | 0.0516 |
571
+ | turbo k4v3 | 4.25 | 0.0622 |
572
+ | turbo k4v2 | 3.75 | 0.205 |
573
+
574
+ Shape reproduces the paper's law: value bits dominate, ~4^-b decay, cliff
575
+ at v2. Turbo adds operating points between affine kv8/kv4; the k8v3
576
+ default (2.56× KV compression) beats uniform kv4's KL with room to spare.
577
+ Live smokes: cpm5 + e4b coherent under turbo:k8v3 (e4b emits the
578
+ rotating-layers-stay-bf16 warning). The paper's LongBench numbers were NOT
579
+ rerun — the reproduction is shape-level plus the codec-level D_mse anchor.
580
+ 4. Whole-repo `tsc --noEmit` = 0; fast suite green; turbo requests route
581
+ serial with a clear reason.
582
+
583
+ ## KV-leg limits and non-goals (recorded so they don't creep)
584
+
585
+ - Full-attention layers only; rotating/sliding-window layers stay bf16 (warn
586
+ once, never throw). Head dims outside {64,128,256,512} refused at server
587
+ start.
588
+ - Solo-only: no batched TurboQuant (novel Cache class); paged-KV combos are
589
+ refused explicitly.
590
+ - No fused quantized-SDPA Metal kernel (remaining fetch cost is
591
+ unpack+gather); no QJL residual stage; no entropy coding (the paper
592
+ declined it too).
593
+ - Speculative lane is bf16-KV-only: turbo + `--draft-model` keeps the KV
594
+ scheme and decodes serially without speculation.
595
+ - No speed claims: v1 dequant-on-fetch is expected slower per step at long
596
+ context; this ships as a memory/context feature like uniform KV. Admission
597
+ still bills turbo as bf16 (server-config.md says so).
598
+
599
+ ---
600
+
601
+ # Part B — Weights leg (rotation-folded quantization)
602
+
603
+ ## What it is
604
+
605
+ Fold an orthogonal rotation R offline into producer/consumer weight pairs so
606
+ every weight matrix is quantized in a rotated basis where outlier channels
607
+ are smeared into near-Gaussian marginals — then quantize with mlx's EXISTING
608
+ formats (affine / mxfp4 / nvfp4). Zero runtime cost, zero new kernels,
609
+ output loadable by stock mlx-lm. The QuaRot/SpinQuant family; composes with
610
+ (does not replace) per-layer allocation (OptiQ-style) and calibration.
611
+
612
+ Target: the best-possible Qwen3.8-27B (+ MTP companion) on our hardware,
613
+ beating OptiQ-4bit / plain 4bit at equal effective bpw (gate: ppl + frozen
614
+ 6-task eval, eval DB rows, per-bpw-band comparisons).
615
+
616
+ Why weights differ from KV (the 2026-07-06 framing that opened the leg):
617
+ - Weights win on one axis: the rotation FOLDS into adjacent matrices offline
618
+ (R into one layer, Rᵀ into the next) — no online transform at all.
619
+ - Weights lose on the other: they are static, so calibration-aware methods
620
+ (GPTQ/AWQ/imatrix, optiq sensitivity maps) are admissible and set a higher
621
+ baseline than any calibration-free scheme — the win to chase is rotation
622
+ COMPOSED WITH calibration/allocation, not instead of it.
623
+ - A Lloyd-Max weight FORMAT needs custom quantized-matmul kernels — the 26B
624
+ gather-qmv shelving (dispatch fixed-cost ate the prize) is the precedent.
625
+ Folding into mlx's existing affine format needs none.
626
+ - Activation quant (W4A4) is mispriced on this hardware (no int4 tensor
627
+ cores, decode is weight-bandwidth-bound; docs/design/orpo-training.md).
628
+
629
+ ## Oracle chain
630
+
631
+ Folding mechanics come from the reference repos (read 2026-08-17, full-file
632
+ agent reads):
633
+
634
+ - **QuaRot** (arXiv:2404.00456, github spcl/QuaRot):
635
+ `fake_quant/rotation_utils.py` (fuse_ln_linear, fuse_layer_norms,
636
+ rotate_model), `fake_quant/hadamard_utils.py` (get_hadK, matmul_hadU,
637
+ random_hadamard_matrix), `fake_quant/model_utils.py`.
638
+ - **SpinQuant** (arXiv:2405.16406, github facebookresearch/SpinQuant):
639
+ `utils/fuse_norm_utils.py` (a copy of QuaRot's γ-fold),
640
+ `eval_utils/rotation_utils.py` (R1/R2 folds), `optimize_rotation.py`
641
+ (Cayley-SGD learned R — deferred to W5-learned).
642
+ - **GPTQ**: `mlx_lm/quant/gptq.py` in the pinned venv (forked minimally in
643
+ `scripts/turboquant/tq-gptq.py`; see the two upstream defects below).
644
+ - **Sensitivity**: `mlx_lm.quant.dynamic_quant` (gradient KL) + OptiQ's
645
+ published `optiq/sensitivity.json` for this exact 27B (`sensitivities[b]`
646
+ = measured KL GAIN at b bits — the first read had the sign flipped).
647
+
648
+ Weight layout: torch and MLX `nn.Linear` are both `[out, in]`, `y = x @ W.T`
649
+ — their fold table transfers verbatim.
650
+
651
+ ## The recipe (weight-only, fully offline)
652
+
653
+ Order matters: γ-fold FIRST (R1 commutes only with gain-free RMSNorm), then
654
+ R1, then R2, then quantize.
655
+
656
+ **0. Untie embeddings** if `tie_word_embeddings` (clone into a separate
657
+ lm_head, set the flag false). Required: step 1 transforms lm_head but not
658
+ embed_tokens (SpinQuant `ptq.py` does exactly this).
659
+
660
+ **1. γ-fold (norm fusion)** — for each RMSNorm feeding linears,
661
+ `W' = W · diag(γ)` on the INPUT dim, then set that norm's γ to ones (keep the
662
+ module + its eps — do NOT copy QuaRot's replacement RMSN, which hardcodes
663
+ eps=1e-5; our subjects use 1e-6):
664
+ - input_layernorm → q/k/v_proj
665
+ - post_attention_layernorm → gate/up_proj
666
+ - final norm → lm_head
667
+
668
+ **2. R1 residual fold** — one hidden-size orthogonal R1:
669
+
670
+ | matrix | transform | reason |
671
+ |---|---|---|
672
+ | embed_tokens | `W @ R1` | writes residual (rows are embeddings) |
673
+ | q/k/v_proj | `W @ R1` | reads residual (input dim) |
674
+ | o_proj (+bias) | `R1ᵀ @ W` | writes residual (output dim) |
675
+ | gate/up_proj | `W @ R1` | reads residual |
676
+ | down_proj (+bias) | `R1ᵀ @ W` | writes residual |
677
+ | lm_head | `W @ R1` | reads residual |
678
+
679
+ Q/K outputs untouched → RoPE unaffected. Input-side folds never touch biases
680
+ (bias lives on the output).
681
+
682
+ **3. R2 per-head v/o fold — SpinQuant's version, NOT QuaRot's.** Per layer,
683
+ one head_dim-sized orthogonal R2 applied block-diagonally: v_proj output
684
+ blocks get R2ᵀ, o_proj input blocks get R2; softmax weights are R2-invariant
685
+ so it cancels exactly, incl. under GQA (same R2 for all heads; repeat_kv
686
+ replicates whole heads). QuaRot's variant folds a FULL Hadamard into o_proj
687
+ and needs an ONLINE cross-head Hadamard (`online_partial_had`) to cancel —
688
+ folding its v-side alone breaks the network. Skip QuaRot's pairing entirely.
689
+
690
+ **4. Quantize** the folded weights with the stock convert path (affine g64 /
691
+ mxfp4 / nvfp4 arms; only affine has been measured — see W3).
692
+
693
+ ### Deviations from the references (decided, with reasons)
694
+
695
+ These are the strings persisted per artifact in `optiq_metadata.json`
696
+ `weight_transforms[].deviations` (`src/quantize/rotate.ts`).
697
+
698
+ - **No embedding mean-centering.** Their `fuse_layer_norms` mean-centers
699
+ embedding rows (SliceGPT LayerNorm→RMSNorm trick). For RMSNorm models this
700
+ is NOT an exact invariance — they do it anyway; we don't, because W0's exit
701
+ criterion is logit parity of the folded bf16 model.
702
+ - **Delete the hidden R4 half-fold.** BOTH repos' `rotate_mlp_output`
703
+ unconditionally applies a full Hadamard to down_proj's input dim right
704
+ after the R1ᵀ fold — the offline HALF of R4, which requires the runtime
705
+ `online_full_had` on down_proj's activation (QuaRot rotation_utils.py:173 /
706
+ SpinQuant eval_utils/rotation_utils.py 98-100). Weight-only must skip both
707
+ halves. The `R1ᵀ @ W_down` part stays.
708
+ - **No R3.** Post-RoPE Q/K rotation exists only for KV-cache quant (their
709
+ code registers it only when k_bits < 16). Our KV story is Part A — online
710
+ rotation remains KV-only.
711
+ - **γ kept in the module as ones (eps preserved).**
712
+ - **Fold precision: fp32, not fp64.** References fold in fp64 (fp16-era
713
+ caution). Our weights are bf16 (8-bit mantissa); fp32 accumulation at
714
+ n≲6k leaves ample headroom. Escape hatch if a parity gate fails: fold
715
+ goldens in the oracle venv (mlx CPU float64).
716
+
717
+ ### R generation
718
+
719
+ - Random-Hadamard R1 = `diag(±1 signs) · H_n / √n` (QuIP#-style). Signs are
720
+ a seed-pinned splitmix32 stream (`signVector(seed, n, lane)`; R1 = lane 0,
721
+ layer i's R2 = lane i+1) so the same (seed, n, lane) yields the same signs
722
+ on every machine. R is never needed at runtime; provenance (transform id,
723
+ family, seed, deviations) is written to `optiq_metadata.json`.
724
+ - Supported n: the Llama adapter asserts power-of-two hidden and head dims;
725
+ the Qwen adapter accepts `n = m·2^k, m ∈ {1,12,20,28}` — what mlx's
726
+ `hadamard_transform` takes natively (verified live at 5120 = 20·256). The
727
+ references' Kronecker table for other K values is NOT implemented; callers
728
+ get a clear throw. (QR-of-Gaussian "random" mode from the references is
729
+ likewise not implemented.)
730
+ - R2 default: plain signed Hadamard of head_dim (must be pow-2).
731
+ - Learned R (SpinQuant Cayley-SGD R1 + per-layer R2) stays deferred until a
732
+ random-R recipe wins on task evals; it folds identically afterward.
733
+
734
+ ## Implementation seam (verified 2026-08-23)
735
+
736
+ `src/quantize/weight-transform.ts` separates pure tensor-name planning from
737
+ lazy tensor application:
738
+ - `WeightTransform { id, plan(names, config), createContext(weights, plan) }`;
739
+ `plan()` is name/config analysis only (no arrays, no device work);
740
+ `WeightTransformContext.apply(outputName, source)` returns a lazy owned
741
+ array and never disposes `source`.
742
+ - `WeightTransformPlan.sourceByOutput` makes untie/clone explicit (a
743
+ synthesized `lm_head.weight` maps to `model.embed_tokens.weight`).
744
+ - Adapters: `llamaWeightTransform` (`rotation.llama`), `qwen35WeightTransform`
745
+ (`rotation.qwen3_5`), `qwenMtpWeightTransform` (`rotation.qwen3_5_mtp`), and
746
+ `automaticRotationWeightTransform` (`rotation.auto`) which selects by
747
+ schema: MTP if `fc.weight` + `pre_fc_norm_hidden.weight` are present, else
748
+ by `model_type` ∈ {qwen3_5, llama}, else throws.
749
+ - Fold math lives in `src/quantize/rotate.ts` (`foldLlamaWeights`,
750
+ `planQwen35Fold`, `planQwenMtpFold`, `QwenFoldContext`; f32 chains → bf16;
751
+ every returned array is a lazy graph over the source mmap so the writer
752
+ materializes one tensor at a time).
753
+ - `src/quantize/quantizer.ts` `quantizeModelDir` applies the plan in its
754
+ existing module walk; refuses a transform when the source is already
755
+ quantized ("requires full-precision source weights"); writes
756
+ `weight_transforms` into both the uniform and the mixed-precision
757
+ `optiq_metadata.json`. Mixed-precision calibration receives a
758
+ `ProbeSource`; the default builds an 8-bit probe through the lower-level
759
+ writer instead of recursively re-entering the top-level quantizer.
760
+ - CLI: `mlx-bun convert --rotate-weights [--rotation-seed N]` (default seed
761
+ 42; cli.md documents it). An MTP companion must be folded with the same
762
+ seed as its trunk.
763
+ - Tests: `tests/weight-transform-plan.test.ts`,
764
+ `tests/weight-transform-numerics.test.ts`.
765
+
766
+ Src-vs-doc note: the production Llama adapter currently REQUIRES tied
767
+ embeddings (`planLlamaWeightTransform` throws if `lm_head.weight` exists) —
768
+ the "untie if tied" recipe step is exercised only in that direction; an
769
+ untied Llama source is not yet accepted. The Qwen adapter handles both.
770
+
771
+ ## W1 — Qwen3.8 (qwen3_5) corridor map (2026-08-18, from `src/model/qwen3_5.ts` + `src/spec/qwen-mtp-source.ts` + `src/vision/qwen3vl-tower.ts` + tensor dumps)
772
+
773
+ **R2 is architecturally OFF for this family:** full attention computes
774
+ `o_proj(attention_output · σ(gate))` — q_proj emits 2×head_dim per head and
775
+ the gate multiplies the attention output ELEMENTWISE in head space. An
776
+ elementwise gate does not commute with a per-head rotation, so only γ+R1
777
+ apply. hidden 5120 = 20·256 — mlx's hadamard_transform takes it natively.
778
+
779
+ Trunk corridors (prefix `language_model.`):
780
+ - readers (@R1 input dim, γ folded in): self_attn.q/k/v_proj,
781
+ linear_attn.in_proj_{qkv,z,b,a}, mlp.gate/up_proj, lm_head (γ = model.norm)
782
+ - writers (R1ᵀ output dim): self_attn.o_proj, linear_attn.out_proj,
783
+ mlp.down_proj
784
+ - vision: `vision_tower.merger.linear_fc2` weight+bias is the ONLY
785
+ vision→residual seam (deepstack is empty for qwen3_5 and not ported) —
786
+ output-dim fold; everything else in the tower passes through bf16
787
+ - untouched internal bases: q_norm/k_norm (head-space), linear_attn.{norm,
788
+ A_log, conv1d, dt_bias} (post-projection), rotary
789
+ - 27B trunk is untied; the 0.8B proof subject is tied (untie step exercised)
790
+
791
+ MTP companion (separate artifact, same seed/R1 — shared residual basis):
792
+ - `fc [H,2H]`: input block 0 = embedding stream (γ = pre_fc_norm_embedding),
793
+ block 1 = hidden stream (γ = pre_fc_norm_hidden) — per-block γ+@R1 — plus
794
+ an output-dim R1ᵀ fold; both pre-fc norms → ones
795
+ - `layers.0`: standard full-attention corridor treatment
796
+ - **final `norm` γ is DROPPED (→ ones):** it feeds the SHARED trunk lm_head,
797
+ which already carries the trunk's final γ. Draft logits see γ_trunk instead
798
+ of γ_mtp — draft-quality-only (the verified target path is exact); revisit
799
+ lever = ship a private folded head inside the companion. Persisted as the
800
+ `mtp-final-gamma-dropped` deviation.
801
+
802
+ Small-scale proof (0.8B, dequantized from OptiQ-4bit since no bf16 is
803
+ published; fold-parity on dequant-vs-folded-dequant tests the fold exactly):
804
+ teacher-forced logits through STOCK mlx-lm (also the W2 cross-stack check) —
805
+ worst per-position KL 0.00353, argmax flips 2/42 positions BOTH at reference
806
+ margin 0.0 (exact ties). Scripts: `scripts/turboquant/dequant-model.ts`,
807
+ `scripts/turboquant/fold-qwen35.ts`, `scripts/turboquant/tq-fold-parity.py`.
808
+
809
+ Quantization packaging (corrected 2026-08-18: OptiQ does NOT fully strip
810
+ vision — its main weights omit it but `optiq/optiq_vision.safetensors`
811
+ carries it, and `optiq/mtp.safetensors` bundles MTP; their artifact also
812
+ publishes `optiq/sensitivity.json`. No kv_config.json is published.) Our
813
+ packaging: language modules quantize uniformly (embed + lm_head included),
814
+ `vision_tower.*` is predicate-excluded and stays bf16 both IN-MAIN (mlx-vlm
815
+ compatible — OptiQ's sidecar-only vision is not) and as the `optiq/` sidecar
816
+ (`scripts/turboquant/tq-make-vision-sidecar.ts`), so one self-contained
817
+ artifact serves text+vision across stacks; the folded MTP companion ships at
818
+ `<model>/mtp/` (engine: `--draft-kind mtp` with no `--draft-model` resolves
819
+ it).
820
+
821
+ ## Subject models
822
+
823
+ - **W0/W3: mlx-community/Llama-3.2-1B-Instruct-bf16** (~2.5 GB; the
824
+ reference repos' literal target family; TIED embeddings → exercises step
825
+ 0; hidden 2048 / head_dim 64, both pow-2). MiniCPM5-1B is the alternate
826
+ subject (no published mlx bf16; geometry fold-friendly: untied, head_dim
827
+ 128, hidden 1536 = had12 ⊗ H128, eps 1e-6).
828
+ - **W4: mlx-community/Qwen3.8-27B-bf16** (11 shards, 54.7 GB) +
829
+ `Qwen3.8-27B-MTP-bf16` companion (~850 MB). The companion shares the
830
+ trunk's residual basis → must fold consistently (W1 decision). 27B bf16
831
+ never fully loads on either laptop: folding bit-identity is proven at W0
832
+ scale plus per-tensor checks; the 27B only ever RUNS folded-and-quantized.
833
+ Streaming fold/quantize (`ShardedWriter` + `Weights.releaseShard`; fold
834
+ peak 17.9 GB after the naive whole-list path OOM'd a 51 GB model).
835
+
836
+ ## W0 fold proof (2026-08-17, Llama-3.2-1B, M1 Max 32 GB, busy box — correctness only)
837
+
838
+ Folded model loads + generates through the UNMODIFIED engine
839
+ (UniversalDenseModel path); teacher-forced two-model KL (16×256 tok) mean
840
+ 0.00131 / median 0.00119 / p95 0.00287 — below the KV curve's
841
+ quality-neutral kv8 yardstick (0.00246); greedy 64-token trajectories 3/6
842
+ token-identical, 3 diverge only at near-ties (margins 0.0/0.125/0.125);
843
+ folded weights fully re-expressed (mean|Δ|≈mean|w|), per-tensor max|w| down
844
+ 3–5× (q_proj 0.676→0.162, down_proj 0.578→0.110, gate 0.965→0.367), down_proj
845
+ excess kurtosis 1.19→0.22. Runner: `scripts/turboquant/w0-fold-llama.ts`
846
+ (`--skip-r1/--skip-r2` bisection arms + `turboquant_fold.json` sidecar).
847
+
848
+ ## W3/W4 measured curve (2026-08-18, RTN affine g64, ppl via stock mlx-lm on the local UF-derived corpus, seq 512; M1 Max 32 GB)
849
+
850
+ **Headline: rotation-only + RTN does NOT beat plain RTN at the 4-bit
851
+ operating point; it wins decisively at 3-bit — the paper's law, reproduced.**
852
+ Per-module function-space Frobenius error is a wash at 4-bit (~0.091 all
853
+ arms, `scripts/turboquant/tq-quant-error.py` — γs are tame, 0.7–2.7, no
854
+ module class is the culprit); the 4-bit regression is the anisotropy story
855
+ (isotropic rotated error vs activation-aligned plain error), which is what
856
+ GPTQ-style calibration fixes — the composition named as the real win.
857
+
858
+ 0.8B lab (dequant-OptiQ source; PAIRED arms; bf16 anchor 6.41, 48×512):
859
+
860
+ | arm | bpw | ppl |
861
+ |---|---|---|
862
+ | plain 4-bit | 4.50 | 7.01 |
863
+ | TQ(R1+γ) 4-bit | 4.50 | 7.39 ← regression, reproduces 27B |
864
+ | plain 3-bit | 3.50 | 19.19 ← RTN collapse |
865
+ | **TQ 3-bit** | 3.50 | **14.54** ← rotation −24% ppl |
866
+ | plain mixed (3-bit MLP, 4-bit attn/embed) | 4.15 | 9.69 |
867
+ | **TQ mixed (same profile)** | 4.24 | **9.22** ← paired rotation win |
868
+
869
+ 27B (32×512, same corpus): plain 4-bit 4.659 ±0.093 (4.50 bpw, 15.0 GB) ·
870
+ TQ 4-bit 4.923 ±0.102 (+5.7%, consistent with the lab) · **TQ-mixed (4-bit
871
+ attn/embed + 3-bit MLP) 4.932 ±0.098 at 3.86 bpw / 13.9 GB** — matches
872
+ rotated-uniform-4-bit quality at −0.64 bpw; the artifact of record for the
873
+ 14z M4-Pro fit lever.
874
+
875
+ AWQ-style equalization spike (norm-carried per-channel scales, α=0.5,
876
+ function-preservation verified at KL 0.0037/0 flips): TQ+eq 7.363 (≈no
877
+ change), plain+eq 7.809 (HURT — per-channel scales inside a g64 group widen
878
+ group ranges; real AWQ grid-searches a far gentler α and the real 4-bit fix
879
+ is GPTQ-style compensated rounding). Scripts kept:
880
+ `scripts/turboquant/tq-collect-actstats.py` + `tq-equalize.ts`.
881
+
882
+ **End-to-end validation of the TQ-mixed 27B artifact (2026-08-18, M1 Max,
883
+ busy box — correctness only):** server chat with correct arithmetic
884
+ reasoning (thinking → `reasoning` field); vision through the folded merger +
885
+ sidecar correctly identifies the gradient fixture's colors over HTTP; MTP
886
+ via the 14g harness with the same-seed folded companion: 71% acceptance,
887
+ 2.40 tok/target-forward, arms token-identical (losslessness), ON/OFF 0.726×
888
+ (consistent with the stock 0.821× correct-but-slower verdict; MTP stays
889
+ opt-in).
890
+
891
+ Consequence for the release recipe: do NOT ship rotated uniform-4-bit (loses
892
+ to the trivial baseline); rotation's band is the ≤4-bpw mixed recipe where
893
+ it is decisively ahead. The 4.5-bpw flagship win waits for the calibration
894
+ composition (W5).
895
+
896
+ ## W5a corrected-GPTQ matrix (2026-08-18; 0.8B lab, 4.5 bpw arms, 48×512 ppl, anchor = dequant-OptiQ 6.41 — verified lossless: original OptiQ scores 6.4077)
897
+
898
+ mlx-lm's shipped GPTQ (`mlx_lm/quant/gptq.py`) has TWO real defects that
899
+ partially cancel: the in-loop update window is `k:k+j` (over-propagates past
900
+ the block edge; must be `k:j` — the k term itself writes the quantized value
901
+ since e·Hinv[k,k] = w−q) and `err[..., k:k+1]` indexes a group-local buffer
902
+ with the GLOBAL k (mlx out-of-range slice assignment silently no-ops →
903
+ cross-group propagation lost after block 0). Fixing only err
904
+ DOUBLE-compensates [j, k+j) and REGRESSES below RTN (measured: 7.22/7.50);
905
+ fixing both = paper GPTQ. Worth an upstream report. Fork:
906
+ `scripts/turboquant/tq-gptq.py` (also restricts GPTQ+fallback to language
907
+ modules — vision H is a zero scalar and stays bf16).
908
+
909
+ | arm | ppl |
910
+ |---|---|
911
+ | **plain + GPTQ (fixed)** | **6.741** |
912
+ | TQ(R1) + GPTQ (fixed) | 6.847 |
913
+ | plain RTN | 7.010 |
914
+ | TQ RTN | 7.390 |
915
+
916
+ **Verdict: calibration is the main course; rotation SUBTRACTS at the 4-bit
917
+ band even under GPTQ on this family** (gap −1.6%; GPTQ closes half the
918
+ RTN→anchor gap). Rotation remains the sub-4-bpw lever (−24% at 3-bit). 27B
919
+ needs a chunked Hessian/GPTQ driver (stock flow = whole bf16 model + all
920
+ Hessians resident; down_proj H = 1.2 GB f32 each) —
921
+ `scripts/turboquant/tq-gptq-27b.py` (per-layer checkpoints, resumable, peak
922
+ ≈ one bf16 layer + one Hessian + activations ≈ 6 GB).
923
+
924
+ ## W5c 27B production results (2026-08-18)
925
+
926
+ **v2 run (uniform GPTQ-4): BROKEN artifact (ppl 306k).** Root cause,
927
+ evidence-backed: the compensation loop DIVERGED on layers with
928
+ rank-deficient calibration Hessians (layer-0 down_proj group scales grew
929
+ 0.003 → 27 monotonically across columns, max 2.8e7; cosine ~0 vs source;
930
+ neighbors healthy). Early layers see low-rank activations (layer-0 input =
931
+ raw embeddings; rank ≤ distinct calibration tokens), so 5120 dims outran
932
+ the standard 1% damping — the 0.8B (1024 dims) never hit it.
933
+
934
+ **v3 fixes (all landed in the 27B driver):** divergence guard in
935
+ `gptq_one_guarded` (GPTQ scales vs 4× the RTN scale ceiling per matrix;
936
+ damping escalation 1e-2→10; RTN fallback = never worse than RTN); 4×
937
+ calibration (128×512); sensitivity-driven per-module bits from OptiQ's
938
+ published sensitivity.json; greedy benefit-per-param to `--target-bpw`.
939
+ v3 27B: 169 modules @8-bit, 4.80 bpw, 17.0 GB, ZERO guard triggers (the
940
+ calibration bump fixed conditioning outright), 64 layers in 166 min.
941
+
942
+ **27B ppl ladder (73×512, UF corpus, stock mlx-lm):** plain RTN-4 4.570
943
+ ±0.060 · OptiQ 5.14 bpw 4.574 ±0.061 · GPTQ+sens 4.80 bpw 4.618 ±0.061.
944
+ **At 27B every sane ≥4.5-bpw recipe SATURATES this instrument** — OptiQ's
945
+ +0.6 bpw buys nothing measurable, and GPTQ shows a small consistent deficit
946
+ (likely calibration-domain mismatch: calibration_v5 vs chat-flavored eval
947
+ text). This contradicts the 0.8B lab ordering (GPTQ clearly won there) —
948
+ quantization robustness grows with scale. Decision moved to task
949
+ benchmarks: MMLU-100 + GSM8K-50 across the arms (`scripts/turboquant/
950
+ tq-evals.py`, paired items through stock mlx-lm).
951
+
952
+ ## FINAL BOARD + DECISION (2026-08-18 night 2)
953
+
954
+ Task evals (lean mlx-lm runner, paired items; in-engine sweep swap-thrashed
955
+ the box — 73 GB swap at 0.7 GB resident — killed, gap recorded below):
956
+
957
+ | arm | bpw | GB | ppl(73×512) | MMLU-100 | GSM8K-50 |
958
+ |---|---|---|---|---|---|
959
+ | **GPTQ+sens v3 (SHIPPED as mjriii/Qwen3.8-27B)** | 4.80 | 16.3 | 4.618 | 87 | **96** |
960
+ | plain RTN 4-bit | 4.50 | 15.0 | **4.570** | 88 | 94 |
961
+ | OptiQ published | 5.14 | 18.1 | 4.574 | **89** | 92 |
962
+ | TQ-mixed (staged, small-footprint variant) | 3.86 | 13.0 | 4.93 | 82 | 96 |
963
+
964
+ All ≥4.5-bpw arms statistically tied on every instrument — 27B quality
965
+ SATURATES at 4-bit; recipes differentiate below 4 bpw and on completeness.
966
+ THE artifact = v3 (never trails; protective recipe; one-repo
967
+ vision+video+MTP; additive to the ecosystem where plain-4bit already exists
968
+ publicly). Serve gauntlet on THE artifact through OUR engine: text ✓
969
+ (23×19), image ✓ ("Green and pink"), video ✓ (gradient motion described;
970
+ AVFoundation chain), MTP harness 76% accept / 2.53 tok-per-forward with an
971
+ output-divergence flag vs plain greedy (consistent with verify-width
972
+ reduction-order near-ties, 12B step-0 class — margin analysis queued; NOT
973
+ claimed lossless on the card).
974
+
975
+ ## Territory-mapping campaign (2026-08-18/19, Josh-directed: "map all the levers before concluding TQ-off")
976
+
977
+ **sens3 arm (Josh's recipe: rotated 3-bit base + sensitivity-driven 8-bit,
978
+ 4.26 bpw / 15.25 GB incl. vision):** ppl 4.630 ±0.058 — TIED with the
979
+ 4.80-bpw flagship (4.618) at −0.54 bpw, and a full recovery over unprotected
980
+ TQ-mixed (4.93). GSM8K 46/50 = 92% (plateau band). **MMLU 83/100 — the
981
+ 3-bit-base dip persists** (TQ-mixed scored 82): two independent 3-bit-base
982
+ arms at 82–83 vs the 87–89 plateau → the knowledge-recall damage is SMEARED
983
+ across the MLP long tail; KL-derived sensitivity shields fluency, not
984
+ recall.
985
+
986
+ Perplexity proxy calibration (from our own table): deltas <~0.1 ppl are
987
+ task-invisible; ppl's remaining role is catastrophe detection + screening
988
+ (see the g128 lesson below, which retired the "~0.3 ppl = visible damage"
989
+ rule). mlx kernel bound: group_size ∈ {32, 64, 128} — 256+ needs custom
990
+ kernels (non-goal).
991
+
992
+ **Group-size lab (0.8B ppl) + 27B g128 arm:** rotation does NOT buy
993
+ coarse-group tolerance at 4-bit (tax +4.7% rotated vs +4.2% plain); at 3-bit
994
+ the dividend is real but partial (+29% vs +47%; rotated 3.25 bpw ≈ plain 3.5
995
+ bpw). 27B plain-4bit-g128 (4.25 bpw): ppl 4.714 (+3%), MMLU 88 (plateau!),
996
+ **GSM8K 62% — COLLAPSE via generation-behavior damage** (correct-but-rambling
997
+ truncations + instant-EOS empty outputs; single-forward instruments blind to
998
+ it). Lessons: (1) ppl deltas DO NOT bound task damage (+0.14 ppl ↔ −32 GSM8K
999
+ pts) — every finalist needs generative tasks; (2) g128 disqualified for
1000
+ generative work.
1001
+
1002
+ **Run B — the full suite (rotation + GPTQ + sensitivity, 4.80 bpw, 17.0 GB,
1003
+ zero guard triggers):** ppl **4.5965** (best quantized arm, tied w/
1004
+ plain-4bit) · MMLU 84 · raw-completion GSM8K **24% — and the collapse is a
1005
+ FORMAT CLIFF, not a reasoning failure**: when it answers it is flawless, but
1006
+ 3/4 raw few-shot prompts get INSTANT EOS; under the native chat template it
1007
+ always answers (3/5 correct at a tight token cap). g128's 62% was the same
1008
+ failure at lower frequency. **Methodology consequence: the raw-completion
1009
+ GSM8K column conflates math ability with EOS-cliff robustness** — different
1010
+ quant-noise realizations displace a razor-thin answer-vs-stop margin at
1011
+ token 0. The map carries TWO generative columns: templated GSM8K
1012
+ (serving-path reasoning) and raw-completion GSM8K (format-robustness probe).
1013
+ Neither ppl nor MMLU sees any of this.
1014
+
1015
+ ## THE FRONTIER (campaign complete, 2026-08-19; chart artifact `reports/frontier-map-2026-08-19.html` is machine-local — `reports/` is gitignored)
1016
+
1017
+ | arm | bpw | GB | ppl | MMLU | tGSM | rawGSM | tok/s* | Pareto |
1018
+ |---|---|---|---|---|---|---|---|---|
1019
+ | **rot+sens48 RTN (Josh's recipe)** | 4.79 | 17.0 | 4.680 | **90** | 94 | 84 | **11.80** | **YES** |
1020
+ | GPTQ+sens (staged flagship) | 4.80 | 17.0 | 4.618 | 87 | 94 | **96** | 11.42 | — |
1021
+ | rot+GPTQ+sens (full suite) | 4.80 | 17.0 | **4.597** | 84 | 94 | 24 | 11.77 | — |
1022
+ | OptiQ published | 5.14 | 18.1 | 4.574 | 89 | 94 | 92 | 8.92 | — (dominated) |
1023
+ | **TQ-mixed** | 3.86 | 13.0 | 4.932 | 82 | 82 | 96 | 10.94 | **YES** |
1024
+ | bf16 ceiling (streamed eval, `tq-bf16-ceiling.py`) | 16 | 54.4 | 4.552 | 87 | n/a | n/a | n/a | ref |
1025
+
1026
+ *M1 Max 32 GB, busy box, spreads 8–34% — directional until a quiet
1027
+ `scripts/bench-serve.ts all` run (probes: `scripts/turboquant/tq-tps.ts`).
1028
+
1029
+ Findings of record: (1) the ≥4.5-bpw band sits AT the bf16 ceiling (ppl
1030
+ within ~1%, MMLU within noise — a quant scoring 90 vs ceiling 87 proves ±3
1031
+ noise); (2) BOTH Pareto points are ROTATED — rotation at 4-bit is
1032
+ competitive exactly when paired with sensitivity allocation (the earlier
1033
+ uniform-rotation loss concentrated in the sensitive modules, as Josh
1034
+ hypothesized), and mandatory below 4 bpw; (3) OptiQ is dominated on all
1035
+ three axes (its extra 0.34 bpw buys nothing and costs ~25% decode); (4)
1036
+ GPTQ's measurable 27B contribution is ppl-only — it buys no task quality at
1037
+ this scale and (with rotation) costs raw-format robustness; (5) 3-bit
1038
+ kernels pay an unpack tax that eats their bandwidth win on M1 (see the M4
1039
+ row for the reversal); (6) cross-engine parity CERTIFIED (byte-identical
1040
+ greedy, ours vs stock mlx-lm, on the staged flagship). Certification
1041
+ benchmark for the chosen artifact: GPQA Diamond at reasoning_effort=xhigh
1042
+ (`scripts/turboquant/tq-gpqa.py`, local mlx-lm or `--server-url` through a
1043
+ running `mlx-bun serve`; greedy runs bit-identical across the two paths).
1044
+
1045
+ ## M4 Pro 24 GB speed rows (2026-08-20, mlx-bun, `scripts/turboquant/tq-speed-row.ts`)
1046
+
1047
+ Winner artifact (rot+sens48), second reference machine (M4 Pro, 24 GB,
1048
+ ~273 GB/s), `sysctl iogpu.wired_limit_mb=21504`, DeltaNet leak fix applied:
1049
+
1050
+ | prompt | prefill tok/s | TTFT | decode tok/s | notes |
1051
+ |---|---|---|---|---|
1052
+ | 1,024 | 127 | 8.1 s | 14.66 (1.1%) | chunk 2048 |
1053
+ | 8,192 | 124 | 66 s | 14.13 (1.3%) | chunk 2048 |
1054
+ | 32,768 | 114 | 288 s | 12.77 | chunk 1024 (peak 19.3 GB) |
1055
+
1056
+ - **Compute-bound decode confirmed cross-machine:** 14.7 tok/s here vs 11.8
1057
+ on the M1 Max (400 GB/s) — the machine with 68% of the bandwidth is 25%
1058
+ FASTER. Decode is dequant-ALU-bound, not weight-streaming-bound (M1
1059
+ telemetry: GPU 100% @ ~266 GB/s of 400). Headroom to the ~24 tok/s
1060
+ bandwidth roofline is a future perf program.
1061
+ - **Decode-at-depth is nearly flat** (−13% at 32× context): only 16 of 64
1062
+ layers grow KV; DeltaNet state is constant.
1063
+ - **Prefill transient scales with chunk × offset** (~2.8 GB at 24k with
1064
+ 2048-chunks; measured via `MLX_BUN_PREFILL_MEM_LOG` and
1065
+ `scripts/turboquant/tq-mem-probe.ts`): on 24 GB the 32k row needs
1066
+ 1024-token chunks (−8% prefill). Engine follow-up: serve should scale
1067
+ prefillChunkSize from fit.ts headroom automatically.
1068
+ - Prefill absolute rate (~125 tok/s) is the weak axis — suspected DeltaNet
1069
+ per-token recurrence during prefill; compare against mlx-lm same-box
1070
+ before treating as our bug (oracle-gap rule).
1071
+
1072
+ Compact sibling (TQ-mixed) on the M4, same protocol, chunk 1024 all rows:
1073
+
1074
+ | prompt | prefill tok/s | TTFT | decode tok/s (spread) |
1075
+ |---|---|---|---|
1076
+ | 1,024 | 117 | 8.8 s | **17.14** (0.7%) |
1077
+ | 8,192 | 124 | 66 s | 16.05 (2.4%) |
1078
+ | 32,768 | 85 | 6.4 min | 13.80 (2.9%) |
1079
+
1080
+ **The 3-bit speed verdict is GPU-generation-dependent:** on M1 Max the
1081
+ 3-bit-heavy artifact decoded SLOWER than the 4/8 winner (10.94 vs 11.80 —
1082
+ unpack ALU dominates); on M4 Pro it decodes FASTER (17.14 vs 14.66 — newer
1083
+ ALU absorbs the unpack, fewer bytes win). Card guidance: M1-class → winner
1084
+ fastest; M4-class → compact is smaller AND faster. Compact 32k prefill
1085
+ (85 tok/s) is its weak axis. Peak not instrumented this run; estimated
1086
+ ~16 GB at 32k (likely fits the DEFAULT 24 GB wired limit — untested).
1087
+
1088
+ ## Known engine gaps found in passing (not TQ defects)
1089
+
1090
+ - `mlx-bun perplexity` cannot score qwen3_5: it routes through
1091
+ `trainForward`, whose cache stub lacks the DeltaNet `SSMCache.advance`
1092
+ (`qwen3_5.ts:226` throws on plain and TQ arms identically). 27B ppl runs
1093
+ through stock mlx-lm (`scripts/turboquant/tq-ppl.py`) — which doubles as
1094
+ the cross-stack load check for the release artifact.
1095
+ - bf16 (unquantized) qwen3_5 trunks don't load in our engine:
1096
+ `QuantizedEmbedding.load`/`QuantizedLinear.load` hard-require `.scales`.
1097
+ The small-scale fold proof ran through stock mlx-lm instead. Backlog: not
1098
+ needed to serve the published quant.
1099
+ - `scripts/eval.ts` capability tasks at 27B swap-thrashed a 32 GB box — root
1100
+ cause fixed (chunked `forwardHidden` prefill + allocator clear cadence);
1101
+ verify at 27B post-GPQA.
1102
+ - Qwen MTP via the HTTP serve lane 500'd (`[slice] Invalid number of
1103
+ indices…`) — pre-existing serving-path bug, reproduced with stock
1104
+ artifacts; FIXED 2026-08-20 (draft gate + `[1,V]` sampler contract). The
1105
+ paired MTP harness is `scripts/turboquant/tq-mtp-ab.ts` (its header still
1106
+ cites its old name, `qwen38-mtp-ab.ts`).
1107
+ - DeltaNet prefill leak (~1 MB/token, `contiguous()` returning a no-op view
1108
+ that pinned whole chunk buffers) FOUND+FIXED 2026-08-20 via this program's
1109
+ dogfood gate; 27B now fits 24 GB (peak 19.2 GB at 16k probe).
1110
+
1111
+ ## Weights-leg non-goals
1112
+
1113
+ The original W recipe's scope below does not limit the 2026-09 Qwen performance
1114
+ program. Custom formats, kernels, alternative algorithms and online-transform
1115
+ candidates are now in scope under the gates in
1116
+ [decode-speed-program.md](decode-speed-program.md#7-qwen38-27b-research-program).
1117
+
1118
+ Mirrors the PLAN.md phase: no custom Lloyd-Max weight format / new qmm
1119
+ kernels; no activation quantization; no runtime weight rotation of any kind
1120
+ (weights fold offline; online rotation remains the KV codec's job); no
1121
+ GGUF/AWQ export; group_size > 128.
1122
+
1123
+ ---
1124
+
1125
+ ## Q campaign — sub-4-bpw frontier: KL instrument, trellis, LDLQ, mixed-k (2026-08-31 → 09-02, M1 Max 32 GB)
1126
+
1127
+ Josh's frame: the best Qwen3.8-27B for THIS 32 GB box — smallest, smartest,
1128
+ fastest — using the rotation ("that was the entire point") plus the levers
1129
+ the 900+ published quants do not combine: trellis coding (QTIP/TCQ), LDLQ,
1130
+ and borrowed sensitivity measurements (EXL3/Unsloth/OptiQ). Program tag Q.
1131
+
1132
+ **Q0 instrument — mean KL vs a bf16 teacher, on 32 GB.** `scripts/turboquant/
1133
+ tq-dump-teacher-logits.py` streams the bf16 27B through stock mlx-lm once and
1134
+ writes, per teacher-forced position, the top-2048 logits + full-vocab
1135
+ logsumexp (fp16 lossless for bf16; captures 99.79% of full-vocab KL);
1136
+ `tq-kl-vs-teacher.py` (Python) and `bun scripts/eval.ts kl --reference-logits`
1137
+ (serving path) score any candidate; self-KL is exactly 0 and the two stacks
1138
+ read the dump byte-identically. Corpus `runs/kl-corpus/uf-4096x32` (32×4096
1139
+ UF-derived chat, sha `6594cfbf…`); dump `runs/kl-teacher/qwen38-27b-bf16`
1140
+ (1.61 GB). Calibration warning of record: this protocol runs ~25× hotter than
1141
+ the published self-generated-trace charts (RTN-4 = 0.1396 here vs ~0.005
1142
+ there) — only within-dump comparisons are valid, never cross-paper numbers.
1143
+
1144
+ **KL ladder (mean KL(bf16‖cand), same dump, lower is better):**
1145
+
1146
+ | arm | bpw | GiB | KL | MMLU | tGSM | rawGSM |
1147
+ |---|---|---|---|---|---|---|
1148
+ | bf16 | 16 | 54 | 0 | | | |
1149
+ | RTN-4 g64 (all 4-bit) | 4.5 | 15.0 | 0.1396 | | | |
1150
+ | **Q3: rotation + LDLQ + mixed-k 3.00** (fake-quant; carrier8 dense 0.1568) | **3.55** | **~11.9 proj.** | **0.1553** | **88** | 48 | 44 |
1151
+ | flagship GPTQ+sens (mjriii/Qwen3.8-27B) | 4.80 | 17.0 | 0.1646 | 87 | 94 | 96 |
1152
+ | **compact allocation, NO rotation** (`tqalloc-norot`) | 3.858 | 13.13 | **0.2524** | 85 | 98 | **0/50** |
1153
+ | q1-latemlp, no rotation | ~3.9 | | 0.2837 | | | |
1154
+ | **trellis k3 uniform + rotation** (q2a, fake-quant) | ~3.4 | | 0.4240 | | | |
1155
+ | trellis k3 uniform, no rotation (q2b) | ~3.4 | | 0.5648 | | | |
1156
+ | compact allocation + rotation (mjriii/Qwen3.8-27B-TQ) | 3.86 | 13.9 | 0.6054 | 82 | 82 | 96 |
1157
+ | q1-latemlp + rotation | ~3.9 | | 0.6519 | | | |
1158
+
1159
+ Findings of record: (1) **rotation is a 2×2 crossover, not a lever with a
1160
+ sign** — it HELPS the trellis (0.565 → 0.424) and HURTS affine g64
1161
+ (0.252 → 0.605): the trellis codes a Gaussian source and needs the Hadamard;
1162
+ affine's per-64 scale+bias already absorbs the outliers the rotation spreads.
1163
+ (2) **KL is the screen, not the verdict**: the lowest-KL sub-4-bpw arm scores
1164
+ 0/50 on raw GSM (an EOS cliff a single-forward instrument cannot see) while the
1165
+ rotated compact arm at 2.4× the KL scores 96 — every finalist still needs the
1166
+ task columns. (3) No trellis cell beats the affine compact arm yet; the two
1167
+ open levers are LDLQ (Hessian error feedback) and mixed k — the published
1168
+ EXL3 numbers (their protocol: SC 3 bpw 0.0257 vs UD-Q3_K_XL 0.0209; 4 bpw
1169
+ 0.0062) say both are worth ~20–70%. (4) Weight-MSE is NOT a screen: it
1170
+ predicted the unrotated trellis at 0.18–0.21; the measured KL was 0.5648.
1171
+ (5) **The full recipe wins (2026-09-02):** rotation + LDLQ + EXL3-derived
1172
+ mixed k at the 3.00 MLP budget scores KL 0.1553 at 3.55 coded bpw — below
1173
+ the shipped 4.80-bpw flagship (0.1646), 38% below the previous sub-4 best
1174
+ (compact-norot 0.2524), 11% above all-4-bit RTN (0.1396) — with MMLU 88/100
1175
+ (flagship 87, compact 85), tGSM 48/50 (flagship 48), rawGSM 44/50 (flagship
1176
+ 48, compact-norot 0): no EOS cliff. Each lever alone lost (q2a 0.4240); the
1177
+ levers compound. At n=100/50 a one-point gap is noise: the claim of record
1178
+ is "matches the 4.8-bpw flagship at 3.55 bpw", not "beats it". Gate Q3 PASSED.
1179
+
1180
+ **Q2 trellis codec** (`scripts/turboquant/tq-trellis.ts`): QTIP bitshift
1181
+ trellis L=12, k∈{1..4}, V=1, T=256, tail-biting Viterbi, 1MAD/RPTC/random
1182
+ codebooks; `--validate` reproduces QTIP Table 2 exactly; 1.2 Mw/s on M1 Max
1183
+ after replacing `argmax` (MLX ArgReduce is 85× slower than `max`) with
1184
+ compare + uint8 max. Driver `tq-quantize-trellis.ts`: streaming fold +
1185
+ quantize, trellis on the 192 MLP tensors along the rotated axis, affine
1186
+ elsewhere per the compact allocation, fake-quant output (decoded bf16 flagged
1187
+ `false`, so stock mlx-lm loads it). Real packed kernels (Q2b) only if a recipe
1188
+ wins: Metal has no `lop3`, so decode costs 4–5 ALU ops/weight; M1 decode is
1189
+ already ALU-bound (3-bit affine at 54% of roofline) → predicted 7–9 tok/s on
1190
+ M1 (a regression), wash-to-win on M4 Pro; ~257 dispatches/token; fallback is
1191
+ load-time expansion to affine.
1192
+
1193
+ **Q2c LDLQ** (`tq-ldlq-hessians.py` + `--ldlq <hdir>`): per-layer MLP input
1194
+ Hessians in the folded basis (gate/up captured pre-γ so H' = R1ᵀ E[nnᵀ] R1;
1195
+ down basis-free), block-LDL on CPU, BlockLDLQ error feedback at block T (=
1196
+ the trellis block, so only the objective changes vs q2a), 4× unweighted-peak
1197
+ guard, 20 GiB leak abort. Calibration = 512×128 rows of the SAME chat domain
1198
+ (disjoint from the scored rows; a domain mismatch was measured to hurt GPTQ).
1199
+ Hessians: 64 layers in 35 min, 0/128 factorization fallbacks. The uniform-k3
1200
+ LDLQ arm (Q2c) was paused at 72/192 and superseded: Q3 went straight to the
1201
+ full recipe, so LDLQ's isolated contribution is not separately measured
1202
+ (q2a → Q3 is LDLQ + mixed-k together, 0.4240 → 0.1553). Bug found on resume
1203
+ (2026-09-02): the k-map refactor's block loop shadowed the codec k
1204
+ (`trellisFor(blockIdx)`); the Trellis ctor now rejects K outside [1,L), the
1205
+ L=0 selftest is byte-identical again, and the real-L probe on layer 21 runs
1206
+ at 5.6 GiB peak with the unweighted MSE +32/+46% (gate/down), as expected for
1207
+ a Hessian-weighted objective.
1208
+
1209
+ **Q3 mixed k** (`--k-map reports/qwen38-allocation/trellis-kmap.json
1210
+ --k-budget 3.00`): per-tensor k∈{2,3,4} greedily allocated from turboderp's
1211
+ EXL3 per-tensor KLD-vs-k table for this exact model (gate/up tied like EXL3's
1212
+ own recipe; 94.3% agreement with their 3.00 bpw recipe). Budgets 2.75 / 3.00
1213
+ / 3.25 avg-MLP-bpw = k2/k3/k4 68/104/20, 26/140/26, 2/140/50 (≈10.8 / 11.3 /
1214
+ 11.8 GiB projected). Run 2026-09-01/02 at 3.00: 259 min (257 in the encoder,
1215
+ 1.11 Mw/s), 192 trellis tensors, 3.5468 coded bpw over 27.3 G params, 38 GiB
1216
+ fake-quant on disk; artifact `mjriii/Qwen3.8-27B-q3-trellis-ldlq-k300-fakequant`
1217
+ on the external volume, KL row `rows/q3-ldlq-k300.json`. Result: see finding
1218
+ (5). 3.25 not run — 3.00 already clears the gate; a 2.75 arm is the next
1219
+ size lever, not 3.25.
1220
+
1221
+ **Q2b packed format + kernels** (`src/quantize/trellis.ts`,
1222
+ `src/model/trellis-linear.ts`, driver `scripts/turboquant/
1223
+ tq-quantize-trellis-packed.ts`; the fake-quant codec/driver that produced the
1224
+ Q3 record are frozen, these are new files). On disk a trellis tensor is
1225
+ `.weight` uint32 `[rows, cols·k/32]` + `.scales` fp16 `[rows]`, config entry
1226
+ `{mode:"trellis", bits:k, group_size:T, trellis:{L, code:"1mad", axis}}`
1227
+ (axis 1 = coded along the input dim, stored `[out, in·k/32]`; axis 0 = along
1228
+ the output dim, stored as Wᵀ `[in, out·k/32]`). One block = T·k bits; symbol
1229
+ b_t sits at bit offset (T−1−t)·k — REVERSED time — so the L-bit window at that
1230
+ offset (wrapping, tail-biting) IS state_t, and any weight decodes in O(1):
1231
+ two word loads, shift, mask, LUT. No sequential dependency, hence a plain
1232
+ matvec. Three Metal kernels share that primitive: `reduce` (axis 1, one SIMD
1233
+ group per output row, lanes stride consecutive coded positions → coalesced,
1234
+ `simd_sum`), `scatter` (axis 0, 32 lanes = 32 consecutive outputs of one
1235
+ block, loop over inputs, split-K ×16 partials summed by one mlx op), and
1236
+ `expand` (whole tensor → bf16 for M>4, then a stock matmul; ≤178 MB
1237
+ per expanded tensor). Packed prefill evaluates each projection before
1238
+ building the next. Qwen also evaluates each layer's cache outputs: the
1239
+ copied conv tail otherwise retains the whole chunk's conv input until
1240
+ end-of-chunk cache evaluation. Decode remains lazy and uses packed kernels.
1241
+ These evaluation boundaries bound temporary buffer lifetimes; disposing a
1242
+ JS tensor handle alone does not release a lazy consumer's references.
1243
+ The reconstructed weight is bf16(f32(lut[state])·f32(scale)) in
1244
+ every path — bit-identical to the fake-quant artifact's stored bf16 (unit test
1245
+ `tests/unit/trellis-linear.test.ts`: host unpack + expand kernel exact for
1246
+ k∈{2,3,4}; matvecs within 2e-3 of a bf16 matmul). Engine wiring is additive:
1247
+ `QuantSpec.trellis`, `loadLinear`/`Qwen3MLP` pick `TrellisLinear` per module;
1248
+ trellis MLP tensors are not LoRA targets yet. `MLX_BUN_TRELLIS=expand` is the
1249
+ load-time fallback (8-bit affine carrier). Encoding a packed artifact reruns
1250
+ the Viterbi (the fake-quant stores values, not states; a state path cannot be
1251
+ recovered from values because the 4096 states map onto 1021 distinct values).
1252
+
1253
+ **Q2b measured (2026-09-02, M1 Max 32 GB).** Packed Q3 artifact
1254
+ (`Qwen3.8-27B-q3-trellis-ldlq-k300-packed`): **12.14 GiB** on disk (fake-quant
1255
+ 38 GiB, flagship 17 GiB), LDLQ applied 192/192, 0 guard trips; decoded
1256
+ weights bit-identical to the fake-quant artifact through the expand kernel
1257
+ (0 mismatches in 89 M per tensor, k=2 and k=3, both axes). Loads and generates
1258
+ through the qwen3_5 graph; KL through OUR serving path 0.15501 (mlx-lm on the
1259
+ fake-quant: 0.15532). Decode kernels, pipelined 8-deep (the in-graph cost; an
1260
+ eval-per-call bench adds ~0.3 ms of sync and misleads), gate/up [17408×5120]
1261
+ and down [5120×17408], k=3, M=1:
1262
+
1263
+ | kernel | no decode (floor) | inline 1MAD, f32 weight | stock affine 3-bit g64 |
1264
+ |---|---|---|---|
1265
+ | gate/up matvec (reduce) | 0.24 ms | 0.41 ms | 0.16 ms |
1266
+ | gate+up+swiglu fused | 0.37 ms | 0.73 ms | 2×0.16 |
1267
+ | down matvec (scatter) | 0.54 ms | 0.77 ms | 0.16 ms |
1268
+
1269
+ Whole model: **9.3 tok/s** vs the 4.80-bpw affine flagship at **18.9** on the
1270
+ same box (kernels v3, `c0a1e00`); the first kernels read 4.4. What the
1271
+ decomposition says: (1) the 1MAD decode is ~12 int ops/weight and costs
1272
+ 0.17 ms per 89 M weights; a precise divide cost 1 ms (replaced by y×(1/d),
1273
+ which is 1-ulp-off f32 for 398 of 1021 code values — a 2-FMA residual step
1274
+ restores exactness at +0.05 ms, `trellis_val_rcp`); (2) rounding the decoded
1275
+ weight to bf16 (to match the artifact's stored bf16) cost as much as the
1276
+ decode, so the served weight is now f32 code×scale — MORE accurate than the
1277
+ fake-quant, no longer bit-identical to it — measured on the served DECODE
1278
+ path (`eval.ts kl --decode --self MLX_BUN_TRELLIS_VARIANT 1→6`, 16 prompts ×
1279
+ 32 teacher-forced steps): KL(bit-exact ‖ f32-weight) mean 0.00089, p95
1280
+ 0.0025, i.e. 0.6% of the model's own 0.155 to the teacher (the prefill/expand
1281
+ path always rounds to bf16 and is unaffected); (3) the threadgroup LUT (16 KB) halves
1282
+ occupancy and the device LUT gathers 32 lines per load — both slower than
1283
+ computing the code; (4) the axis-0 (down) matvec is the hard one: the code
1284
+ sequence runs along the OUTPUT dim, so a reduction over inputs is a column
1285
+ gather; lanes-over-outputs looping rows costs a full line per 16-byte
1286
+ segment, and lane-per-word (whole-line reads, shuffle for spilled windows)
1287
+ did not beat it — its no-decode floor is 3× affine. M1 decode at these sizes
1288
+ is latency/occupancy bound (affine reaches 200 GB/s of 400), so every ALU op
1289
+ per weight lands on the wall clock. Verdict: the packed format works and is
1290
+ the honest 12 GiB artifact; on M1 Max it decodes at ~half the flagship's
1291
+ speed. Levers left: code down_proj along its INPUT dim (a recipe change —
1292
+ the rotated axis is the output dim — needs a re-encode and a KL check),
1293
+ half-precision LUT (8 KB, occupancy back, +double rounding), or accept
1294
+ M4-class ALU. `MLX_BUN_TRELLIS=expand` (8-bit carrier, 23 GiB) is the
1295
+ full-speed fallback where it fits.
1296
+
1297
+ **The down_proj axis, settled (2026-09-02 night).** Re-encoded the same recipe
1298
+ with `--down-axis in` (down_proj coded along its INPUT dim — the un-rotated
1299
+ intermediate — so all three MLP projections decode with the plain reduce
1300
+ kernel; `--reuse` copied the 128 unchanged gate/up tensors, so only 64 Viterbi
1301
+ runs, 98 min):
1302
+
1303
+ | arm | bpw | GiB | KL (engine) | top-1 | decode tok/s |
1304
+ |---|---|---|---|---|---|
1305
+ | down coded along the ROTATED output dim | 3.5468 | 12.14 | 0.15501 | 86.38% | 9.3 |
1306
+ | **down coded along the INPUT dim** | 3.5463 | 12.14 | **0.15543** | **86.59%** | **11.3** |
1307
+
1308
+ KL is a wash (+0.0004, 0.3% of the arm's own distance to the teacher) and
1309
+ top-1 is marginally BETTER — so on the screen the input axis looked like a
1310
+ free +22% decode. **The task columns overturned it** (8-bit carriers, same
1311
+ frozen items, `tq-evals.py`):
1312
+
1313
+ | arm | MMLU-100 | tGSM-50 | rawGSM-50 |
1314
+ |---|---|---|---|
1315
+ | down along the rotated output dim | 88 | 48 | **44** |
1316
+ | down along the input dim | 88 | 47 | **29** |
1317
+
1318
+ The rawGSM regression is STRICTLY NESTED — 15 items the output-axis arm
1319
+ answers correctly that the input-axis arm fails, **0 the other way** (McNemar
1320
+ p ≈ 3e-5) — and the failure mode is an EMPTY continuation: on those items the
1321
+ model emits EOS immediately after `A:` (verified by dumping the raw
1322
+ generations). That is the same EOS-cliff class as the unrotated affine arm
1323
+ (`tqalloc-norot`, rawGSM 0/50 at KL 0.2524), and it is invisible to MMLU
1324
+ (logprob over four letters), to templated GSM (chat template, 47 vs 48), and
1325
+ to KL (a single-forward instrument that never asks the model to STOP).
1326
+
1327
+ **Finding of record (third instance of "KL is the screen, not the verdict"):
1328
+ one-sided incoherence processing must reach the CODED axis.** R1 gaussianizes
1329
+ down_proj's output dim; coding along the un-rotated input dim leaves that
1330
+ sequence non-Gaussian for a codebook that assumes unit-variance Gaussian
1331
+ inputs, and what breaks is not average next-token quality but the model's
1332
+ raw-format stopping behavior. **Carry-forward stays the output-axis arm**
1333
+ (`…-k300-packed`, 9.3 tok/s); `--down-axis in` is kept as the measured
1334
+ counter-example, not a default, and the scatter kernel stays load-bearing.
1335
+ The re-encode also surfaced a driver bug: the LDLQ Hessian must be chosen by
1336
+ MODULE, not by trellis axis (`2b29b18`).
1337
+
1338
+ **Eval carrier** (`scripts/turboquant/tq-repack-fakequant.ts`): a 38 GiB
1339
+ fake-quant cannot load dense on 32 GB, and `tq-evals.py` (MMLU logprob
1340
+ scoring, GSM greedy generation) needs the whole model resident. The tool
1341
+ re-packs exactly the bf16 MLP tensors flagged `false` as 8-bit g64 affine
1342
+ (≈ −45 dB, negligible next to the ≈ −13 dB trellis error) and passes every
1343
+ other tensor through byte-identical: 23 GiB, loads in 24 s, dense KL 0.1568
1344
+ vs 0.1553 streamed (+1%, identity confirmed). All task columns for trellis
1345
+ arms are measured on the carrier. NOT a shipping format — the real footprint
1346
+ needs Q2b (packed trellis + Metal decode kernel).
1347
+
1348
+ ## Packed kernel experiments (2026-09-05, M4 Pro 24 GB)
1349
+
1350
+ Experimental `MLX_BUN_TRELLIS_VARIANT=7` reuses each decoded gate/up weight
1351
+ across M=2..4 input vectors. The new kernels live in
1352
+ `src/model/trellis-shared-m.ts`; the original kernels remain the default
1353
+ (variant 6). Variant 7 keeps the same two-accumulator order, reductions and
1354
+ bf16 activation boundaries. M=1, scatter/down and M>4 expansion retain the
1355
+ variant-6 decoder and kernels. No weight format or crossover changed.
1356
+
1357
+ The kernel diagnostic now loads an individual layer's real packed tensors
1358
+ with `--model-path`, selecting a gate bit-width via `--k` (and optionally
1359
+ `--layer`). It records the actual per-role geometry, tensor SHA-256 hashes,
1360
+ source identity, every timing sample and memory. `--variants 6,7` compares
1361
+ the implementations; `--reference-variant 6` checks outputs separately from
1362
+ timing. `--m`, `--seed`, `--reps` and `--pipe` control the micro-workload;
1363
+ `--skip-affine` excludes synthetic affine controls, which are not a quality
1364
+ comparison. `--json reports/<name>.json` preserves results. Its complete-MLP
1365
+ cell retains gate/up → SwiGLU → down dependencies and production barriers.
1366
+ `--kernel-module` permits isolated candidate modules with the same exports.
1367
+
1368
+ The first screen covers the packed artifact's k2/k3/k4 matrices at
1369
+ M=1/2/3/4/5/8. Shared-M reduce and fused gate/up prototypes match the observed
1370
+ outputs at M=1..4. Six sequential AB/BA blocks with different activation seeds
1371
+ retain the gain in the complete MLP. Raw evidence is under
1372
+ `reports/qwen38-rd/paired-shared-m/`; integrated variant-7 checks are under
1373
+ `reports/qwen38-rd/variant7/`. Unit tests also compare exact output bytes for
1374
+ bf16/f32, both axes, fused activation, the complete MLP and the M=4/5 boundary.
1375
+ The complete packed 27B also passes candidate/current equality for all logits,
1376
+ live KV/recurrent state and a subsequent token at M=1..5. Six AB/BA full-model
1377
+ forward blocks at M=2/4 preserve state/logit identity on distinct token inputs;
1378
+ results are in `reports/qwen38-rd/shared-m-full-model.json`. These timings
1379
+ project every position from a short shared prefix. They do not measure HTTP
1380
+ generation or strict fill's last-position-only head. The machine fails the
1381
+ quiet/swap gate, so user-workload A/B and quiet M4 Pro evidence remain required
1382
+ before a default. `tests/parity/trellis-shared-m.test.ts` retains the native
1383
+ gate behind `MLX_BUN_TEST_TRELLIS_MODEL`; `MLX_BUN_TRELLIS_AB_REPORT` optionally
1384
+ records the diagnostic forward timings.
1385
+
1386
+ Experimental variant 8 combines variant 7 with balanced 3-bit scatter in
1387
+ `src/model/trellis-balanced-scatter.ts`. The incumbent assigns one packed word
1388
+ per lane and leaves eight SIMD lanes idle for a 3-bit block. The candidate
1389
+ assigns eight outputs to each of all 32 lanes, loads adjacent packed words
1390
+ and aligns them once before constant-shift decoding. It preserves each
1391
+ output's row/FMA order, all split boundaries, partial reduction and final
1392
+ cast. This path requires k=3, T=256 and L<=12; other geometries retain the
1393
+ incumbent scatter. M>4 still expands. The default remains variant 6.
1394
+
1395
+ Balanced lanes alone improve the diagnostic screen; aligning words improves
1396
+ it further. Row-loop unrolling does not materially improve the aligned
1397
+ candidate, so it is not retained. The unroll screen has no repeatable-win
1398
+ verdict for k2/k4. Raw screens: `reports/qwen38-rd/balanced3-screen/`,
1399
+ `scatter-unroll-screen/` and `aligned3-screen/`. Six blocks comparing
1400
+ variants 6/7/8 at k3 and M=1/2/4 retain the down/complete-MLP gain with exact
1401
+ observed outputs (`variant8-paired/`). Native packed-model logits, live state
1402
+ and subsequent-token checks also pass M=1..5. Six full-model A/B blocks at
1403
+ M=1/2/4 retain the gain (`variant8-full-model.json`). Select this gate with
1404
+ `MLX_BUN_TRELLIS_AB_VARIANT=8`; these remain short-prefix diagnostics.
1405
+
1406
+ `MLX_BUN_TRELLIS_AB_LAST=1` selects a native append screen at
1407
+ M=1/2/3/4/5/8/9/16 with only the last vocabulary projection, closer to strict
1408
+ fill's work. The variant-7 screen preserves state/logit identity and confirms
1409
+ the M=4/5 cost discontinuity (`shared-m-append-lengths.json`). Splitting a long
1410
+ span crosses the packed-f32/expanded-bf16 paths and is a separate numerical
1411
+ experiment; the timing curve does not authorize that change.
1412
+
1413
+ A device lookup table of variant-6 f32 code values also matched the observed
1414
+ outputs but slowed the M=1 kernels for every tested bit width. That candidate
1415
+ is a measured loss on this chip/regime (`reports/qwen38-rd/lut6-*.json`). The
1416
+ M=4/5 gate-projection cost discontinuity remains; changing the crossover also
1417
+ changes the accumulation/precision path and needs separate numerical gates.
1418
+
1419
+ The smaller-table follow-up also loses on the M4 Pro. A constant-address-space
1420
+ table stores all 4,096 integer code values in signed 16-bit entries, preserving
1421
+ the reciprocal, scaling and reduction arithmetic. It matches the k3 M=1
1422
+ checks but slows both projections and complete MLP execution. Device and
1423
+ threadgroup tables store the same integers exactly in fp16, avoiding the
1424
+ double rounding of fp16 normalized code values. Both lose at M=1/4; the
1425
+ threadgroup version is the less costly choice. Those two experiments change
1426
+ gate/up decoding while retaining the aligned k3 scatter's computed decoder.
1427
+ None proceeds to full-model integration. Evidence: `trellis-constant-short-k3-m1.json`,
1428
+ `trellis-device-half-k3-m1.json`, `trellis-device-half-k3-m4.json`,
1429
+ `trellis-threadgroup-half-k3-m1.json` and `trellis-threadgroup-half-k3-m4.json`
1430
+ under `reports/qwen38-rd/`, with source manifests and every timing sample.
1431
+
1432
+ A later scatter-specific table experiment starts from the interleaved
1433
+ variant-13 artifact. Each threadgroup computes the exact 4,096 integer code
1434
+ values into an 8 KiB signed-short table; packed float multiplication and the
1435
+ original dense-expansion LUT remain unchanged. An initial 45-process screen
1436
+ across bf16/f16/f32 and M1/2/4/5/8 preserves all output hashes. Changing all
1437
+ decode sites still loses because gate/up slows. The apparent M1 scatter gain
1438
+ does not survive the narrower repeated control.
1439
+
1440
+ Restricting the table to the balanced scatter kernels completes 72 processes
1441
+ with exact separate-process output hashes, including complete MLP outputs
1442
+ and M5/8 fallback. Six alternating bf16 pairs on the M4 Pro 24 GB change
1443
+ complete-MLP time by +1.28% at M1, -0.62% at M2 and -4.12% at M4. All M1
1444
+ pairs lose and all M2/M4 pairs improve. A 20-process M3 follow-up also preserves
1445
+ every output hash; its six bf16 pairs improve the down projection by a median
1446
+ 8.02% and the complete MLP by 2.52%. The smaller fp16/fp32 screens preserve
1447
+ identity but do not establish retention. Each native/candidate pair reaches
1448
+ the same final active-allocation baseline. These are diagnostic operation
1449
+ timings, with fixed sources and all samples retained. The next full-model
1450
+ prototype targets only M3/4 for the tested k3/L12/T256 interleaved down shape.
1451
+ Ordinary M1 stays procedural. Full-model state, generation and actual serving
1452
+ acceptance remain before integration. Evidence: `trellis-threadgroup-codebook.json`,
1453
+ `trellis-threadgroup-scatter{,-review}.json` and
1454
+ `trellis-threadgroup-scatter-m3{,-review}.json` in the campaign directory.
1455
+
1456
+ The narrow full-model screen then preserves all 24 forward comparisons,
1457
+ including every vocabulary row at M1..8, live attention/recurrent state and
1458
+ the next token's logits/state. It also preserves four greedy/seeded-sampled
1459
+ generation streams and their log-probabilities, counts and final cache bytes.
1460
+ The prefix snapshot stays unchanged. Both fresh processes exit cleanly with
1461
+ fixed sources, equal scatter-call counts and the same 1,015,814-byte final
1462
+ active counter after model/cache disposal. The first pair's measured M3/4
1463
+ forwards improve by 1.6–2.8%; ordinary 64-token generation changes by less
1464
+ than 0.1%. This is an initial screen. Six balanced process pairs and actual
1465
+ MTP serving comparisons remain before retaining the prototype. Evidence:
1466
+ `trellis-scatter-full{,-review}.json`.
1467
+
1468
+ Six balanced fresh-process pairs preserve all 144 forward/logit/state and
1469
+ 24 generation comparisons, with fixed sources, identical call counts and
1470
+ the same final active allocation. Taking the two measured forward samples
1471
+ within each process before pairing, median M3/M4 forward changes are
1472
+ -1.44/-2.00%, improving every pair. Ordinary 64-token greedy/sampled complete
1473
+ times change by +0.045/-0.125%. The unchanged M128 prefill control is 2.59%
1474
+ slower in this sequence and loses all six pairs. A focused M128-only control
1475
+ tests whether that regression requires preceding M3/4 work. The initial actual MTP HTTP pair preserves all seven responses,
1476
+ 7,100 eligible scatter calls per arm and equal final active counters, but
1477
+ its complete-time changes are mixed. Evidence:
1478
+ `trellis-scatter-full-repeat{,-review}.json` and
1479
+ `trellis-scatter-http{,-review}.json`.
1480
+
1481
+ Six subsequent balanced MTP HTTP pairs preserve all 42 paired responses,
1482
+ call maps and final active allocations. Median complete-request changes are
1483
+ -1.53% for code, -1.29% for explanation and -1.04% for JSON. Code and explanation
1484
+ improve in every pair; JSON improves in four of six. TTFT changes by less than
1485
+ 0.71% and median paired sampled peak RSS increases by 2.16 MiB. These are
1486
+ M4 Pro 24 GB diagnostic results, with static swap still failing preflight.
1487
+ They support a small serving benefit in this scope, not ordinary M1 decode.
1488
+ The separate four-pair M128-only control executes no eligible M3/4 scatter
1489
+ calls. All 20 forward/state comparisons remain exact and final active memory
1490
+ is 16,390 bytes in every process. Its median paired time increases by 0.52%,
1491
+ with individual process-median differences from 0.06% to 1.01%. This does not
1492
+ reproduce the earlier 2.59% magnitude, but does not identify its cause either.
1493
+ Keep both results and inspect scheduling before integration. Evidence:
1494
+ `trellis-scatter-http-repeat{,-review}.json` and
1495
+ `trellis-scatter-prefill{,-review}.json`.
1496
+
1497
+ A four-pair sequence control adds 80 exact forward/state/continuation cases,
1498
+ with the same 16,390-byte final active counter in all eight processes. M3/M4
1499
+ forwards improve by median 1.45/1.73%, in every pair. The same-input M128
1500
+ measurement immediately before and after those calls changes by -0.92/-1.77%
1501
+ between arms; the after-versus-before difference does not show the suspected
1502
+ slowdown. The unchanged M512 control instead changes by +1.11%, losing three
1503
+ of four pairs. These controls do not reproduce the original M128 result or
1504
+ establish a prefill benefit. Keep prefill variation in the uncertainty of this
1505
+ small candidate and require integrated, combined and quiet acceptance.
1506
+ Evidence: `trellis-scatter-sequence{,-review}.json`.
1507
+
1508
+ The normal-scheduling recorder preserves the three HTTP responses and all
1509
+ scatter calls in a separate JSON pair. The measured request has 76,928
1510
+ dispatches, 3,153 command buffers with dispatches, 50,001 barriers and 3,870
1511
+ concurrent dispatches in both arms. Temporary-reference counts and bytes also
1512
+ match. Buffers without dispatches and fence waits differ slightly. Candidate
1513
+ command-span union falls by about 51 ms, while gaps between those spans grow
1514
+ by about 72 ms; instrumented complete time increases by 0.32%. Command spans
1515
+ include internal waits and do not measure active GPU utilization. This pair
1516
+ shows how scheduling gaps can cancel a small operation gain; it does not
1517
+ override the six normal-library serving pairs or prove the cause of the
1518
+ prefill difference. Evidence: `trellis-scatter-http-trace{,-review}.json`.
1519
+
1520
+ The first ordinary four-request serving pair also preserves all 12 paired
1521
+ responses, submitted-row counts, scatter-call maps and final active allocation.
1522
+ The measured 256-token cohort completes in 11.342 versus 11.778 seconds,
1523
+ 3.70% faster, with 4,050 eligible M4 scatter calls across its three cohorts.
1524
+ This is one M4 Pro 24 GB diagnostic pair, not a repeat verdict. Explicit API
1525
+ seeds require the serial lane, so this fixture uses temperature-zero argmax
1526
+ without an explicit seed. The initial seeded attempt is retained as a rejected
1527
+ batch fixture. Evidence: `trellis-scatter-cohort-v2{,-review}.json`.
1528
+
1529
+ The codebook is now part of experimental variant 13. Qwen's MLP loader enables
1530
+ it for the 5120/17408 down projection. `TrellisLinear` further requires bf16,
1531
+ k3/L12/T256, two-block interleaving and M3/4. The generic shared scatter kernel
1532
+ receives that decision as a compile-time integer. The table is initialized
1533
+ before the threadgroup's bounds return, and no new MLX allocation or graph
1534
+ boundary is introduced. Request and execution interfaces do not select it.
1535
+ The 23 focused tests and all three typechecks pass. The new test compares
1536
+ actual output bytes and dispatch eligibility across three dtypes, variants
1537
+ 6/10/13, M1/2/3/4/5/8 and an incomplete row tile. Evidence:
1538
+ `trellis-scatter-integration-source-change.json`,
1539
+ `trellis-scatter-integration-focused-tests-final.txt` and
1540
+ `trellis-scatter-integration-typecheck.txt`.
1541
+
1542
+ The integrated full-model gate preserves all 24 forward/logit/state and four
1543
+ generation comparisons, the immutable prefix, call maps and final allocation.
1544
+ M3/M4 each exercise 150 table calls in the candidate. Timing also improves in
1545
+ unchanged control shapes during an unrelated CPU workload burst; those broad
1546
+ changes cannot be attributed to this kernel. Six integrated MTP HTTP pairs
1547
+ then preserve all 42 responses and the same final active counter. Median
1548
+ complete-time changes are -1.07/-1.27/-1.19% for code/explanation/JSON, with
1549
+ 5/6, 5/6 and 4/6 pairs improving. TTFT changes remain below 0.46%. Retain every
1550
+ losing pair and the diagnostic classification. Repeated ordinary four-request
1551
+ serving, combined/pressure and quiet M4 Pro acceptance remain. Evidence:
1552
+ `trellis-scatter-integrated-full{,-review}.json` and
1553
+ `trellis-scatter-integrated-http{,-review}.json`.
1554
+
1555
+ Six integrated ordinary four-request serving pairs preserve all 72 paired
1556
+ responses, counts and finish reasons. Every cohort admits four batched rows,
1557
+ uses the eligible M4 kernel and returns to the same final active counter.
1558
+ Median measured cohort time changes by -1.49%, improving four of six pairs.
1559
+ Four measured pairs have identical per-cohort call maps; their median is also
1560
+ -1.49%, with three improving. The other two retain arrival-dependent M1/M2
1561
+ work differences. Report all pairs and the work-matched subset separately;
1562
+ do not silently discard scheduling differences or losing samples. The first
1563
+ integrated attempt preserved all responses but failed its stricter total-call
1564
+ map gate, motivating snapshots collected outside timed cohorts. This remains
1565
+ diagnostic evidence, with combined/pressure and quiet acceptance open. Evidence:
1566
+ `trellis-scatter-integrated-cohort.json` and
1567
+ `trellis-scatter-integrated-cohort-v2{,-review}.json`.
1568
+
1569
+ Sharing activation loads across multiple gate/up output rows per SIMD group
1570
+ also loses in the k3 M=1 screen. Every tested layout preserves the incumbent
1571
+ output bytes. One row per group reproduces the incumbent timing; two, four
1572
+ and eight rows increase complete-MLP time. Reducing the threadgroup to one
1573
+ or two SIMD groups does not rescue the two/four-row candidates. The original
1574
+ row mapping remains. Evidence: `trellis-shared-rows-k3-r*-m1.json` and
1575
+ `trellis-shared-rows-k3-r*-s*-m1.json` in the campaign report directory.
1576
+
1577
+ An integer arithmetic rewrite replaces the final byte-pair sum and subtraction
1578
+ with `as_type<int>(p * 65537u - (510u << 16)) >> 16`. If
1579
+ `p = a + (b << 16)`, with both byte-pair sums at most 510, this returns
1580
+ `a + b - 510` exactly. Host exhaustive-state/edge/random checks and all real
1581
+ k2/k3/k4 M=1/4 operation checks pass. The timing screen gives no useful
1582
+ additional gain, so the readable existing expression remains. Evidence:
1583
+ `trellis-y-mad-manifest.json` and `trellis-y-mad-k*-m*.json`.
1584
+
1585
+ The activation audit identifies a separate numerical distinction in the
1586
+ existing Lab path. Its fused kernel uses a float32 precise-exp sigmoid,
1587
+ whereas compiled MLX uses a dtype-specific abs/exp expression. Real k3 inputs
1588
+ at M=1/4 differ for bf16 and f32. Applying the old activation independently
1589
+ to the two projection outputs reproduces the fused result exactly, isolating
1590
+ the difference to the activation. Earlier small-input tolerance tests did
1591
+ not establish bit identity here. Existing variant-to-variant identity results
1592
+ still compare the same packed activation. Changing it requires a separately
1593
+ declared numerical/quality experiment; no default is changed by this audit.
1594
+ Evidence: `trellis-activation-audit.json`.
1595
+
1596
+ Variant 9 tests evaluation barriers separately. It keeps the variant-8
1597
+ kernels but lets the three expanded projections remain lazy until the
1598
+ existing layer-end hidden/state evaluation. The native last-head sweep
1599
+ preserves logits and live state at all observed lengths. M>4 improves while
1600
+ peak allocation grows; small M is unchanged. Evidence is in
1601
+ `reports/qwen38-rd/variant9-full-model.json`, with per-arm memory peaks.
1602
+ Larger prefill and the frozen long-agent pressure replay remain required.
1603
+ The default's per-projection barriers remain in place.
1604
+
1605
+ The variant-13 follow-up fixes the interleaved artifact and all kernels,
1606
+ then changes only the returned expanded projection's evaluation. It compares
1607
+ blocking evaluation, deferral to the existing layer boundary and asynchronous
1608
+ submission followed by that same layer boundary. All 35 warm/measured cases
1609
+ at M1/8/16/128/512 preserve logits, live state and continuation. Six balanced
1610
+ blocks exercise 64 expansion boundaries at M16 and 192 at M128/512; M1/8
1611
+ have none and are controls. Deferred evaluation reduces median paired time
1612
+ by 2.46%, 5.15% and 2.03% at M16/128/512; asynchronous evaluation reduces it
1613
+ by 2.39%, 6.56% and 2.89%. Every affected pair improves. M128/512 peak MLX
1614
+ allocation grows by about 362/380 MB for deferral and 368/403 MB for async.
1615
+ These M4 Pro 24 GB diagnostics do not remove the memory gate.
1616
+ Evidence: `reports/qwen38-rd/trellis-v13-boundary-full-model{,-review}.json`.
1617
+
1618
+ The serial HTTP follow-up completes six balanced three-arm blocks with 18
1619
+ clean server exits, fixed sources and 126 requests. All 84 paired response
1620
+ texts, usage counts and finish reasons match. At 128/512 prompt tokens,
1621
+ deferral reduces median paired TTFT by 2.88%/0.53%; asynchronous submission
1622
+ reduces it by 4.10%/1.41%, with all six pairs improving in each cell. Async
1623
+ complete-response time falls by 0.90%/0.67%. Six-token TTFT and all decode
1624
+ cells stay approximately flat. Per-server peak RSS is approximately flat;
1625
+ the separately measured MLX transient-allocation increase still applies.
1626
+ Evidence: `trellis-v13-boundary-http-serial{,-review}.json`.
1627
+
1628
+ The saved-agent pressure gate rejects unrestricted deferral on this M4.
1629
+ The synchronous control completes all seven requests, including 14,465 prompt
1630
+ tokens, 12,953 cached tokens and a 512-token response, followed by a durable
1631
+ SSD flush and clean exit. Deferral preserves the first five responses but
1632
+ fails on the sixth request at 12,954 prompt tokens. Native tracing reports
1633
+ Metal command-buffer insufficient memory and the child exits with SIGTRAP.
1634
+ Sources remain fixed. The asynchronous and mapped arms had not started;
1635
+ their outcome cannot be inferred from this failure. Keep per-projection
1636
+ barriers while testing bounded alternatives. Evidence:
1637
+ `qwen-long-boundary-serial{,-review}.json` and its deferred server trace.
1638
+
1639
+ A fresh synchronous/asynchronous pair reaches the same result. The control
1640
+ again completes all seven requests with a durable final flush and clean exit.
1641
+ Async preserves the first five responses, then fails the sixth request with
1642
+ the same Metal insufficient-memory error. Source fingerprints remain fixed.
1643
+ Thus both unrestricted alternatives fail this pressure gate despite their
1644
+ short-prompt timing gains. A further candidate must retain blocking evaluation
1645
+ when available headroom is insufficient. Evidence:
1646
+ `qwen-long-async-serial{,-review}.json` and its async server trace.
1647
+
1648
+ The next policy submits asynchronously only while MLX active allocation is
1649
+ below 75% of the device's recommended working set, retaining blocking
1650
+ evaluation above that ceiling. It still evaluates at the existing layer
1651
+ boundary. All 35 native warm/measured cases preserve logits, live cache bytes
1652
+ and continuation. The short native sweep stays below the ceiling and exercises
1653
+ asynchronous submission at every expanded projection. Median paired time
1654
+ falls 2.00%/5.40%/3.07% at M16/128/512, with six wins per cell; M1/8 controls
1655
+ are approximately flat. The short sweep does not exercise the pressure fallback.
1656
+ Evidence: `trellis-v13-bounded-full-model{,-review}.json`.
1657
+
1658
+ The bounded policy also passes a fresh serial saved-agent pair. All seven
1659
+ responses and usage counts match, both servers exit cleanly, and the final
1660
+ SSD flush reports durable state with no pending, missing or failed writes.
1661
+ The candidate submits 1,152 expanded projections asynchronously and evaluates
1662
+ 2,112 synchronously, exercising both branches. Its maximum observed active
1663
+ allocation is 17.216 GB; the 14.302 GB threshold controls scheduling, not total
1664
+ allocation. Peak sampled RSS is 13.370/13.743 GB for control/candidate.
1665
+ This single pair establishes the pressure gate, not a timing or RSS win.
1666
+ The default scheduler pressure gate remains.
1667
+ Evidence: `qwen-long-bounded-serial{,-review}.json`.
1668
+
1669
+ Six fresh serial HTTP pairs then complete 84 requests with fixed sources,
1670
+ clean exits and all 42 paired responses identical. The bounded policy reduces
1671
+ median paired TTFT by 4.42%/1.78% at 128/512 prompt tokens, with six wins in
1672
+ each cell. Complete-response time falls 0.95%/0.80%; decode speed and the
1673
+ six-token control are approximately flat. Median per-server sampled peak
1674
+ RSS is 12.364/12.362 GB for synchronous/bounded evaluation. Each candidate
1675
+ server exercises 768 asynchronous boundaries in this short sweep. These M4
1676
+ diagnostics retain the separate native transient-allocation increase and
1677
+ do not replace the default scheduler or quiet-machine gates. Evidence:
1678
+ `trellis-v13-bounded-http-serial{,-review}.json`.
1679
+
1680
+ The actual continuous-scheduler follow-up completes six pairs and 84 requests.
1681
+ All 42 paired response texts, usage counts and finish reasons match; each
1682
+ request reports `"batched"` and increments scheduler submissions. Sources
1683
+ remain fixed and all 12 servers exit cleanly. Median paired TTFT falls
1684
+ 4.46% at 128 tokens with six wins and 1.30% at 512 with five wins.
1685
+ Complete-response time falls 0.96%/0.60%. Decode and six-token controls remain
1686
+ approximately flat; median sampled peak RSS is 12.381/12.352 GB. MLX is seeded
1687
+ to 42 at startup with greedy sampling; request-level seed is omitted because
1688
+ it selects serial placement. Evidence:
1689
+ `trellis-v13-bounded-http-default{,-review}.json`.
1690
+
1691
+ The continuous saved-agent gate also passes all seven paired responses with
1692
+ fixed sources. Every request reports the batched lane and a scheduler
1693
+ submission. Both servers finish durable SSD flushes with no pending, missing
1694
+ or failed writes and exit cleanly. The candidate again uses 1,152 async and
1695
+ 2,112 blocking expansion boundaries; maximum observed active allocation is
1696
+ 17.265 GB. Sampled peak RSS is 12.842/12.992 GB for control/candidate.
1697
+ This one pair closes the prototype's continuous pressure gate, not a timing
1698
+ or memory-saving claim. Evidence: `qwen-long-bounded-default{,-review}.json`.
1699
+
1700
+ The integrated opt-in `MLX_BUN_TRELLIS_ASYNC_EXPAND` now applies that policy
1701
+ to variant 13. It reads the execution's existing runtime snapshot and caches
1702
+ only the fixed device threshold per linear layer. It preserves layer barriers
1703
+ and blocks expanded projections above the threshold. Default remains off;
1704
+ other variants retain their current behavior. Typechecks, hygiene and all
1705
+ 1,822 model-free tests pass. Integrated native, both short HTTP repeats and both
1706
+ long-agent pressure gates pass on the M4 Pro. Evidence:
1707
+ `trellis-async-integrated-{typecheck,hygiene,model-free}.txt`.
1708
+
1709
+ The integrated native sweep preserves all 35 logit/live-cache/continuation
1710
+ cases with fixed sources. An observer records the implementation's own async
1711
+ and blocking calls without substituting a scheduling decision. Median paired
1712
+ forward time falls 2.09%/6.19%/2.80% at M16/128/512, with all six pairs
1713
+ improving. M1/M8 exercise no expansion boundaries; their paired timings are
1714
+ +1.16%/-0.21%, so the M1 control variation remains visible. M128/512 peak
1715
+ MLX allocation again grows about 368/403 MB. Integrated serving and pressure
1716
+ gates remain. Evidence: `trellis-async-production-full-model{,-review}.json`.
1717
+
1718
+ The integrated serial HTTP repeat also passes all 42 paired responses across
1719
+ 12 clean server exits with fixed sources. Median paired TTFT falls 4.00% at
1720
+ 128 prompt tokens in all six pairs and 1.43% at 512 in five pairs. Request
1721
+ wall time falls 0.88%/0.63%; decode and the six-token control remain flat.
1722
+ Median sampled peak RSS is 12.331/12.355 GB for control/candidate. Each
1723
+ candidate server exercises 768 async boundaries, with no blocking fallback
1724
+ in these short prompts. These are M4 Pro diagnostics; pressure and quiet
1725
+ acceptance remain. Evidence:
1726
+ `trellis-async-production-http-serial{,-review}.json`.
1727
+
1728
+ The integrated continuous repeat passes another 42 paired responses and
1729
+ 12 clean exits with fixed sources. Every request reports the batched lane
1730
+ and exactly one scheduler submission. Median paired TTFT falls 3.96%/1.40%
1731
+ at 128/512 prompt tokens, with six/five improving pairs. Wall time falls
1732
+ 0.87%/0.63% in five/four pairs. Decode and the six-token control remain flat.
1733
+ Median sampled peak RSS is 12.353/12.349 GB. Short prompts exercise the async
1734
+ branch only; integrated long-agent pressure gates are next. Evidence:
1735
+ `trellis-async-production-http-default{,-review}.json`.
1736
+
1737
+ The integrated serial saved-agent gate preserves all seven paired responses
1738
+ and usage records with fixed sources and clean exits. Both SSD flushes finish
1739
+ durably with no pending, missing or failed snapshots and a longest durable
1740
+ prefix of 14,976 tokens. The candidate uses 1,151 async and 2,113 blocking
1741
+ boundaries; its maximum observed active allocation is 17.227 GB. Sampled peak
1742
+ RSS is 13.550/13.544 GB for control/candidate. The longest request prefills
1743
+ 14,465 tokens, reuses 12,953 and generates 512. This is a single pressure
1744
+ gate under system swap, without a performance or memory-saving claim.
1745
+ Continuous pressure acceptance remains. Evidence:
1746
+ `trellis-async-production-long-serial{,-review}.json`.
1747
+
1748
+ The integrated continuous saved-agent pair also passes all seven responses,
1749
+ usage records, source hashes and clean exits. Every request uses the batched
1750
+ lane with one scheduler submission. Both final flushes are durable, with
1751
+ 14 snapshots, a 14,976-token longest prefix and no pending, missing or failed
1752
+ writes. The candidate uses 1,152 async and 2,112 blocking boundaries; maximum
1753
+ observed active allocation is 17.253 GB. Sampled peak RSS is 12.994/12.873 GB
1754
+ for control/candidate. This completes the integrated M4 pressure gates; the
1755
+ one-pair RSS difference is not a memory-saving claim. Broader callers, combined
1756
+ optimizations and quiet M4 Pro acceptance remain. Evidence:
1757
+ `trellis-async-production-long-default{,-review}.json`.
1758
+
1759
+ Variant 10 shares scatter weight decoding and scales across M=2..4 in
1760
+ `src/model/trellis-shared-scatter.ts`. It uses aligned k3 decoding where
1761
+ eligible and the incumbent packed-word mapping for other bit widths. Every
1762
+ output retains its row/FMA order, split boundaries, sum and cast. M=1 uses
1763
+ the existing variant-8 path; M>4 keeps the projection barriers. Variant 10
1764
+ does not combine the variant-9 memory experiment.
1765
+
1766
+ Real k2/k3/k4 screens improve down and complete-MLP time with exact observed
1767
+ outputs. Unit checks cover both axes, bf16/f32 inputs, M=1..5 and the complete
1768
+ MLP. The packed 27B passes exact logits, live state and continuation checks
1769
+ at M=1..5. Six native A/B blocks against variant 8 improve M=2..4 while M=1
1770
+ is unchanged. Raw evidence is in `shared-scatter-screen/`,
1771
+ `shared-scatter-all-screen/` and `variant10-full-model.json` under
1772
+ `reports/qwen38-rd/`. The native gate accepts
1773
+ `MLX_BUN_TRELLIS_AB_BASELINE=8 MLX_BUN_TRELLIS_AB_VARIANT=10`.
1774
+
1775
+ Variant 11 combines variant 10 with tiled axis-1 prefill at M=5..32.
1776
+ `src/model/trellis-tiled-prefill.ts` reconstructs bf16 weights in threadgroup
1777
+ storage and consumes them through SIMD-group matrix operations. Its shared
1778
+ operation accepts other compatible matrix shapes; automatic experimental
1779
+ dispatch is limited to the measured 5120-to-17408 geometry, bf16, T=256,
1780
+ L=12 and k=2/3/4. Down projections keep stock expansion and matmul.
1781
+ Dense expansion and layer-end evaluation boundaries remain in place.
1782
+
1783
+ The initial computed-code prototype failed the full model despite matching
1784
+ the sampled synthetic matrices. Expansion consumes the precise host LUT,
1785
+ whereas variant-6 packed matvec uses an unrefined reciprocal. Refining that
1786
+ reciprocal before scaling reproduces expansion's bf16 weights. The corrected
1787
+ prototype passes logits, live cache state and continuation at M=1/4/5/8/16/32;
1788
+ six paired native blocks improve M=5..32 without a larger observed peak.
1789
+ Evidence: `trellis-tiled-initial-review.json`,
1790
+ `trellis-tiled-refined-gate-model.json` and its review under
1791
+ `reports/qwen38-rd/`. The integrated kernel repeats the full-model gate in
1792
+ `variant11-full-model.json`. Six process A/B blocks per serving method also
1793
+ improve first-token and complete-request latency on one six-token raw prompt,
1794
+ with identical output at eight and 64 generated tokens. Serial and continuous
1795
+ B=1 paths agree; this does not test concurrent users. Evidence:
1796
+ `trellis-v11-http.json` and its review. Broader native/HTTP workloads, pressure
1797
+ replay and quiet M4 Pro acceptance remain required. Variant 6 remains the default.
1798
+
1799
+ Variant 12 also reconstructs axis-0 weights inside split-K matrix tiles at
1800
+ M=5..8. The shared `trellis-splitk-prefill.ts` operation follows MLX 0.31.2's
1801
+ partition geometry, float partials and ordered final sum, then rounds to bf16.
1802
+ The measured dispatch profile covers 17408-to-5120 matrices at k2/k3/k4.
1803
+ It avoids the dense down-projection weight allocation. Dense expansion and
1804
+ layer evaluation boundaries remain. Larger M, wider tiles, padding and a device
1805
+ lookup table did not give a consistent advantage in the initial sweep.
1806
+
1807
+ Other-shape unit checks include transposed inputs and a longer final K
1808
+ partition. Both the prototype and integrated packed 27B preserve logits,
1809
+ live state and continuation. Six paired integrated blocks improve M=5/8;
1810
+ M=1..4/9/16/32 retain their existing path and timing. Evidence:
1811
+ `trellis-splitk-screen-review.json`, `trellis-splitk-tuning-review.json`,
1812
+ `trellis-splitk-full-model-review.json` and `variant12-full-model.json` under
1813
+ `reports/qwen38-rd/`. Combined native and HTTP results are recorded in
1814
+ decode-speed-program §7; quiet-machine acceptance remains open.
1815
+
1816
+ The calibrated operation trace led to a second tile pass. At M=5..8, both
1817
+ direct kernels now use eight output rows and place all four SIMD groups
1818
+ across columns. This removes unused row work while preserving each output's
1819
+ matrix products, accumulation order, weight rounding and split reduction.
1820
+ Contiguous packed reads with transposed threadgroup writes were slower in
1821
+ the controlled screen; neither that layout nor padding is retained.
1822
+
1823
+ Direct tiles also avoid per-projection evaluation. They keep activations and
1824
+ bounded partials without constructing a dense weight matrix, so the existing
1825
+ model layer boundary bounds their graph. Expansion paths retain their original
1826
+ projection evaluations. A four-arm full-model experiment separates tile
1827
+ geometry from synchronization: all logits, live cache bytes and continuation
1828
+ match across six blocks at M=1/4/5/8/16/32/128. The combination improves the
1829
+ eligible shapes with a small temporary-memory increase. A subsequent six-pair
1830
+ check at M=5/8 adds the smaller split-K tile with exact outputs and unchanged
1831
+ peak allocation. These changes remain within the experimental variants;
1832
+ variant 6 is unchanged. Evidence: `trellis-tile-layout-screen-review.json`,
1833
+ `trellis-tile-full-model-review.json`,
1834
+ `trellis-splitk-bm8-other-bits-review.json` and
1835
+ `trellis-splitk-bm8-full-model-review.json` under `reports/qwen38-rd/`.
1836
+ The first layout screen also changed evaluation boundaries and is excluded
1837
+ from isolated-kernel speed attribution. The integrated operation passes
1838
+ targeted units and ten old/new full-model append lengths. Six subsequent
1839
+ HTTP process pairs improve actual eight-request throughput and B=1 TTFT,
1840
+ with identical responses and verified scheduler activity. Evidence:
1841
+ `trellis-tile-integrated-forward-review.json` and `trellis-tile-http-review.json`.
1842
+ Six old/new native process pairs also preserve every emitted ID and finish
1843
+ reason, with lower first-token latency and a small complete-request gain;
1844
+ single-row decode is unchanged. All workers complete with fixed source hashes.
1845
+ Evidence: `trellis-tile-native-review.json`. These remain loaded-machine
1846
+ diagnostics, with quiet M4 Pro acceptance pending.
1847
+
1848
+ A corrected larger-prefill sweep checks ten tile geometries, packed-load
1849
+ orders and threadgroup padding choices on actual k3 gate/down matrices at
1850
+ M=128 and 512. Every one of the 40 cases reproduces the expansion-plus-matmul
1851
+ output bytes, but none beats that existing path in six AB/BA timing pairs.
1852
+ Both arms include output evaluation and GPU synchronization; the candidate
1853
+ gets no pipeline-only timing advantage. Larger tiles and coalesced packed
1854
+ loads therefore remain rejected for these measured shapes. Keep MLX's native
1855
+ matrix multiplication and investigate the separate expansion operation.
1856
+ Evidence: `trellis-large-prefill-screen.json` and
1857
+ `trellis-large-prefill-screen-review.json` in the campaign report directory.
1858
+
1859
+ A source audit also rejects an assumed fp16 throughput shortcut. In pinned
1860
+ [MLX 0.31.2 Steel GEMM](https://github.com/ml-explore/mlx/blob/v0.31.2/mlx/backend/metal/kernels/steel/gemm/kernels/steel_gemm_fused.metal),
1861
+ both half and bf16 instantiations explicitly select float accumulation.
1862
+ `BlockMMA` loads both input tiles into float matrix fragments before the
1863
+ matrix multiply. Changing storage dtype alone therefore does not select a
1864
+ half-fragment multiply in this path. This is a dispatch/arithmetic finding,
1865
+ not a measured bf16-versus-fp16 performance comparison; other native paths
1866
+ need their own inspection before making the same claim.
1867
+
1868
+ Variant 13 builds on variant 12 with `trellis-vector-expand.ts`: four adjacent
1869
+ weights share two packed-word reads and the scale. An unrolled loop extracts
1870
+ their circular windows and refines the computed code value to reproduce the
1871
+ host f32 LUT before bf16 rounding. MLX's matrix multiplication and each dense
1872
+ projection's evaluation boundary remain unchanged. The operation is independent
1873
+ of the model; dispatch requires bf16 output, k2/k3/k4, T=256 and L=12. Other
1874
+ packing/dtypes retain their existing expansion.
1875
+
1876
+ The actual-weight screen passes all 96 configurations across three bit widths
1877
+ and both projection axes. Four values per thread with 128 threads gives a
1878
+ consistent isolated improvement. Explicit wider output stores add little in
1879
+ a subsequent exact 24-case screen and are not retained. The initial full-model
1880
+ prototype preserves logits, all live cache bytes and continuation in seven
1881
+ blocks at M=1/8/16/128/512, including six measured AB/BA pairs. Eligible prefills
1882
+ improve; M=1 and M=8 retain the variant-12 path. The integrated kernel repeats
1883
+ those checks and passes the repository parity harness, whose variant-13 arm
1884
+ also exercises larger prefills. All HTTP responses match in two existing-suite
1885
+ pairs, but their timing is mostly flat; see decode-speed-program §7. Closed
1886
+ research helpers are removed after recording their results. These are M4 Pro
1887
+ diagnostics, with the
1888
+ default still variant 6. Evidence: `trellis-vector-expand-screen-review.json`,
1889
+ `trellis-vector-store-screen-review.json` and
1890
+ `trellis-vector-expand-full-model-review.json` in the campaign directory.
1891
+
1892
+ The R6 layout screen rearranges groups of coded blocks across input rows,
1893
+ preserving the codes, payload size, arithmetic and split-K reduction order.
1894
+ All six actual k3 down-projection layouts invert to the original code bytes
1895
+ and produce identical output bytes. Two groupings improve the isolated
1896
+ operation; the original-order control is flat. Both full-model groupings
1897
+ preserve every eligible matrix's code bytes, logits, live cache state and
1898
+ continuation, with a small single-token forward improvement. Group two is
1899
+ simpler and group ten offers no additional model gain. The prototype
1900
+ retains both layouts to alternate A/B arms, so its additional residency and
1901
+ packing cost are recorded separately. Six subsequent production-generation
1902
+ pairs with a fixed seed and 64 outputs preserve every emitted ID, final live
1903
+ cache byte and continuation logit. The small gain survives the complete
1904
+ generation loop. Six fresh-server pairs in each of serial and default
1905
+ scheduling then preserve all 60 paired responses. Both prompt lengths retain
1906
+ the small decode and complete-request improvement; the default scheduler's
1907
+ longer-prompt TTFT is approximately flat. All servers exit cleanly, and
1908
+ telemetry proves the configured execution path. Both arms retain the extra
1909
+ code copy, so their flat RSS comparison is not a memory improvement. A stored
1910
+ format usable by every prefill, scatter and expansion reader remains open.
1911
+ No artifact or production dispatch is changed. Evidence:
1912
+ `trellis-block-interleave-screen-review.json` and
1913
+ `trellis-block-interleave-full-model-{g2,g10}-review.json`, plus
1914
+ `trellis-block-interleave-native-generation-g2-interface-review.json` in the
1915
+ campaign directory, plus `trellis-block-interleave-http-{serial,default}-review.json`.
1916
+ The first generation driver used the wrong iterator
1917
+ interface and failed before timing; its failed report remains separate.
1918
+
1919
+ The replacement-layout reader prototype now preserves all 27 actual-matrix
1920
+ projection cases across bf16/f16/f32 and three dense expansions. It covers
1921
+ single-token and shared M2..4 scatter, M5..8 split-K and larger dense prefills,
1922
+ through M512. The operation timings show small-batch gains with marginal
1923
+ prefill regressions. The first full-model reader check preserves 63 cases,
1924
+ but its wrapper changes input disposal and output evaluation order. A repeat
1925
+ with the incumbent order preserves all 35 cases and retains the single-token
1926
+ gain; M8/16 are approximately flat, while M128/512 are slightly slower.
1927
+ Changing expansion threadgroup traversal preserves all five operation cases;
1928
+ moderate row tiles improve that reader slightly, while fully prioritizing
1929
+ contiguous code reads loses. The revised traversal preserves all 35 full-model
1930
+ cases, retains the small single-token gain and has approximately flat prefills
1931
+ in that repeat. The next gate uses a separate stored artifact through serving,
1932
+ with every reader consuming one resident code copy. Evidence:
1933
+ `trellis-interleaved-readers-screen-review.json`,
1934
+ `trellis-interleaved-readers-full-model-count-review.json`,
1935
+ `trellis-interleaved-readers-order-full-model-review.json`,
1936
+ `trellis-interleaved-vector-schedule-screen-review.json` and
1937
+ `trellis-interleaved-readers-schedule-full-model-review.json`. Those prototype
1938
+ measurements keep both code copies resident; the stored-artifact gate below
1939
+ removes that extra allocation.
1940
+
1941
+ The integrated reader accepts an optional U32 code shape
1942
+ `[coded_columns / 512, stored_rows, 48]` for k3, T256, L12, 1MAD, axis0.
1943
+ Each 48-word row holds two original coded blocks; block groups precede rows.
1944
+ Scales retain their original one-value-per-stored-row representation. The
1945
+ existing 2D format retains its addressing. Shape validation rejects other
1946
+ 3D geometries, and every scatter/expansion reader selects its address formula
1947
+ at compile time. Variant 13 also uses the measured moderate row traversal
1948
+ when rows divide its tile size; other shapes retain the original traversal.
1949
+ No request, scheduler or state interface changes. Focused tests preserve all
1950
+ 14 variants, bf16/f16/f32 outputs, strided inputs and expansion, with explicit
1951
+ split-K partition-tail and malformed-format checks. Broader artifact and
1952
+ serving acceptance remain open; this is not a default artifact change. All three
1953
+ typechecks, hygiene and the complete model-free tier pass. A separately
1954
+ written artifact verifies all converted code inverses and every unchanged
1955
+ tensor payload. Native and HTTP comparisons load one artifact per process,
1956
+ without runtime repacking or duplicate codes. Closed layout prototype helpers
1957
+ are removed; their raw reports remain. The first native artifact driver fails
1958
+ before spawning a child because of an unsupported stdout option; its corrected
1959
+ file-descriptor version has a separate report. Evidence:
1960
+ `trellis-interleave-integrated-tests.txt`,
1961
+ `trellis-interleave-integrated-tail-tests.txt`,
1962
+ `trellis-interleave-model-free.txt` and `trellis-interleave-artifact-review.json`.
1963
+ The converter's header padding does not determine the kernel buffer's
1964
+ alignment. `Weights` uses native MLX safetensors loading, whose pinned
1965
+ `Load::eval_cpu` allocates its output buffer and reads the payload into it.
1966
+ The paired kernels therefore consume MLX-owned buffers in both artifacts;
1967
+ they do not access the file mapping at the tensor's raw header offset.
1968
+ The packed quantizer's optional `--interleave-codes` switch produces this
1969
+ layout for eligible tensors, including row-major tensors read through
1970
+ `--reuse`. Other tensors retain their layout. The shared codec helper only
1971
+ reorders words; the quantizer records the number of interleaved modules.
1972
+ Six fresh-process native artifact pairs now preserve all warm/measured
1973
+ forward logits, live states, continuations and generation IDs. Single-token
1974
+ and M4 forwards improve in every pair; M8 is slightly slower, M16 improves,
1975
+ and longer prefills are mixed. Complete 64-token generation improves in every
1976
+ pair with equal peak MLX allocation. One slower M512 sample remains included.
1977
+ This removes the prototype's duplicate-code residency from the comparison;
1978
+ HTTP RSS and timing have their own gate below. Evidence:
1979
+ `trellis-interleave-artifact-native-fd-review.json` and
1980
+ `trellis-interleave-encoder-tests.txt`. Native research helpers are removed
1981
+ after recording the finding; the artifact converter is superseded by the
1982
+ quantizer's layout option and the retained codec helper.
1983
+
1984
+ Six fresh-server serial HTTP artifact pairs preserve all 42 paired responses,
1985
+ including warmups, usage, finish reasons and stream completion. Each process
1986
+ loads only its selected artifact, uses production readers at variant 13, and
1987
+ exits cleanly. Source hashes remain fixed. Decode improves in every measured
1988
+ pair at each actual prompt length of 6, 128 and 512 tokens; TTFT changes are
1989
+ small or mixed. RSS is approximately flat, with no retained duplicate code
1990
+ buffer. These M4 Pro diagnostics use cache-disabled requests and do not
1991
+ establish cached, pressure or quiet-machine acceptance. Six further pairs
1992
+ under the default continuous scheduler preserve another 42 paired responses
1993
+ with clean exits and fixed sources. Telemetry confirms the configured batch
1994
+ capacity of eight, one submitted row per request and no remaining active or
1995
+ pending rows. These are single-prompt measurements. Decode improves in every
1996
+ pair at all three lengths; TTFT changes remain small or mixed, and RSS is
1997
+ approximately flat. Evidence:
1998
+ `trellis-interleave-artifact-http-{serial,default}-review.json`.
1999
+ The completed HTTP research helper is removed; raw responses, telemetry,
2000
+ source hashes and server logs remain in the reports.
2001
+
2002
+ A separate M<=8 crossover prototype improves complete-MLP time at M=5/8
2003
+ but changes output bytes for every tested bit width. It replaces expanded
2004
+ bf16 matrix arithmetic with packed f32 code-times-scale arithmetic, so this
2005
+ is a Lab precision change, not an exact optimization. The prototype is not
2006
+ retained in production dispatch. `reports/qwen38-rd/m8-screen/` records the
2007
+ timings and numerical differences. Quality and long-state checks must precede
2008
+ any use of the larger crossover or equivalent small-chunk span splitting.
2009
+
2010
+ # Open items (weights leg — mirrors the PLAN.md phase boxes; PLAN.md owns status)
2011
+
2012
+ - **W5 calibration composition** — PLAN sub-boxes W5a (0.8B matrix), W5b
2013
+ (allocation axis, pick the best ≤4.5 bpw recipe), W5c (chunked 27B
2014
+ production run + gates) remain unchecked in PLAN.md although the results
2015
+ above were measured; closing them is a PLAN.md edit, not new work.
2016
+ Deferred lever inside W5: learned R (SpinQuant Cayley-SGD) — only if a
2017
+ random-R recipe wins first.
2018
+ - **W6 release — SINGLE REPO:** one artifact = quantized trunk + bf16 vision
2019
+ (in-main + optiq sidecar) + folded MTP companion at `mtp/`. Engine landed
2020
+ (`--draft-kind mtp` bundled resolution). Publish the winner via
2021
+ `mlx-bun upload`; awaiting Josh's go. Card notes owed: M1-vs-M4 3-bit
2022
+ speed guidance; MTP not claimed lossless; adapters trained on the artifact
2023
+ are rotation-basis-married to it.
2024
+ - **W6.5 dogfood gate (publish blocker, ahead of W6 upload):** open
2025
+ sub-items — `mlx-bun perplexity` on qwen3_5 (SSMCache.advance stub);
2026
+ dogfood close-out (re-score a ~30-question GPQA subset through mlx-bun and
2027
+ match the mlx-lm scores); 24 GB near-ceiling UX (detect weightsBytes ≈
2028
+ default iogpu wired limit and PRINT the sysctl advice up front instead of
2029
+ dying mid-request — clamp/advise, never refuse).
2030
+ - **W7 Pareto frontier (Josh's frame: "we shouldn't operate in a vacuum"):**
2031
+ (intelligence × tok/s × memory) for local models on consumer Macs at the
2032
+ 24 GB and 32 GB budgets, defended against NON-OURS points: ours
2033
+ {GPTQ-4bit, TQ-mixed} × mlx-bun; published {OptiQ-4bit, mlx-community
2034
+ RTN-4bit} × {mlx-bun, mlx-lm}; cross-ecosystem anchor {GGUF Q4_K_M ×
2035
+ llama.cpp/Ollama}. Still owed: quiet-box tps per arm (labeled
2036
+ host/chip/RAM), peak footprint + max-context-that-fits per budget, the
2037
+ GGUF anchor. The KV axis composes: `--kv-quant turbo` (k8v3) is the
2038
+ context-headroom lever at fixed weight bpw. `scripts/turboquant/
2039
+ farm-setup.sh` provisions a rented Apple-silicon worker for these runs.
2040
+ - **Q campaign (sub-4 bpw):** Q3 passed its gate (finding 5); Q2b packed
2041
+ format landed and measured (12.1 GiB, KL 0.1550 through the engine, 9.3
2042
+ tok/s vs 18.9 on M1 Max — "Q2b measured"); the down_proj axis is settled the
2043
+ OTHER way (input-dim coding buys +22% decode but reintroduces the raw-GSM
2044
+ EOS cliff, 29/50 vs 44/50 strictly nested — the coded axis must be the
2045
+ rotated one). Owed: a 2.75-budget arm
2046
+ for the size axis; task columns on q2a/q2b for the ladder's completeness;
2047
+ root-cause the rawGSM EOS cliff on the unrotated affine arm. Source bf16,
2048
+ all Q artifacts, Hessians live on the external `/Volumes/MLX-Models` volume.
2049
+ - **Engine follow-up from the M4 rows:** serve should scale
2050
+ prefillChunkSize from fit.ts headroom automatically; prefill-rate
2051
+ oracle comparison vs mlx-lm same-box.
2052
+ - **KV-leg projector:** `KvScheme.bytesAt` bills turbo as bf16; a packed-
2053
+ layout projector would let admission advertise the real window.
2054
+ - **Queued post-campaign (GPU owned by certification until then):** DSpark×27B
2055
+ Track A (confidence-scheduled verification on the stock MTP head) and
2056
+ Track B (multi-token drafter on the TQ trunk; gate: beat MTP's 0.68
2057
+ generalizing acceptance or drop); ORPO LoRA on the TQ 27B (QLoRA shape:
2058
+ frozen 17 GB base + bf16 adapters; first step a one-layer-backward memory
2059
+ probe). Designs live in docs/design/speculative-decoding.md §"27B program".
2060
+
2061
+ ## Invariants (both legs)
2062
+
2063
+ - Rotation never enters the runtime graph for weights; for KV it enters only
2064
+ inside the cache class (dequantize-on-fetch or deferred un-rotation at the
2065
+ attention site). L1 attention math stays unmodified.
2066
+ - Quantization groups run along head_dim (KV) — token-axis surgery stays
2067
+ byte-safe.
2068
+ - Oracle-backed at the codec level (KV: vllm-metal goldens; weights: fold
2069
+ parity vs stock mlx-lm on the folded bf16 model). Quality of a recipe is
2070
+ a measured curve with host/chip/RAM labels, never inferred from ppl alone
2071
+ — every finalist needs generative tasks (templated + raw GSM8K).
2072
+ - Same seed for a trunk and its MTP companion; provenance persisted in
2073
+ `optiq_metadata.json`.
2074
+ - Comparisons are per-bpw-band and paired (same corpus, same items, same
2075
+ scorer); no perf claims off a loaded box.
2076
+
2077
+ ## History
2078
+
2079
+ - 2026-06-12 — TurboQuant KV promoted from research path to Phase 13.
2080
+ - 2026-07-06 — KV v1 landed end-to-end (codec, `TurboQuantKVCache`,
2081
+ `--kv-quant turbo`, kv-store persistence, goldens vs vllm-metal); Gate 3
2082
+ curve passed on MiniCPM5-1B; deferred inverse FWHT landed same day; the
2083
+ weights leg noted as "Future".
2084
+ - 2026-07-07 — review fixes: spec-lane exclusion (turbo was silently routed
2085
+ into speculation), head-dim refusal at server start.
2086
+ - 2026-08-17 — weights phase opened; W0 Llama fold proof (`rotate.ts`).
2087
+ - 2026-08-18 — W1 corridor map, W2 streaming fold/quantize at 27B, W3/W4
2088
+ curves + TQ-mixed artifact, W5a corrected-GPTQ matrix, W5c v3 27B run,
2089
+ FINAL BOARD (v3 staged as mjriii/Qwen3.8-27B).
2090
+ - 2026-08-19 — territory campaign + THE FRONTIER; W6.5 dogfood gate opened.
2091
+ - 2026-08-20 — `WeightTransform` seam promoted into production convert
2092
+ (`--rotate-weights`, `optiq_metadata.json` provenance); MTP serve lane
2093
+ fixed; DeltaNet prefill leak fixed; M4 Pro 24 GB speed rows.
2094
+ - 2026-08-23 — research scripts moved to `scripts/turboquant/`; this
2095
+ consolidated doc.
2096
+ - 2026-08-31 — Q0 KL-vs-bf16-teacher instrument (both stacks); transformers-5.8
2097
+ checkpoint loader.
2098
+ - 2026-09-01 — Q2 trellis codec + driver, rotation 2×2, Q2c LDLQ, Q3 k-map;
2099
+ KL ladder above.
2100
+ - 2026-09-02 — Q3 full recipe (rot + LDLQ + k-map 3.00) PASSED: KL 0.1553 at
2101
+ 3.55 bpw, MMLU 88 / tGSM 48 / rawGSM 44; eval-carrier repack tool; LDLQ
2102
+ loop-shadow bug fixed.