mlx-bun 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/README.md +71 -588
  2. package/docs/README.md +91 -0
  3. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  4. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  5. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  6. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  7. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  8. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  9. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  10. package/docs/design/batching.md +569 -0
  11. package/docs/design/decode-speed-program.md +3178 -169
  12. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  13. package/docs/design/generic-model-support.md +579 -334
  14. package/docs/design/kv-cache.md +680 -0
  15. package/docs/design/orpo-training.md +673 -1239
  16. package/docs/design/speculative-decoding.md +1627 -0
  17. package/docs/design/turboquant.md +2102 -0
  18. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  19. package/docs/design/web-chat-redesign.md +666 -1083
  20. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  21. package/docs/planning/release-notes-v0.3.0.md +35 -0
  22. package/docs/planning/video-series.md +2 -2
  23. package/docs/reference/benchmarks.md +1162 -0
  24. package/docs/reference/cli.md +394 -159
  25. package/docs/reference/distribution.md +321 -182
  26. package/docs/reference/environment.md +348 -0
  27. package/docs/reference/glossary.md +39 -0
  28. package/docs/reference/library-api.md +566 -168
  29. package/docs/reference/memory.md +227 -64
  30. package/docs/reference/models.md +104 -6
  31. package/docs/reference/server-api.md +886 -425
  32. package/docs/reference/server-config.md +638 -330
  33. package/docs/reference/training.md +393 -254
  34. package/docs/reference/troubleshooting.md +166 -0
  35. package/package.json +9 -4
  36. package/src/audio/decode.ts +2 -2
  37. package/src/audio/features.ts +2 -2
  38. package/src/backends/mlx/autoregressive.ts +108 -0
  39. package/src/backends/mlx/batch-group.ts +1646 -0
  40. package/src/backends/mlx/diffusion.ts +39 -0
  41. package/src/backends/mlx/draft-target.ts +47 -0
  42. package/src/backends/mlx/evaluation.ts +105 -0
  43. package/src/backends/mlx/fill-append.ts +45 -0
  44. package/src/backends/mlx/gateway-binding.ts +71 -0
  45. package/src/backends/mlx/graph.ts +38 -0
  46. package/src/backends/mlx/memory-client.ts +189 -0
  47. package/src/backends/mlx/memory-guard.ts +40 -0
  48. package/src/backends/mlx/methods.ts +90 -0
  49. package/src/backends/mlx/model-prompt.ts +164 -0
  50. package/src/backends/mlx/model-serving.ts +79 -0
  51. package/src/backends/mlx/prefill.ts +33 -0
  52. package/src/backends/mlx/request-policy.ts +20 -0
  53. package/src/backends/mlx/rollback.ts +51 -0
  54. package/src/backends/mlx/serial-executor.ts +273 -0
  55. package/src/backends/mlx/speculative.ts +114 -0
  56. package/src/backends/mlx/state-views.ts +26 -0
  57. package/src/backends/mlx/text-engine.ts +73 -0
  58. package/src/cli.ts +58 -25
  59. package/src/client.ts +66 -0
  60. package/src/config.ts +21 -4
  61. package/src/contracts/completion.ts +16 -0
  62. package/src/contracts/execution.ts +49 -0
  63. package/src/contracts/generation.ts +103 -0
  64. package/src/contracts/host.ts +7 -0
  65. package/src/contracts/jobs.ts +66 -0
  66. package/src/contracts/pi-provider.ts +30 -0
  67. package/src/contracts/pi.ts +259 -0
  68. package/src/contracts/resources.ts +18 -0
  69. package/src/contracts/scheduling.ts +27 -0
  70. package/src/contracts/tsconfig.json +14 -0
  71. package/src/diffusion/diffusion-generate.ts +218 -153
  72. package/src/embed.ts +1 -1
  73. package/src/engine/admission.ts +59 -0
  74. package/src/engine/cancellation.ts +38 -0
  75. package/src/engine/engine.ts +56 -0
  76. package/src/engine/execution-coordinator.ts +70 -0
  77. package/src/engine/execution-plan.ts +51 -0
  78. package/src/engine/resources.ts +42 -0
  79. package/src/engine/scheduler.ts +67 -0
  80. package/src/engine/session.ts +247 -0
  81. package/src/eval/README.md +44 -1
  82. package/src/eval/kl.ts +236 -3
  83. package/src/eval/reference-logits.ts +207 -0
  84. package/src/eval/runner.ts +8 -96
  85. package/src/fill/echo-index.ts +188 -0
  86. package/src/fill/fill-session.ts +452 -0
  87. package/src/fill/proposal.ts +68 -0
  88. package/src/fill/schema-rows.ts +506 -0
  89. package/src/fill/tool-boundary.ts +103 -0
  90. package/src/fit.ts +1 -1
  91. package/src/generate.ts +656 -268
  92. package/src/grammar.ts +12 -7
  93. package/src/harness-pi.ts +14 -33
  94. package/src/hub-rest.ts +4 -4
  95. package/src/index.ts +7 -0
  96. package/src/inference/denoising.ts +17 -0
  97. package/src/inference/graph.ts +33 -0
  98. package/src/inference/prefill.ts +39 -0
  99. package/src/inference/rollback.ts +7 -0
  100. package/src/jobs/db.ts +4 -0
  101. package/src/jobs/index.ts +2 -0
  102. package/src/jobs/job-entry.ts +12 -12
  103. package/src/jobs/runner.ts +98 -13
  104. package/src/jobs/task-client.ts +19 -0
  105. package/src/jobs/types.ts +2 -58
  106. package/src/kv-store.ts +109 -49
  107. package/src/lab/README.md +18 -0
  108. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  109. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  110. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
  111. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  112. package/src/library.ts +40 -0
  113. package/src/lora.ts +18 -0
  114. package/src/media-fetch.ts +1 -1
  115. package/src/memory/article.ts +1 -1
  116. package/src/memory/chunk.ts +1 -1
  117. package/src/memory/model.ts +30 -169
  118. package/src/memory/prompts.ts +1 -1
  119. package/src/memory/rest.ts +1 -1
  120. package/src/memory/schedule.ts +1 -1
  121. package/src/memory/stages.ts +4 -1
  122. package/src/memory/vault.ts +1 -3
  123. package/src/mlx/array.ts +9 -10
  124. package/src/mlx/compile.ts +12 -2
  125. package/src/mlx/ffi.ts +58 -11
  126. package/src/mlx/materialize.ts +23 -0
  127. package/src/mlx/metal-kernel.ts +10 -11
  128. package/src/mlx/ops.ts +33 -13
  129. package/src/mlx/quantized-rows.ts +36 -0
  130. package/src/mlx/token-bitmask.ts +52 -0
  131. package/src/mlx/turboquant-kv-decode.ts +82 -0
  132. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  133. package/src/mlx/turboquant-ops.ts +33 -3
  134. package/src/mlx/turboquant-tables.ts +2 -2
  135. package/src/model/batched-mask.ts +8 -2
  136. package/src/model/batched-quant.ts +1 -1
  137. package/src/model/batched-rotating-quant.ts +2 -2
  138. package/src/model/batched-rotating.ts +1 -1
  139. package/src/model/compiled-decode.ts +1 -1
  140. package/src/model/diffusion-gemma.ts +8 -8
  141. package/src/model/factory.ts +76 -33
  142. package/src/model/fingerprint.ts +1 -1
  143. package/src/model/flash-attention.ts +3 -3
  144. package/src/model/gemma4-base.ts +26 -9
  145. package/src/model/gemma4.ts +6 -6
  146. package/src/model/generated/gemma4-12b.ts +2 -2
  147. package/src/model/generated/gemma4-26b.ts +2 -2
  148. package/src/model/generated/gemma4-e4b.ts +2 -2
  149. package/src/model/glm52-dsa.ts +13 -9
  150. package/src/model/glm52-memory.ts +1 -1
  151. package/src/model/implementation.ts +69 -0
  152. package/src/model/minicpm5.ts +6 -8
  153. package/src/model/profile.ts +12 -2
  154. package/src/model/qwen-append.ts +12 -0
  155. package/src/model/qwen-conv.ts +52 -0
  156. package/src/model/qwen3_5-checkpoint.ts +164 -0
  157. package/src/model/qwen3_5.ts +140 -56
  158. package/src/model/support.ts +1 -1
  159. package/src/model/trellis-balanced-scatter.ts +51 -0
  160. package/src/model/trellis-linear.ts +672 -0
  161. package/src/model/trellis-shared-m.ts +151 -0
  162. package/src/model/trellis-shared-scatter.ts +144 -0
  163. package/src/model/trellis-splitk-prefill.ts +158 -0
  164. package/src/model/trellis-tiled-prefill.ts +136 -0
  165. package/src/model/trellis-vector-expand.ts +62 -0
  166. package/src/model/trellis-wide-prefill.ts +94 -0
  167. package/src/model/universal/dense.ts +1 -1
  168. package/src/model/universal/modules.ts +8 -1
  169. package/src/native-pack.ts +6 -5
  170. package/src/pi-provider.ts +6 -64
  171. package/src/pi-web.ts +8 -315
  172. package/src/prompt-cache.ts +96 -53
  173. package/src/quantize/allocator.ts +1 -1
  174. package/src/quantize/config-writer.ts +10 -3
  175. package/src/quantize/rotate.ts +4 -4
  176. package/src/quantize/trellis.ts +586 -0
  177. package/src/responses.ts +28 -1
  178. package/src/runtime-config.ts +18 -4
  179. package/src/sampler.ts +4 -4
  180. package/src/serve/batch-scheduler.ts +8 -1671
  181. package/src/serve/chat-request.ts +391 -0
  182. package/src/serve/chat-stage.ts +195 -0
  183. package/src/serve/checkpoint-identity.ts +64 -0
  184. package/src/serve/completion-executor.ts +15 -7
  185. package/src/serve/discovery-routes.ts +12 -13
  186. package/src/serve/generation-gateway.ts +154 -193
  187. package/src/serve/http.ts +159 -0
  188. package/src/serve/inference-request.ts +79 -0
  189. package/src/serve/isolate.ts +297 -105
  190. package/src/serve/lab-routes.ts +3 -0
  191. package/src/serve/lane-registry.ts +2 -1
  192. package/src/serve/model-admin-routes.ts +7 -5
  193. package/src/serve/model-binding.ts +43 -0
  194. package/src/serve/model-host.ts +512 -0
  195. package/src/serve/openai-wire.ts +103 -0
  196. package/src/serve/parent-application.ts +56 -0
  197. package/src/serve/pipeline.ts +33 -0
  198. package/src/serve/preparation.ts +33 -0
  199. package/src/serve/request-plan.ts +19 -15
  200. package/src/serve/request-prep.ts +346 -0
  201. package/src/serve/response-lease.ts +24 -0
  202. package/src/serve/responses-client.ts +69 -0
  203. package/src/serve/session-completion-engine.ts +110 -0
  204. package/src/serve/session-search.ts +1 -1
  205. package/src/serve/static-routes.ts +5 -5
  206. package/src/serve/text-completion-stage.ts +104 -0
  207. package/src/serve/token-streams.ts +519 -0
  208. package/src/server.ts +317 -2649
  209. package/src/spec/assistant-source.ts +10 -44
  210. package/src/spec/deepspec-source.ts +6 -6
  211. package/src/spec/dflash-source.ts +8 -8
  212. package/src/spec/dspark/ab-stats.ts +3 -3
  213. package/src/spec/dspark/calibration.ts +2 -2
  214. package/src/spec/dspark/deepspec-module.ts +2 -2
  215. package/src/spec/dspark/generate-dflash.ts +2 -2
  216. package/src/spec/dspark/generate.ts +2 -2
  217. package/src/spec/dspark/loader.ts +1 -1
  218. package/src/spec/dspark/module-dflash.ts +4 -3
  219. package/src/spec/dspark/module.ts +3 -3
  220. package/src/spec/dspark/quantize-drafter.ts +1 -1
  221. package/src/spec/generate.ts +1 -1
  222. package/src/spec/glm52-mtp-source.ts +1 -1
  223. package/src/spec/ngram-source.ts +1 -1
  224. package/src/spec/prefix-state.ts +45 -0
  225. package/src/spec/qwen-mtp-source.ts +166 -69
  226. package/src/spec/serve-loop.ts +154 -156
  227. package/src/spec/source.ts +42 -10
  228. package/src/spec/two-model.ts +1 -1
  229. package/src/ssd-cache.ts +80 -12
  230. package/src/ssd-durability.ts +25 -13
  231. package/src/tool-call.ts +51 -14
  232. package/src/train/flash-cce.ts +2 -2
  233. package/src/train/forward.ts +6 -0
  234. package/src/train/loss.ts +1 -1
  235. package/src/train/prefix-shared.ts +5 -1
  236. package/src/train/segmented.ts +5 -3
  237. package/src/train/trainer.ts +6 -6
  238. package/src/vision/prompt.ts +1 -1
  239. package/src/vision/qwen3vl-tower.ts +52 -10
  240. package/src/web/app.html +1 -1
  241. package/src/web/app.js +5 -3
  242. package/src/web/src/adapters-panel.ts +3 -3
  243. package/src/web/src/assistant.ts +2 -36
  244. package/src/web/src/chat.ts +4 -4
  245. package/src/web/src/composer.ts +4 -4
  246. package/src/web/src/hub.ts +1 -1
  247. package/src/web/src/markdown.ts +0 -0
  248. package/src/web/src/memory-panel.ts +1 -1
  249. package/src/web/src/model-picker.ts +1 -1
  250. package/src/web/src/palette.ts +1 -1
  251. package/src/web/src/protocol.ts +3 -39
  252. package/src/web/src/sessions.ts +1 -1
  253. package/src/web/src/ui-catalog.ts +4 -18
  254. package/src/web-assets.ts +47 -0
  255. package/src/weights.ts +55 -9
  256. package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
  257. package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
  258. package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
  259. package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
  260. package/docs/archive/investigations/hlg-ab.html +0 -34
  261. package/docs/archive/investigations/hlg-explorer.html +0 -238
  262. package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
  263. package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
  264. package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
  265. package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
  266. package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
  267. package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
  268. package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
  269. package/docs/archive/investigations/hlg-report.html +0 -262
  270. package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
  271. package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  272. package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
  273. package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
  274. package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
  275. package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
  276. package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
  277. package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
  278. package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
  279. package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
  280. package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  281. package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
  282. package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
  283. package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
  284. package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  285. package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  286. package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  287. package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  288. package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  289. package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  290. package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  291. package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
  292. package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
  293. package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
  294. package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
  295. package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
  296. package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  297. package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
  298. package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
  299. package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
  300. package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
  301. package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
  302. package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  303. package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
  304. package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  305. package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  306. package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  307. package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  308. package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  309. package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  310. package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
  311. package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  312. package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
  313. package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  314. package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  315. package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  316. package/docs/archive/mlx-bun-lab-report.html +0 -275
  317. package/docs/archive/wwdc-mlx-bun.html +0 -848
  318. package/docs/design/adapters-end-to-end.md +0 -205
  319. package/docs/design/batching-perf-path.md +0 -255
  320. package/docs/design/batching-v2-plan.md +0 -529
  321. package/docs/design/bucketing-stage.md +0 -197
  322. package/docs/design/compat-cli-surface-design.md +0 -230
  323. package/docs/design/docs-reorg-plan.md +0 -165
  324. package/docs/design/dspark-serving-program.md +0 -371
  325. package/docs/design/dspark-speculative-decoding.md +0 -318
  326. package/docs/design/grammar-spec-batching-integration.md +0 -301
  327. package/docs/design/memory-inference-path.md +0 -422
  328. package/docs/design/memory-synthesis.md +0 -287
  329. package/docs/design/memory-system.md +0 -237
  330. package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
  331. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  332. package/docs/design/omlx-adoption-map.md +0 -98
  333. package/docs/design/orpo-dynamic-lambda.md +0 -220
  334. package/docs/design/orpo-future-enhancements.md +0 -340
  335. package/docs/design/paged-kv-cache.md +0 -214
  336. package/docs/design/parallel-slots.md +0 -567
  337. package/docs/design/parity-tier-dag.md +0 -161
  338. package/docs/design/repo-cleanup-plan.md +0 -231
  339. package/docs/design/runtime-isolation.md +0 -263
  340. package/docs/design/segmented-backward-training.md +0 -543
  341. package/docs/design/spec-decode-larger-targets.md +0 -229
  342. package/docs/design/ssd-kv-cold-tier.md +0 -415
  343. package/docs/design/structured-output.md +0 -484
  344. package/docs/design/tauri-desktop-app.md +0 -127
  345. package/docs/design/the-dreaming-master-plan.md +0 -445
  346. package/docs/design/turboquant-kv.md +0 -235
  347. package/docs/design/turboquant-weights.md +0 -467
  348. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  349. package/docs/design/web-chat-beat-matrix.md +0 -315
  350. package/docs/design/write-pipeline-entity.md +0 -468
  351. package/docs/planning/IDEAS.md +0 -69
  352. package/docs/reference/embedding.md +0 -197
  353. package/docs/reference/features-matrix.md +0 -101
  354. package/docs/reference/orpo-quickstart.md +0 -135
  355. package/src/flags.ts +0 -11
  356. /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
  357. /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
  358. /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
  359. /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
  360. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  361. /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
  362. /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
  363. /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
  364. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -12,9 +12,11 @@
12
12
  import type { ModelConfig } from "../config";
13
13
  import type { Weights } from "../weights";
14
14
  import { MlxArray } from "../mlx/array";
15
- import { Dtype } from "../mlx/ffi";
15
+ import { Dtype, deviceArchitecture } from "../mlx/ffi";
16
16
  import * as ops from "../mlx/ops";
17
17
  import { CompiledFunction } from "../mlx/compile";
18
+ import { qwenAppendChunkSize } from "./qwen-append";
19
+ import { TrellisLinear, fusedGateUpEligible, fusedGateUpSwiglu, TRELLIS_MATVEC_MAX_M } from "./trellis-linear";
18
20
  import {
19
21
  argmaxLastPosition,
20
22
  disposeTriple,
@@ -29,7 +31,10 @@ import {
29
31
  type Cache,
30
32
  type Mask,
31
33
  } from "./gemma4-base";
34
+ import { qwen35WeightsView } from "./qwen3_5-checkpoint";
35
+ import { materializeCopy } from "../mlx/materialize";
32
36
  import { gatedDeltaUpdate, SSMCache } from "./qwen3-delta";
37
+ import type { QwenConvolution } from "./qwen-conv";
33
38
  import {
34
39
  activeMrope, applyInterleavedRope, buildMropePositions, mropeInvFreq,
35
40
  setActiveMrope, type MropeRequestState,
@@ -107,6 +112,10 @@ export function compiledSilu(x: MlxArray): MlxArray {
107
112
 
108
113
  /** Gated-DeltaNet linear-attention layer (mlx-lm GatedDeltaNet). */
109
114
  export class GatedDeltaNet {
115
+ /** Per-model experiment seam; null retains the oracle graph. The same
116
+ * implementation advances speculative rollback prefixes. Borrowed inputs,
117
+ * owned activation and independent state tail; no global runtime mutation. */
118
+ convolution: QwenConvolution | null = null;
110
119
  readonly inProjQkv: QuantizedLinear;
111
120
  readonly inProjZ: QuantizedLinear;
112
121
  readonly inProjB: QuantizedLinear;
@@ -146,7 +155,24 @@ export class GatedDeltaNet {
146
155
  this.normWeight = weights.tensor(`${prefix}.norm.weight`);
147
156
  }
148
157
 
149
- forward(x: MlxArray, cache: SSMCache): MlxArray {
158
+ #convolve(qkv: MlxArray, state: MlxArray): [MlxArray, MlxArray] {
159
+ if (this.convolution) return this.convolution(qkv, state, this.convWeight);
160
+ const [B, S, D] = qkv.shape as [number, number, number];
161
+ const nKeep = this.convKernel - 1;
162
+ const input = ops.concatAxis([state, qkv], 1);
163
+ // MLX copy and contiguous can both alias the whole prefill buffer.
164
+ // Materialize only the tail so cache residency follows its logical size.
165
+ const view = input.slice([0, S, 0], [B, S + nKeep, D]);
166
+ const tail = materializeCopy(view);
167
+ view.dispose();
168
+ const conv = ops.conv1d(input, this.convWeight, 1, 0, 1, D);
169
+ input.dispose();
170
+ const out = compiledSilu(conv);
171
+ conv.dispose();
172
+ return [out, tail];
173
+ }
174
+
175
+ forward(x: MlxArray, cache: SSMCache, independentRows = false): MlxArray {
150
176
  const [B, S] = x.shape as [number, number, number];
151
177
  const convDim = this.keyDim * 2 + this.valueDim;
152
178
  const nKeep = this.convKernel - 1;
@@ -168,38 +194,25 @@ export class GatedDeltaNet {
168
194
  }
169
195
 
170
196
  const t0 = prof ? performance.now() : 0;
171
- const qkv = this.inProjQkv.forward(x); // [B,S,convDim]
172
- let z = this.inProjZ.forward(x);
197
+ const qkv = this.inProjQkv.forward(x, independentRows); // [B,S,convDim]
198
+ let z = this.inProjZ.forward(x, independentRows);
173
199
  z = disposing(z, ops.reshape(z, [B, S, this.numVHeads, this.headVDim]));
174
- const b = this.inProjB.forward(x); // [B,S,numVHeads]
175
- const a = this.inProjA.forward(x);
200
+ const b = this.inProjB.forward(x, independentRows); // [B,S,numVHeads]
201
+ const a = this.inProjA.forward(x, independentRows);
176
202
  if (prof) { ops.evalAll([qkv, z, b, a]); prof.proj = (prof.proj ?? 0) + performance.now() - t0; }
177
203
  const tc = prof ? performance.now() : 0; // [B,S,numVHeads]
178
204
 
179
205
  // Causal depthwise conv with the conv-state prefix (B=1: no ssm mask).
180
206
  const convState =
181
207
  cache.conv ?? ops.zeros([B, nKeep, convDim], x.dtype);
182
- const convInput = ops.concatAxis([convState, qkv], 1); // [B,S+nKeep,convDim]
208
+ const [convOut, newConv] = this.#convolve(qkv, convState);
183
209
  if (!cache.conv) convState.dispose();
184
210
  if (spec) spec.qkv = qkv;
185
211
  else qkv.dispose();
186
- // New conv state = last nKeep rows. MUST be a TRUE copy (mx.copy):
187
- // this tail slice is already row-contiguous at B=1, so ops.contiguous
188
- // is a no-op VIEW sharing convInput's buffer — caching it pinned the
189
- // whole [B, S+nKeep, convDim] chunk buffer (~42 MB/layer/chunk, 48
190
- // layers = the measured 2.0 GB-per-2048-chunk prefill leak; 46 GB
191
- // active at 32k, async-GPU-OOM on 24 GB — M4 2026-08-20).
192
- const newConvView = convInput.slice([0, S, 0], [B, S + nKeep, convDim]);
193
- const newConv = ops.copyOf(newConvView);
194
- newConvView.dispose();
195
212
  if (spec) spec.prevConv = cache.conv;
196
213
  else cache.conv?.dispose();
197
214
  cache.conv = newConv;
198
215
 
199
- const conv = ops.conv1d(convInput, this.convWeight, 1, 0, 1, convDim);
200
- convInput.dispose();
201
- const convOut = compiledSilu(conv); // nn.silu (mx.compile), matches mlx-lm
202
- conv.dispose();
203
216
  if (prof) { ops.evalAll([convOut]); prof.conv = (prof.conv ?? 0) + performance.now() - tc; }
204
217
  const tn = prof ? performance.now() : 0;
205
218
 
@@ -248,7 +261,7 @@ export class GatedDeltaNet {
248
261
  z.dispose();
249
262
  const merged = ops.reshape(gated, [B, S, this.valueDim]);
250
263
  gated.dispose();
251
- const result = this.outProj.forward(merged);
264
+ const result = this.outProj.forward(merged, independentRows);
252
265
  merged.dispose();
253
266
  if (prof) { ops.evalAll([result]); prof.out = (prof.out ?? 0) + performance.now() - to; }
254
267
  return result;
@@ -273,21 +286,12 @@ export class GatedDeltaNet {
273
286
  const qkvPfx = ops.contiguous(qkvPfxView);
274
287
  qkvPfxView.dispose();
275
288
  const convState = cache.conv ?? ops.zeros([B, nKeep, convDim], qkvPfx.dtype);
276
- const convInput = ops.concatAxis([convState, qkvPfx], 1);
289
+ const [convOut, newConv] = this.#convolve(qkvPfx, convState);
277
290
  if (!cache.conv) convState.dispose();
278
291
  qkvPfx.dispose();
279
- // TRUE copy, same reason as forward(): the contiguous tail slice would
280
- // otherwise be a view pinning the whole replay convInput buffer.
281
- const rConvView = convInput.slice([0, keep, 0], [B, keep + nKeep, convDim]);
282
- const newConv = ops.copyOf(rConvView);
283
- rConvView.dispose();
284
292
  cache.conv?.dispose();
285
293
  cache.conv = newConv;
286
294
 
287
- const conv = ops.conv1d(convInput, this.convWeight, 1, 0, 1, convDim);
288
- convInput.dispose();
289
- const convOut = compiledSilu(conv);
290
- conv.dispose();
291
295
  const [qFlat, kFlat, vFlat] = ops.split(
292
296
  convOut, [this.keyDim, 2 * this.keyDim], -1,
293
297
  ) as [MlxArray, MlxArray, MlxArray];
@@ -365,18 +369,18 @@ export class Qwen3Attention {
365
369
  this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
366
370
  }
367
371
 
368
- forward(x: MlxArray, mask: Mask, cache: Cache): MlxArray {
372
+ forward(x: MlxArray, mask: Mask, cache: Cache, independentRows = false): MlxArray {
369
373
  const [B, L] = x.shape as [number, number, number];
370
374
 
371
375
  // q_proj emits 2× head_dim per head → split into queries + gate.
372
- const qp = this.qProj.forward(x);
376
+ const qp = this.qProj.forward(x, independentRows);
373
377
  const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
374
378
  const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
375
379
  qpr.dispose();
376
380
  const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
377
381
 
378
- let k = this.kProj.forward(x);
379
- let v = this.vProj.forward(x);
382
+ let k = this.kProj.forward(x, independentRows);
383
+ let v = this.vProj.forward(x, independentRows);
380
384
 
381
385
  // q/k norm over head_dim BEFORE transpose (reference order).
382
386
  let q = this.qNorm.forward(qHeads);
@@ -391,7 +395,7 @@ export class Qwen3Attention {
391
395
  // Batched decode: the scheduler's mask wrapper exposes each row's REAL
392
396
  // position as ropeOffsetArr (rows have different prompt lengths); the
393
397
  // dynamic-offset kernel is the same fast::rope, bit-exact vs the static
394
- // form (tests/compile.test.ts). Serial lane: scalar offset, unchanged.
398
+ // form (tests/unit/compile.test.ts). Serial lane: scalar offset, unchanged.
395
399
  // Vision requests (serial lane only) install activeMrope for the current
396
400
  // forward: 3D interleaved positions via the manual apply — the reference
397
401
  // never uses the fused fast-rope kernel when position_ids are supplied,
@@ -439,32 +443,56 @@ export class Qwen3Attention {
439
443
  const gated = ops.mul(merged, sig);
440
444
  merged.dispose();
441
445
  sig.dispose();
442
- const out = this.oProj.forward(gated);
446
+ const out = this.oProj.forward(gated, independentRows);
443
447
  gated.dispose();
444
448
  return out;
445
449
  }
446
450
  }
447
451
 
452
+ /** MLP projections are affine-quantized (QuantizedLinear) or, in a Q2b
453
+ * packed-trellis artifact, TrellisLinear — chosen per module by the config's
454
+ * quantization entry (`mode: "trellis"`). */
455
+ export type MlpLinear = QuantizedLinear | TrellisLinear;
456
+ function loadMlpLinear(weights: Weights, path: string, config: ModelConfig): MlpLinear {
457
+ if (!TrellisLinear.isTrellis(config, path)) return QuantizedLinear.load(weights, path, config);
458
+ // M4 Pro measurements support the 27B down projection at M3/4. The
459
+ // operation also checks variant, dtype and packed-code layout eligibility.
460
+ const useSharedScatterCodebook = config.text.hiddenSize === 5120 &&
461
+ config.text.intermediateSize === 17408 && path.endsWith(".down_proj");
462
+ return TrellisLinear.load(weights, path, config, useSharedScatterCodebook);
463
+ }
464
+
448
465
  export class Qwen3MLP {
449
- readonly gate: QuantizedLinear;
450
- readonly up: QuantizedLinear;
451
- readonly down: QuantizedLinear;
466
+ readonly gate: MlpLinear;
467
+ readonly up: MlpLinear;
468
+ readonly down: MlpLinear;
452
469
 
453
470
  constructor(weights: Weights, config: ModelConfig, prefix: string) {
454
- this.gate = QuantizedLinear.load(weights, `${prefix}.gate_proj`, config);
455
- this.up = QuantizedLinear.load(weights, `${prefix}.up_proj`, config);
456
- this.down = QuantizedLinear.load(weights, `${prefix}.down_proj`, config);
471
+ this.gate = loadMlpLinear(weights, `${prefix}.gate_proj`, config);
472
+ this.up = loadMlpLinear(weights, `${prefix}.up_proj`, config);
473
+ this.down = loadMlpLinear(weights, `${prefix}.down_proj`, config);
457
474
  }
458
475
 
459
- forward(x: MlxArray): MlxArray {
460
- const g = this.gate.forward(x);
461
- const u = this.up.forward(x);
476
+ forward(x: MlxArray, inputRowContiguous = false, independentRows = false): MlxArray {
477
+ // Packed-trellis decode (M ≤ 4): gate, up and the swiglu in ONE kernel —
478
+ // x read once, no gate/up vectors materialized. This Lab path retains
479
+ // the packed activation arithmetic documented in turboquant.md.
480
+ if (this.gate instanceof TrellisLinear && this.up instanceof TrellisLinear &&
481
+ fusedGateUpEligible(this.gate, this.up) &&
482
+ x.shape.slice(0, -1).reduce((a, b) => a * b, 1) <= TRELLIS_MATVEC_MAX_M) {
483
+ const hidden = fusedGateUpSwiglu(x, this.gate, this.up);
484
+ const out = this.down instanceof QuantizedLinear ? this.down.forward(hidden, independentRows) : this.down.forward(hidden);
485
+ hidden.dispose();
486
+ return out;
487
+ }
488
+ const g = this.gate instanceof TrellisLinear ? this.gate.forward(x, inputRowContiguous) : this.gate.forward(x, independentRows);
489
+ const u = this.up instanceof TrellisLinear ? this.up.forward(x, inputRowContiguous) : this.up.forward(x, independentRows);
462
490
  // Oracle: down_proj(swiglu(gate_proj(x), up_proj(x))) — swiglu ALWAYS compiled
463
491
  // (mlx-lm's @mx.compile swiglu; no unfused silu+mul, matches its kernel set).
464
492
  const hidden = compiledSwiglu(g, u);
465
493
  g.dispose();
466
494
  u.dispose();
467
- const out = this.down.forward(hidden);
495
+ const out = this.down instanceof QuantizedLinear ? this.down.forward(hidden, independentRows) : this.down.forward(hidden);
468
496
  hidden.dispose();
469
497
  return out;
470
498
  }
@@ -489,16 +517,18 @@ export class Qwen3Layer {
489
517
  this.postAttnNorm = new RMSNorm(weights.tensor(`${prefix}.post_attention_layernorm.weight`), config.text.rmsNormEps);
490
518
  }
491
519
 
492
- forward(x: MlxArray, faMask: Mask, cache: Cache): MlxArray {
520
+ forward(x: MlxArray, faMask: Mask, cache: Cache, independentRows = false): MlxArray {
493
521
  const xn = this.inputNorm.forward(x);
494
522
  const r = this.isLinear
495
- ? this.linearAttn!.forward(xn, cache as SSMCache)
496
- : this.selfAttn!.forward(xn, faMask, cache);
523
+ ? this.linearAttn!.forward(xn, cache as SSMCache, independentRows)
524
+ : this.selfAttn!.forward(xn, faMask, cache, independentRows);
497
525
  xn.dispose();
498
526
  const h = ops.add(x, r);
499
527
  r.dispose();
500
528
  const hn = this.postAttnNorm.forward(h);
501
- const m = this.mlp.forward(hn);
529
+ // RMSNorm allocates an aligned row-contiguous output. Pass that layout
530
+ // proof so Trellis can match native small-prefill matmul without an eval.
531
+ const m = this.mlp.forward(hn, true, independentRows);
502
532
  hn.dispose();
503
533
  const out = ops.add(h, m);
504
534
  h.dispose();
@@ -522,6 +552,14 @@ export class Qwen35Model {
522
552
  readonly faIdx: number;
523
553
 
524
554
  constructor(weights: Weights, config: ModelConfig) {
555
+ // Checkpoint-generation normalization BEFORE the first tensor() call: the
556
+ // 5.8-family export names the trunk `model.language_model.*` with a
557
+ // top-level `lm_head`, ships `mtp.*` in-repo, and stores RMSNorm gains as
558
+ // γ−1 with HF-layout conv1d. The view maps all of that onto the names and
559
+ // values this graph reads (mlx-lm's post-sanitize space). No-op — nothing
560
+ // installed at all — for artifacts already in that space.
561
+ const view = qwen35WeightsView(weights);
562
+ if (view) weights.setView(view);
525
563
  this.config = config;
526
564
  this.tied = config.text.tieWordEmbeddings;
527
565
  this.weightsBytes = [...weights.shards.files.values()]
@@ -555,9 +593,10 @@ export class Qwen35Model {
555
593
  out.set(`${p}.linear_attn.in_proj_a`, l.linearAttn.inProjA);
556
594
  out.set(`${p}.linear_attn.out_proj`, l.linearAttn.outProj);
557
595
  }
558
- out.set(`${p}.mlp.gate_proj`, l.mlp.gate);
559
- out.set(`${p}.mlp.up_proj`, l.mlp.up);
560
- out.set(`${p}.mlp.down_proj`, l.mlp.down);
596
+ // Trellis-coded MLP tensors are not LoRA targets (no adapter seam yet).
597
+ if (l.mlp.gate instanceof QuantizedLinear) out.set(`${p}.mlp.gate_proj`, l.mlp.gate);
598
+ if (l.mlp.up instanceof QuantizedLinear) out.set(`${p}.mlp.up_proj`, l.mlp.up);
599
+ if (l.mlp.down instanceof QuantizedLinear) out.set(`${p}.mlp.down_proj`, l.mlp.down);
561
600
  }
562
601
  return out;
563
602
  }
@@ -566,6 +605,38 @@ export class Qwen35Model {
566
605
  return this.layers.map((l) => (l.isLinear ? new SSMCache() : new KVCache()));
567
606
  }
568
607
 
608
+ /** The 27B append path keeps the native M1 affine arithmetic while Trellis
609
+ * shares up to four rows. Other shapes retain single-token execution. */
610
+ createAppend(policy: { hasAdapters: boolean; pagedKv: boolean }) {
611
+ const t = this.config.text;
612
+ if (policy.hasAdapters || policy.pagedKv || this.loraState.active.length || this.mrope ||
613
+ t.hiddenSize !== 5120 || t.intermediateSize !== 17408 || this.layers.length !== 64 ||
614
+ t.headDim !== 256 || t.numAttentionHeads !== 24 || t.numKeyValueHeads !== 4)
615
+ return null;
616
+ if (deviceArchitecture() !== "applegpu_g16s") return null;
617
+ const qualified = (linear: QuantizedLinear | TrellisLinear) => {
618
+ if (linear instanceof TrellisLinear) return true;
619
+ const { mode, bits, groupSize } = linear.spec;
620
+ return mode === "affine" && [2, 3, 4, 6, 8].includes(bits) && [32, 64, 128].includes(groupSize);
621
+ };
622
+ for (const layer of this.layers) {
623
+ const a = layer.linearAttn, s = layer.selfAttn;
624
+ const attention = a ? [a.inProjQkv, a.inProjZ, a.inProjB, a.inProjA, a.outProj]
625
+ : [s!.qProj, s!.kProj, s!.vProj, s!.oProj];
626
+ if (!attention.every(qualified) || ![layer.mlp.gate, layer.mlp.up, layer.mlp.down].every(qualified))
627
+ return null;
628
+ }
629
+ return {
630
+ maxChunkSize: (state: readonly Cache[]) => qwenAppendChunkSize(state[0]!.offset),
631
+ forwardHidden: (ids: MlxArray, cache: Cache[]): MlxArray => {
632
+ if (ids.shape.length !== 2 || ids.shape[0] !== 1 || ids.shape[1]! > 4)
633
+ throw new Error("Qwen committed-token append supports one row and at most four positions");
634
+ const hidden = this.embed.encode(ids);
635
+ return this.forwardLayers(hidden, cache, true);
636
+ },
637
+ };
638
+ }
639
+
569
640
  forwardHidden(ids: MlxArray, cache: Cache[]): MlxArray {
570
641
  const h = this.embed.encode(ids);
571
642
  return this.forwardLayers(h, cache);
@@ -576,6 +647,8 @@ export class Qwen35Model {
576
647
  * the bit-exact fast-rope path). While set, forwardLayers installs the
577
648
  * per-forward interleaved cos/sin consumed by every full-attn layer. */
578
649
  mrope: MropeRequestState | null = null;
650
+ /** Serving's memory-pressure check, scoped to the serial request. */
651
+ prefillMemoryGuard: (() => void) | null = null;
579
652
  #mropeInvFreq: MlxArray | null = null;
580
653
 
581
654
  /** Vision prefill: spliced input embeddings [1, L, H] (image features
@@ -609,7 +682,7 @@ export class Qwen35Model {
609
682
  tap.captured.set(i, copy);
610
683
  }
611
684
 
612
- protected forwardLayers(h0: MlxArray, cache: Cache[]): MlxArray {
685
+ protected forwardLayers(h0: MlxArray, cache: Cache[], independentRows = false): MlxArray {
613
686
  const L = h0.shape[1]!;
614
687
  // One full-attention mask shared by all full layers (same offset); linear
615
688
  // layers see no ssm mask at B=1.
@@ -633,10 +706,21 @@ export class Qwen35Model {
633
706
  | undefined;
634
707
  try {
635
708
  for (let i = 0; i < this.layers.length; i++) {
709
+ if (L > 1) this.prefillMemoryGuard?.();
636
710
  const tl = prof ? performance.now() : 0;
637
- const next = this.layers[i]!.forward(h, faMask, cache[i]!);
711
+ const next = this.layers[i]!.forward(h, faMask, cache[i]!, independentRows);
638
712
  h.dispose();
639
713
  h = next;
714
+ const mlp = this.layers[i]!.mlp;
715
+ if (L > TRELLIS_MATVEC_MAX_M &&
716
+ [mlp.gate, mlp.up, mlp.down].some((p) => p instanceof TrellisLinear && !p.fallback)) {
717
+ // Materialize the cache outputs too. In particular, the tiny
718
+ // copied conv tail is not a dependency of `h`; leaving it lazy
719
+ // pins the whole prefill conv buffer until the chunk ends.
720
+ const state = cache[i]!.state();
721
+ try { ops.evalAll([h, ...state]); }
722
+ finally { if (cache[i]!.stateNeedsDispose) for (const a of state) a.dispose(); }
723
+ }
640
724
  if (prof) {
641
725
  ops.evalAll([h]);
642
726
  const key = this.layers[i]!.isLinear ? "layerLin" : "layerFull";
@@ -56,7 +56,7 @@ export function isDrafterModelType(modelType: string): boolean {
56
56
 
57
57
  /** DiffusionGemma (model_type `diffusion_gemma`): block/masked-diffusion canvas
58
58
  * model. Non-autoregressive — routed through the diffusion engine, not the AR
59
- * loop. See docs/design/diffusion-gemma-port.md. */
59
+ * loop. See docs/design/generic-model-support.md. */
60
60
  export function isDiffusionGemmaConfig(config: ModelConfig): boolean {
61
61
  return config.modelType === "diffusion_gemma";
62
62
  }
@@ -0,0 +1,51 @@
1
+ // Experimental variant 8, k=3 / T=256 / L<=12 only.
2
+ // The word-per-lane kernel uses 24 of 32 SIMD lanes for a 3-bit block.
3
+ // Assign eight outputs to every lane, load adjacent packed words, and align
4
+ // them once so each unrolled decoder has constant shifts. Row accumulation,
5
+ // split boundaries and the separate partial reduction stay identical to v6.
6
+ export const BALANCED_SCATTER_SOURCE = String.raw`
7
+ const uint lane = thread_index_in_simdgroup;
8
+ const uint sg = simdgroup_index_in_threadgroup;
9
+ const uint block = thread_position_in_grid.y * (uint)SG_TG + sg;
10
+ const uint nBlocks = (uint)C / (uint)BT;
11
+ const uint zi = thread_position_in_grid.z;
12
+ const uint sample = zi / (uint)SPLITS;
13
+ const uint split = zi - sample * (uint)SPLITS;
14
+ if (block >= nBlocks || sample >= (uint)M) return;
15
+ const uint wpb = (uint)(BT * K / 32);
16
+ const ulong rowWords = (ulong)(INTERLEAVE ? 2 : nBlocks) * wpb;
17
+ const uint pBase = lane * 8u * (uint)K;
18
+ const uint wi = pBase >> 5u;
19
+ const uint off = pBase & 31u;
20
+ const uint next = (wi + 1u == wpb) ? 0u : wi + 1u;
21
+ const device uint32_t* words = codes + (INTERLEAVE
22
+ ? (ulong)(block / 2) * R * 2 * wpb + (block % 2) * wpb : (ulong)block * wpb);
23
+ const uint rowsPer = ((uint)R + (uint)SPLITS - 1u) / (uint)SPLITS;
24
+ const uint r0 = split * rowsPer;
25
+ const uint r1 = metal::min((uint)R, r0 + rowsPer);
26
+ const device T* xs = x + (ulong)sample * (ulong)R;
27
+ float acc[8];
28
+ #pragma clang loop unroll(full)
29
+ for (uint i = 0; i < 8u; ++i) acc[i] = 0.0f;
30
+ for (uint r = r0; r < r1; ++r) {
31
+ const uint word = words[(ulong)r * rowWords + wi];
32
+ const uint nxt = words[(ulong)r * rowWords + next];
33
+ const uint lo = off == 0u ? word : (word >> off) | (nxt << (32u - off));
34
+ const uint hi = off == 0u ? nxt : nxt >> off;
35
+ const float xr = float(xs[r]);
36
+ const float sr = float(scales[r]);
37
+ #pragma clang loop unroll(full)
38
+ for (uint i = 0; i < 8u; ++i) {
39
+ const uint shift = i * (uint)K;
40
+ uint win = lo >> shift;
41
+ if (shift + (uint)L > 32u) win |= hi << (32u - shift);
42
+ win &= (1u << (uint)L) - 1u;
43
+ const float value = (float)trellis_y(win) * (1.0f / 147.800537109375f);
44
+ acc[i] = metal::fma(value * sr, xr, acc[i]);
45
+ }
46
+ }
47
+ const ulong outBase = ((ulong)sample * (ulong)SPLITS + split) * (ulong)C;
48
+ #pragma clang loop unroll(full)
49
+ for (uint i = 0; i < 8u; ++i)
50
+ partial[outBase + block * (uint)BT + (uint)BT - 1u - lane * 8u - i] = acc[i];
51
+ `;