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