mlx-bun 0.0.4

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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +362 -0
  3. package/THIRD_PARTY_LICENSES.md +41 -0
  4. package/bin/mlx-bun.mjs +32 -0
  5. package/docs/curve-designer.html +303 -0
  6. package/docs/design/adapters-end-to-end.md +201 -0
  7. package/docs/design/compat-cli-surface-design.md +227 -0
  8. package/docs/design/docs-reorg-plan.md +152 -0
  9. package/docs/design/hlg-sampling.md +340 -0
  10. package/docs/design/memory-system.md +339 -0
  11. package/docs/design/optimization_plan.md +344 -0
  12. package/docs/design/parallel-slots.md +515 -0
  13. package/docs/design/runtime-isolation.md +247 -0
  14. package/docs/design/segmented-backward-training.md +508 -0
  15. package/docs/investigations/chunk-finetune-experiment.md +84 -0
  16. package/docs/investigations/curve-bisector-routing.md +123 -0
  17. package/docs/investigations/curve-runs/bisector-route.json +98 -0
  18. package/docs/investigations/curve-runs/bisector-trace.json +1393 -0
  19. package/docs/investigations/curve-runs/curve-terrain.json +1179 -0
  20. package/docs/investigations/curve-runs/curve-terrain.svg +42 -0
  21. package/docs/investigations/curve-terrain.html +105 -0
  22. package/docs/investigations/expert-offload-single-user-moe.md +292 -0
  23. package/docs/investigations/hlg-ab.html +34 -0
  24. package/docs/investigations/hlg-explorer.html +238 -0
  25. package/docs/investigations/hlg-figs/cliff.svg +1 -0
  26. package/docs/investigations/hlg-figs/cross.svg +1 -0
  27. package/docs/investigations/hlg-figs/edges.svg +1 -0
  28. package/docs/investigations/hlg-figs/k20-grids.svg +1 -0
  29. package/docs/investigations/hlg-figs/k20-peaks.svg +1 -0
  30. package/docs/investigations/hlg-figs/lw-curve.svg +1 -0
  31. package/docs/investigations/hlg-figs/terrain.svg +1 -0
  32. package/docs/investigations/hlg-report.html +262 -0
  33. package/docs/investigations/hlg-runs/hlg-ab.json +149 -0
  34. package/docs/investigations/hlg-runs/hlg-blind-prompts.md +87 -0
  35. package/docs/investigations/hlg-runs/hlg-blind.json +107 -0
  36. package/docs/investigations/hlg-runs/hlg-cliff.json +197 -0
  37. package/docs/investigations/hlg-runs/hlg-cross.json +505 -0
  38. package/docs/investigations/hlg-runs/hlg-edge.json +388 -0
  39. package/docs/investigations/hlg-runs/hlg-eval1.json +25 -0
  40. package/docs/investigations/hlg-runs/hlg-frontier.json +86 -0
  41. package/docs/investigations/hlg-runs/hlg-grid.json +242 -0
  42. package/docs/investigations/hlg-runs/hlg-grid2.json +343 -0
  43. package/docs/investigations/hlg-runs/hlg-lw-coarse.json +110 -0
  44. package/docs/investigations/hlg-runs/hlg-map.json +383 -0
  45. package/docs/investigations/hlg-runs/run0-original.log +159 -0
  46. package/docs/investigations/hlg-runs/runA-recommended.log +131 -0
  47. package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +26 -0
  48. package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +18 -0
  49. package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +25 -0
  50. package/docs/investigations/hlg-runs/runAD-ab-samples.log +8 -0
  51. package/docs/investigations/hlg-runs/runAE-edge-e4b.log +51 -0
  52. package/docs/investigations/hlg-runs/runAF-logit-calibration.log +10 -0
  53. package/docs/investigations/hlg-runs/runAG-eval-config2.log +16 -0
  54. package/docs/investigations/hlg-runs/runB-gain-variants.log +192 -0
  55. package/docs/investigations/hlg-runs/runC-pivots.log +162 -0
  56. package/docs/investigations/hlg-runs/runD-oetf.log +132 -0
  57. package/docs/investigations/hlg-runs/runE-eotf.log +162 -0
  58. package/docs/investigations/hlg-runs/runF-shaper-working.log +86 -0
  59. package/docs/investigations/hlg-runs/runG-shaper-sweep.log +101 -0
  60. package/docs/investigations/hlg-runs/runH-wsweep.log +176 -0
  61. package/docs/investigations/hlg-runs/runI-asweep.log +176 -0
  62. package/docs/investigations/hlg-runs/runJ-knobmap.log +49 -0
  63. package/docs/investigations/hlg-runs/runK-frontier.log +19 -0
  64. package/docs/investigations/hlg-runs/runL-widemap.log +65 -0
  65. package/docs/investigations/hlg-runs/runM-frontier-emb.log +15 -0
  66. package/docs/investigations/hlg-runs/runN-verify.log +116 -0
  67. package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +15 -0
  68. package/docs/investigations/hlg-runs/runP-grid-AxSm.log +15 -0
  69. package/docs/investigations/hlg-runs/runQ-grid-tgW.log +15 -0
  70. package/docs/investigations/hlg-runs/runR-blind-transcript.log +79 -0
  71. package/docs/investigations/hlg-runs/runS-cross-coherence.log +33 -0
  72. package/docs/investigations/hlg-runs/runT-cross-cpm5.log +33 -0
  73. package/docs/investigations/hlg-runs/runU-cross-12b.log +33 -0
  74. package/docs/investigations/hlg-runs/runV-lw-e4b.log +22 -0
  75. package/docs/investigations/hlg-runs/runW-lw-12b.log +22 -0
  76. package/docs/investigations/hlg-runs/runX-lw-cpm5.log +22 -0
  77. package/docs/investigations/hlg-runs/runY-cliff-e4b.log +26 -0
  78. package/docs/investigations/hlg-runs/runZ-cliff-12b.log +26 -0
  79. package/docs/investigations/hlg-sampling-investigation.md +970 -0
  80. package/docs/investigations/lab-build-journal.md +157 -0
  81. package/docs/investigations/pi-builtin-investigation.md +308 -0
  82. package/docs/investigations/starter-model-port-handoff.md +323 -0
  83. package/docs/planning/IDEAS.md +26 -0
  84. package/docs/planning/PRODUCT_ROADMAP.md +426 -0
  85. package/docs/planning/ResearchTopics.md +119 -0
  86. package/docs/planning/chunk-task-roadmap.md +77 -0
  87. package/docs/planning/journal.md +155 -0
  88. package/docs/reference/distribution.md +121 -0
  89. package/docs/reference/embedding.md +168 -0
  90. package/docs/reference/library-api.md +153 -0
  91. package/docs/reference/server-api.md +224 -0
  92. package/docs/reference/server-config.md +243 -0
  93. package/docs/segmented-backward-handoff.md +148 -0
  94. package/package.json +68 -0
  95. package/src/anthropic.ts +491 -0
  96. package/src/chat-template.ts +186 -0
  97. package/src/cli.ts +1075 -0
  98. package/src/config.ts +229 -0
  99. package/src/curve-sampler.ts +122 -0
  100. package/src/dataset/generators.ts +1087 -0
  101. package/src/dataset/index.ts +12 -0
  102. package/src/dataset/job.ts +81 -0
  103. package/src/dataset/llm.ts +71 -0
  104. package/src/dataset/registry.ts +528 -0
  105. package/src/download.ts +283 -0
  106. package/src/eval/README.md +86 -0
  107. package/src/eval/kl-prompts.ts +29 -0
  108. package/src/eval/kl.ts +294 -0
  109. package/src/eval/quality-db.ts +92 -0
  110. package/src/eval/runner.ts +102 -0
  111. package/src/eval/score.ts +49 -0
  112. package/src/eval/tasks/bfcl.ts +500 -0
  113. package/src/eval/tasks/gsm8k.ts +85 -0
  114. package/src/eval/tasks/hashhop.ts +109 -0
  115. package/src/eval/tasks/humaneval.ts +252 -0
  116. package/src/eval/tasks/ifeval.ts +354 -0
  117. package/src/eval/tasks/mmlu.ts +145 -0
  118. package/src/evaldb.ts +199 -0
  119. package/src/expert-offload-build.ts +77 -0
  120. package/src/expert-offload.ts +61 -0
  121. package/src/expert-trace.ts +67 -0
  122. package/src/fit.ts +254 -0
  123. package/src/generate.ts +451 -0
  124. package/src/harness-pi.ts +177 -0
  125. package/src/hf-push.ts +628 -0
  126. package/src/index.ts +22 -0
  127. package/src/jobs/db.ts +139 -0
  128. package/src/jobs/index.ts +29 -0
  129. package/src/jobs/job-entry.ts +106 -0
  130. package/src/jobs/runner.ts +304 -0
  131. package/src/jobs/sse.ts +114 -0
  132. package/src/jobs/types.ts +58 -0
  133. package/src/kv-store.ts +185 -0
  134. package/src/lora.ts +314 -0
  135. package/src/md-modules.d.ts +7 -0
  136. package/src/mlx/array.ts +237 -0
  137. package/src/mlx/autograd.ts +267 -0
  138. package/src/mlx/checkpoint.ts +116 -0
  139. package/src/mlx/compile.ts +158 -0
  140. package/src/mlx/custom-vjp.ts +140 -0
  141. package/src/mlx/ffi.ts +433 -0
  142. package/src/mlx/metal-kernel.ts +130 -0
  143. package/src/mlx/ops.ts +707 -0
  144. package/src/mmap.ts +90 -0
  145. package/src/model/batched-mask.ts +204 -0
  146. package/src/model/batched-rotating.ts +300 -0
  147. package/src/model/compiled-decode.ts +681 -0
  148. package/src/model/factory.ts +27 -0
  149. package/src/model/fingerprint.ts +33 -0
  150. package/src/model/flash-attention.ts +674 -0
  151. package/src/model/fused-decode-kernel.ts +305 -0
  152. package/src/model/fused-geglu-kernel.ts +87 -0
  153. package/src/model/gemma4-base.ts +1616 -0
  154. package/src/model/gemma4.ts +1012 -0
  155. package/src/model/generated/gemma4-12b.ts +526 -0
  156. package/src/model/generated/gemma4-26b.ts +500 -0
  157. package/src/model/generated/gemma4-e4b.ts +757 -0
  158. package/src/model/generated/index.ts +18 -0
  159. package/src/model/minicpm5.ts +278 -0
  160. package/src/model/qwen3-delta.ts +224 -0
  161. package/src/model/qwen3_5.ts +429 -0
  162. package/src/model/support.ts +46 -0
  163. package/src/native-pack.ts +152 -0
  164. package/src/pi-provider.ts +95 -0
  165. package/src/pi-terminal.ts +264 -0
  166. package/src/pi-web.ts +924 -0
  167. package/src/preflight.ts +123 -0
  168. package/src/prompt-cache.ts +113 -0
  169. package/src/quantize/allocator.ts +627 -0
  170. package/src/quantize/calibration.ts +180 -0
  171. package/src/quantize/config-writer.ts +132 -0
  172. package/src/quantize/index.ts +57 -0
  173. package/src/quantize/job.ts +136 -0
  174. package/src/quantize/quantizer.ts +462 -0
  175. package/src/quantize/safetensors-writer.ts +171 -0
  176. package/src/quantize/sensitivity.ts +234 -0
  177. package/src/registry.ts +336 -0
  178. package/src/responses.ts +774 -0
  179. package/src/safetensors.ts +158 -0
  180. package/src/sampler.ts +517 -0
  181. package/src/serve/batch-scheduler.ts +362 -0
  182. package/src/serve/generation-gateway.ts +160 -0
  183. package/src/server.ts +1891 -0
  184. package/src/spec/drafter.ts +261 -0
  185. package/src/spec/generate.ts +221 -0
  186. package/src/status-page.html +497 -0
  187. package/src/tokenizer.ts +38 -0
  188. package/src/tool-call.ts +242 -0
  189. package/src/train/dataset.ts +461 -0
  190. package/src/train/export.ts +32 -0
  191. package/src/train/forward.ts +229 -0
  192. package/src/train/index.ts +43 -0
  193. package/src/train/job.ts +148 -0
  194. package/src/train/lora-params.ts +194 -0
  195. package/src/train/loss.ts +342 -0
  196. package/src/train/merge.ts +195 -0
  197. package/src/train/optimizer.ts +176 -0
  198. package/src/train/rank.ts +101 -0
  199. package/src/train/segmented.ts +520 -0
  200. package/src/train/trainer.ts +600 -0
  201. package/src/tui.ts +161 -0
  202. package/src/vision/embedder.ts +168 -0
  203. package/src/vision/preprocess.ts +247 -0
  204. package/src/vision/prompt.ts +158 -0
  205. package/src/vision/siglip.ts +599 -0
  206. package/src/web/app.html +2471 -0
  207. package/src/web/skills/web-research/SKILL.md +44 -0
  208. package/src/web/skills.ts +59 -0
  209. package/src/web-tools.ts +426 -0
  210. package/src/weights.ts +91 -0
@@ -0,0 +1,158 @@
1
+ // mx.compile from Bun: wrap a JS graph-builder in an mlx_closure, hand it
2
+ // to mlx_compile, and replay the compiled graph in C++ thereafter.
3
+ //
4
+ // The trace function runs EXACTLY ONCE per (ndim, dtype) input signature
5
+ // (shapeless=true): mlx invokes our JSCallback with tracer arrays during
6
+ // the first mlx_closure_apply, we build the graph with the ordinary ops
7
+ // layer, and every later apply replays it natively — zero per-op FFI.
8
+ //
9
+ // Contract for trace functions:
10
+ // - receive positional MlxArray inputs, return positional MlxArray outputs
11
+ // - must NOT dispose the inputs (the trampoline owns them) and must NOT
12
+ // eval anything (tracer arrays have no data — eval inside a trace is an
13
+ // mlx error by design)
14
+ // - arrays created inside the trace (weights, scalarLike constants) are
15
+ // captured as graph constants — correct for weights, which never change
16
+ // - with shapeless=true the graph must stay valid as dim SIZES change
17
+ // (ndim/dtype changes retrace automatically): no reshape/slice constants
18
+ // derived from a varying dimension
19
+
20
+ import { JSCallback, ptr, read } from "bun:ffi";
21
+ import { C, type MlxHandle, takeMlxError } from "./ffi";
22
+ import { MlxArray } from "./array";
23
+
24
+ export type TraceFn = (inputs: MlxArray[]) => MlxArray[];
25
+
26
+ const traceRegistry = new Map<number, { fn: TraceFn; count: number }>();
27
+ let nextTraceId = 1;
28
+ /** Error thrown by a trace fn, carried across the C boundary (the
29
+ * trampoline must not throw into mlx; it returns non-zero instead). */
30
+ let traceError: unknown = null;
31
+
32
+ /** Count of trace invocations, exposed for tests (a working shapeless
33
+ * compile traces once and replays thereafter). */
34
+ export let traceCalls = 0;
35
+
36
+ // int (*fun)(mlx_vector_array* res, const mlx_vector_array args, void* payload)
37
+ // mlx_vector_array is the usual one-pointer struct, by value → u64.
38
+ // Tracing happens synchronously inside mlx_closure_apply on the calling
39
+ // thread, so a non-threadsafe JSCallback is safe (same argument as the
40
+ // error handler in ffi.ts).
41
+ const trampoline = new JSCallback(
42
+ (resPtr: number, argsVec: bigint, payload: number): number => {
43
+ try {
44
+ const entry = traceRegistry.get(payload);
45
+ if (!entry) throw new Error(`compile trampoline: unknown trace id ${payload}`);
46
+ const fn = entry.fn;
47
+ entry.count++;
48
+ traceCalls++;
49
+ const n = Number(C.mlx_vector_array_size(argsVec));
50
+ const inputs: MlxArray[] = [];
51
+ const slot = new BigUint64Array(1);
52
+ const slotPtr = ptr(slot);
53
+ for (let i = 0; i < n; i++) {
54
+ slot[0] = C.mlx_array_new();
55
+ if (C.mlx_vector_array_get(slotPtr, argsVec, BigInt(i)) !== 0)
56
+ throw new Error(`vector_array_get(${i}) failed: ${takeMlxError() ?? ""}`);
57
+ inputs.push(new MlxArray(read.u64(slotPtr, 0)));
58
+ }
59
+ const outputs = fn(inputs);
60
+ const handles = new BigUint64Array(outputs.map((o) => o.handle));
61
+ if (C.mlx_vector_array_set_data(resPtr, ptr(handles), BigInt(outputs.length)) !== 0)
62
+ throw new Error(`vector_array_set_data failed: ${takeMlxError() ?? ""}`);
63
+ for (const o of outputs) o.dispose();
64
+ for (const a of inputs) a.dispose();
65
+ return 0;
66
+ } catch (e) {
67
+ traceError = e;
68
+ return 1;
69
+ }
70
+ },
71
+ { args: ["ptr", "u64", "ptr"], returns: "i32" },
72
+ );
73
+
74
+ /** MLX_COMPILE_MODE_* (compile.h). NO_FUSE keeps the host-side replay win
75
+ * while disabling kernel fusion — the escape hatch if fusion ever breaks
76
+ * the bit-exact gate. */
77
+ export function setCompileMode(mode: "disabled" | "no_simplify" | "no_fuse" | "enabled"): void {
78
+ const v = { disabled: 0, no_simplify: 1, no_fuse: 2, enabled: 3 }[mode];
79
+ if (C.mlx_set_compile_mode(v) !== 0)
80
+ throw new Error(`mlx_set_compile_mode failed: ${takeMlxError() ?? ""}`);
81
+ }
82
+
83
+ export class CompiledFunction {
84
+ #compiled: MlxHandle;
85
+ #traceId: number;
86
+ #disposed = false;
87
+
88
+ /** Times this function's trace ran. A shapeless decode closure must
89
+ * trace exactly once — its input ndims/dtypes never change — so any
90
+ * count > 1 is a silent retrace (per-step JS graph builds sneaking
91
+ * back in) and gets flagged by compiled-decode. */
92
+ get traceCount(): number {
93
+ return traceRegistry.get(this.#traceId)?.count ?? 0;
94
+ }
95
+
96
+ constructor(fn: TraceFn, shapeless = true) {
97
+ this.#traceId = nextTraceId++;
98
+ traceRegistry.set(this.#traceId, { fn, count: 0 });
99
+ // payload is the trace id smuggled through the void* (never deref'd);
100
+ // dtor is null — nothing to free on the C side.
101
+ const raw = C.mlx_closure_new_func_payload(trampoline.ptr, this.#traceId as never, null);
102
+ const slot = new BigUint64Array([C.mlx_closure_new()]);
103
+ const slotPtr = ptr(slot);
104
+ const status = C.mlx_compile(slotPtr, raw, shapeless);
105
+ const compiled = read.u64(slotPtr, 0);
106
+ C.mlx_closure_free(raw);
107
+ if (status !== 0) {
108
+ C.mlx_closure_free(compiled);
109
+ traceRegistry.delete(this.#traceId);
110
+ throw new Error(`mlx_compile failed: ${takeMlxError() ?? ""}`);
111
+ }
112
+ this.#compiled = compiled;
113
+ }
114
+
115
+ /** Run the compiled graph. First call (per ndim/dtype signature) traces
116
+ * through the JS fn; later calls replay natively. Returned arrays are
117
+ * owned by the caller. */
118
+ apply(inputs: MlxArray[]): MlxArray[] {
119
+ if (this.#disposed) throw new Error("CompiledFunction used after dispose");
120
+ const handles = new BigUint64Array(inputs.length);
121
+ for (let i = 0; i < inputs.length; i++) handles[i] = inputs[i]!.handle;
122
+ const inVec = C.mlx_vector_array_new_data(ptr(handles), BigInt(inputs.length));
123
+ const slot = new BigUint64Array([C.mlx_vector_array_new()]);
124
+ const slotPtr = ptr(slot);
125
+ const status = C.mlx_closure_apply(slotPtr, this.#compiled, inVec);
126
+ const outVec = read.u64(slotPtr, 0);
127
+ C.mlx_vector_array_free(inVec);
128
+ if (status !== 0) {
129
+ C.mlx_vector_array_free(outVec);
130
+ const e = traceError;
131
+ traceError = null;
132
+ throw e instanceof Error
133
+ ? e
134
+ : new Error(`compiled apply failed: ${takeMlxError() ?? "(no mlx message)"}`);
135
+ }
136
+ const n = Number(C.mlx_vector_array_size(outVec));
137
+ const outputs: MlxArray[] = [];
138
+ const aSlot = new BigUint64Array(1);
139
+ const aPtr = ptr(aSlot);
140
+ for (let i = 0; i < n; i++) {
141
+ aSlot[0] = C.mlx_array_new();
142
+ if (C.mlx_vector_array_get(aPtr, outVec, BigInt(i)) !== 0) {
143
+ C.mlx_vector_array_free(outVec);
144
+ throw new Error(`vector_array_get(out ${i}) failed: ${takeMlxError() ?? ""}`);
145
+ }
146
+ outputs.push(new MlxArray(read.u64(aPtr, 0)));
147
+ }
148
+ C.mlx_vector_array_free(outVec);
149
+ return outputs;
150
+ }
151
+
152
+ dispose(): void {
153
+ if (this.#disposed) return;
154
+ this.#disposed = true;
155
+ C.mlx_closure_free(this.#compiled);
156
+ traceRegistry.delete(this.#traceId);
157
+ }
158
+ }
@@ -0,0 +1,140 @@
1
+ // Custom vjp via mlx-c mlx_custom_vjp: attach a hand-written backward to a
2
+ // forward whose primitives have no usable vjp (a fused mx.fast.metal_kernel
3
+ // is a CustomKernel with no gradient). This is the mechanism the L2 flash-
4
+ // attention training op uses — the forward is a Metal kernel, the backward is
5
+ // three more Metal kernels, wired together here so the enclosing
6
+ // value_and_grad differentiates through them.
7
+ //
8
+ // Same FFI lifecycle as ValueAndGrad/Checkpoint: a JSCallback per closure →
9
+ // mlx_closure_new_func_payload (forward) + mlx_closure_custom_new_func_payload
10
+ // (vjp) → mlx_custom_vjp → applied with mlx_closure_apply. Closures must not
11
+ // throw across the FFI boundary; a JS throw is captured and surfaced from
12
+ // apply().
13
+
14
+ import { JSCallback, ptr, read } from "bun:ffi";
15
+ import { C, takeMlxError } from "./ffi";
16
+ import { MlxArray } from "./array";
17
+
18
+ function readVec(vec: bigint): MlxArray[] {
19
+ const n = Number(C.mlx_vector_array_size(vec));
20
+ const out: MlxArray[] = [];
21
+ for (let i = 0; i < n; i++) {
22
+ const slot = new BigUint64Array([C.mlx_array_new()]);
23
+ const sp = ptr(slot);
24
+ if (C.mlx_vector_array_get(sp, vec, BigInt(i)) !== 0)
25
+ throw new Error(`vector_array_get(${i}) failed`);
26
+ out.push(new MlxArray(read.u64(sp, 0)));
27
+ }
28
+ return out;
29
+ }
30
+
31
+ function setVec(outPtr: number, arrays: MlxArray[]): void {
32
+ const handles = new BigUint64Array(arrays.map((a) => a.handle));
33
+ if (C.mlx_vector_array_set_data(outPtr as never, ptr(handles), BigInt(arrays.length)) !== 0)
34
+ throw new Error("vector_array_set_data failed");
35
+ }
36
+
37
+ /** A reusable forward op with a hand-written backward.
38
+ *
39
+ * `fwd(inputs)` returns the forward outputs. `vjp(primals, cotangents,
40
+ * outputs)` returns one gradient per primal (in primal order). Both must be
41
+ * pure functions of their arguments — the vjp re-reads everything from the
42
+ * graph during the enclosing backward. */
43
+ export class CustomVjp {
44
+ readonly #fwdCb: JSCallback;
45
+ readonly #vjpCb: JSCallback;
46
+ readonly #fwdClosure: bigint;
47
+ readonly #vjpClosure: bigint;
48
+ readonly #combined: bigint;
49
+ #error: string | null = null;
50
+ #disposed = false;
51
+
52
+ constructor(
53
+ fwd: (inputs: MlxArray[]) => MlxArray[],
54
+ vjp: (primals: MlxArray[], cotangents: MlxArray[], outputs: MlxArray[]) => MlxArray[],
55
+ ) {
56
+ this.#fwdCb = new JSCallback(
57
+ (outPtr: number, inVec: bigint, _payload: number): number => {
58
+ try {
59
+ const inputs = readVec(inVec);
60
+ const outs = fwd(inputs);
61
+ setVec(outPtr, outs);
62
+ for (const p of inputs) p.dispose();
63
+ for (const o of outs) o.dispose();
64
+ return 0;
65
+ } catch (e) {
66
+ this.#error = e instanceof Error ? e.message : String(e);
67
+ return 1;
68
+ }
69
+ },
70
+ { args: ["ptr", "u64", "ptr"], returns: "i32" },
71
+ );
72
+
73
+ this.#vjpCb = new JSCallback(
74
+ (resPtr: number, primalsVec: bigint, cotsVec: bigint, outsVec: bigint, _payload: number): number => {
75
+ try {
76
+ const primals = readVec(primalsVec);
77
+ const cots = readVec(cotsVec);
78
+ const outs = readVec(outsVec);
79
+ const grads = vjp(primals, cots, outs);
80
+ setVec(resPtr, grads);
81
+ for (const a of [...primals, ...cots, ...outs]) a.dispose();
82
+ for (const g of grads) g.dispose();
83
+ return 0;
84
+ } catch (e) {
85
+ this.#error = e instanceof Error ? e.message : String(e);
86
+ return 1;
87
+ }
88
+ },
89
+ { args: ["ptr", "u64", "u64", "u64", "ptr"], returns: "i32" },
90
+ );
91
+
92
+ this.#fwdClosure = C.mlx_closure_new_func_payload(this.#fwdCb.ptr as never, null, null);
93
+ this.#vjpClosure = C.mlx_closure_custom_new_func_payload(this.#vjpCb.ptr as never, null, null);
94
+
95
+ const slot = new BigUint64Array([C.mlx_closure_new()]);
96
+ if (C.mlx_custom_vjp(ptr(slot), this.#fwdClosure, this.#vjpClosure) !== 0) {
97
+ C.mlx_closure_free(this.#fwdClosure);
98
+ C.mlx_closure_custom_free(this.#vjpClosure);
99
+ this.#fwdCb.close();
100
+ this.#vjpCb.close();
101
+ throw new Error(`mlx_custom_vjp failed: ${takeMlxError() ?? ""}`);
102
+ }
103
+ this.#combined = read.u64(ptr(slot), 0);
104
+ }
105
+
106
+ apply(inputs: MlxArray[]): MlxArray[] {
107
+ if (this.#disposed) throw new Error("CustomVjp used after dispose");
108
+ this.#error = null;
109
+
110
+ const handles = new BigUint64Array(inputs.map((p) => p.handle));
111
+ const inVec = C.mlx_vector_array_new_data(ptr(handles), BigInt(inputs.length));
112
+ const outSlot = new BigUint64Array([C.mlx_vector_array_new()]);
113
+ const st = C.mlx_closure_apply(ptr(outSlot), this.#combined, inVec);
114
+ const outVec = read.u64(ptr(outSlot), 0);
115
+ if (st !== 0) {
116
+ C.mlx_vector_array_free(outVec);
117
+ C.mlx_vector_array_free(inVec);
118
+ const detail = this.#error ?? takeMlxError() ?? "";
119
+ throw new Error(`custom_vjp apply failed${detail ? `: ${detail}` : ""}`);
120
+ }
121
+ const outs = readVec(outVec);
122
+ C.mlx_vector_array_free(outVec);
123
+ C.mlx_vector_array_free(inVec);
124
+ return outs;
125
+ }
126
+
127
+ dispose(): void {
128
+ if (this.#disposed) return;
129
+ this.#disposed = true;
130
+ C.mlx_closure_free(this.#combined);
131
+ C.mlx_closure_free(this.#fwdClosure);
132
+ C.mlx_closure_custom_free(this.#vjpClosure);
133
+ this.#fwdCb.close();
134
+ this.#vjpCb.close();
135
+ }
136
+
137
+ [Symbol.dispose](): void {
138
+ this.dispose();
139
+ }
140
+ }