@seanhogg/builderforce-memory-engine 2026.7.14 → 2026.9.28

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 (186) hide show
  1. package/README.md +14 -0
  2. package/dist/bench/adaptation.d.ts +57 -0
  3. package/dist/bench/adaptation.d.ts.map +1 -0
  4. package/dist/bench/adaptation.js +167 -0
  5. package/dist/bench/adaptation.js.map +1 -0
  6. package/dist/bench/harness.d.ts +2 -0
  7. package/dist/bench/harness.d.ts.map +1 -1
  8. package/dist/bench/harness.js +1 -1
  9. package/dist/bench/harness.js.map +1 -1
  10. package/dist/bench/index.d.ts +5 -3
  11. package/dist/bench/index.d.ts.map +1 -1
  12. package/dist/bench/index.js +4 -2
  13. package/dist/bench/index.js.map +1 -1
  14. package/dist/bench/types.d.ts +88 -0
  15. package/dist/bench/types.d.ts.map +1 -1
  16. package/dist/evermind/adapt.d.ts +40 -0
  17. package/dist/evermind/adapt.d.ts.map +1 -0
  18. package/dist/evermind/adapt.js +25 -0
  19. package/dist/evermind/adapt.js.map +1 -0
  20. package/dist/evermind/delta_wire.d.ts +43 -0
  21. package/dist/evermind/delta_wire.d.ts.map +1 -0
  22. package/dist/evermind/delta_wire.js +94 -0
  23. package/dist/evermind/delta_wire.js.map +1 -0
  24. package/dist/evermind/eval.d.ts +31 -0
  25. package/dist/evermind/eval.d.ts.map +1 -0
  26. package/dist/evermind/eval.js +36 -0
  27. package/dist/evermind/eval.js.map +1 -0
  28. package/dist/evermind/index.d.ts +16 -0
  29. package/dist/evermind/index.d.ts.map +1 -0
  30. package/dist/evermind/index.js +11 -0
  31. package/dist/evermind/index.js.map +1 -0
  32. package/dist/evermind/merge.d.ts +21 -0
  33. package/dist/evermind/merge.d.ts.map +1 -0
  34. package/dist/evermind/merge.js +79 -0
  35. package/dist/evermind/merge.js.map +1 -0
  36. package/dist/{moe/moe_package.d.ts → evermind/package.d.ts} +55 -5
  37. package/dist/evermind/package.d.ts.map +1 -0
  38. package/dist/{moe/moe_package.js → evermind/package.js} +118 -19
  39. package/dist/evermind/package.js.map +1 -0
  40. package/dist/import/foreign/adapter.d.ts +59 -0
  41. package/dist/import/foreign/adapter.d.ts.map +1 -0
  42. package/dist/import/foreign/adapter.js +76 -0
  43. package/dist/import/foreign/adapter.js.map +1 -0
  44. package/dist/import/foreign/apply.d.ts +23 -0
  45. package/dist/import/foreign/apply.d.ts.map +1 -0
  46. package/dist/import/foreign/apply.js +43 -0
  47. package/dist/import/foreign/apply.js.map +1 -0
  48. package/dist/import/foreign/codestral_mamba.d.ts +57 -0
  49. package/dist/import/foreign/codestral_mamba.d.ts.map +1 -0
  50. package/dist/import/foreign/codestral_mamba.js +174 -0
  51. package/dist/import/foreign/codestral_mamba.js.map +1 -0
  52. package/dist/import/foreign/falcon_mamba.d.ts +51 -0
  53. package/dist/import/foreign/falcon_mamba.d.ts.map +1 -0
  54. package/dist/import/foreign/falcon_mamba.js +131 -0
  55. package/dist/import/foreign/falcon_mamba.js.map +1 -0
  56. package/dist/import/foreign/index.d.ts +21 -0
  57. package/dist/import/foreign/index.d.ts.map +1 -0
  58. package/dist/import/foreign/index.js +18 -0
  59. package/dist/import/foreign/index.js.map +1 -0
  60. package/dist/import/foreign/plan.d.ts +122 -0
  61. package/dist/import/foreign/plan.d.ts.map +1 -0
  62. package/dist/import/foreign/plan.js +171 -0
  63. package/dist/import/foreign/plan.js.map +1 -0
  64. package/dist/import/foreign/registry.d.ts +35 -0
  65. package/dist/import/foreign/registry.d.ts.map +1 -0
  66. package/dist/import/foreign/registry.js +89 -0
  67. package/dist/import/foreign/registry.js.map +1 -0
  68. package/dist/import/index.d.ts +7 -0
  69. package/dist/import/index.d.ts.map +1 -1
  70. package/dist/import/index.js +7 -0
  71. package/dist/import/index.js.map +1 -1
  72. package/dist/index.d.ts +25 -12
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +21 -9
  75. package/dist/index.js.map +1 -1
  76. package/dist/kernels/selective_scan.d.ts.map +1 -1
  77. package/dist/kernels/selective_scan.js +15 -9
  78. package/dist/kernels/selective_scan.js.map +1 -1
  79. package/dist/kernels/slice.d.ts +26 -0
  80. package/dist/kernels/slice.d.ts.map +1 -0
  81. package/dist/kernels/slice.js +52 -0
  82. package/dist/kernels/slice.js.map +1 -0
  83. package/dist/kernels/weight_update.d.ts.map +1 -1
  84. package/dist/kernels/weight_update.js +30 -4
  85. package/dist/kernels/weight_update.js.map +1 -1
  86. package/dist/lm/evermind_lm.d.ts +110 -1
  87. package/dist/lm/evermind_lm.d.ts.map +1 -1
  88. package/dist/lm/evermind_lm.js +220 -35
  89. package/dist/lm/evermind_lm.js.map +1 -1
  90. package/dist/lm/index.d.ts +4 -2
  91. package/dist/lm/index.d.ts.map +1 -1
  92. package/dist/lm/index.js +3 -1
  93. package/dist/lm/index.js.map +1 -1
  94. package/dist/lm/text_embedder.d.ts +55 -0
  95. package/dist/lm/text_embedder.d.ts.map +1 -0
  96. package/dist/lm/text_embedder.js +62 -0
  97. package/dist/lm/text_embedder.js.map +1 -0
  98. package/dist/model/attention_block.d.ts.map +1 -1
  99. package/dist/model/attention_block.js +9 -5
  100. package/dist/model/attention_block.js.map +1 -1
  101. package/dist/model/mamba1_block.d.ts.map +1 -1
  102. package/dist/model/mamba1_block.js +28 -12
  103. package/dist/model/mamba1_block.js.map +1 -1
  104. package/dist/model/mamba2_block.d.ts.map +1 -1
  105. package/dist/model/mamba2_block.js +14 -9
  106. package/dist/model/mamba2_block.js.map +1 -1
  107. package/dist/model/mamba3_block.d.ts.map +1 -1
  108. package/dist/model/mamba3_block.js +14 -9
  109. package/dist/model/mamba3_block.js.map +1 -1
  110. package/dist/moe/index.d.ts +0 -2
  111. package/dist/moe/index.d.ts.map +1 -1
  112. package/dist/moe/index.js +0 -1
  113. package/dist/moe/index.js.map +1 -1
  114. package/dist/optim/adamw.d.ts +32 -0
  115. package/dist/optim/adamw.d.ts.map +1 -1
  116. package/dist/optim/adamw.js +34 -0
  117. package/dist/optim/adamw.js.map +1 -1
  118. package/dist/tokenizer/bpe.d.ts +28 -0
  119. package/dist/tokenizer/bpe.d.ts.map +1 -1
  120. package/dist/tokenizer/bpe.js +47 -0
  121. package/dist/tokenizer/bpe.js.map +1 -1
  122. package/dist/training/cpu_ops.d.ts +44 -0
  123. package/dist/training/cpu_ops.d.ts.map +1 -0
  124. package/dist/training/cpu_ops.js +111 -0
  125. package/dist/training/cpu_ops.js.map +1 -0
  126. package/dist/training/mamba1_cpu.d.ts +109 -0
  127. package/dist/training/mamba1_cpu.d.ts.map +1 -0
  128. package/dist/training/mamba1_cpu.js +316 -0
  129. package/dist/training/mamba1_cpu.js.map +1 -0
  130. package/dist/training/model_cpu.d.ts +100 -0
  131. package/dist/training/model_cpu.d.ts.map +1 -0
  132. package/dist/training/model_cpu.js +252 -0
  133. package/dist/training/model_cpu.js.map +1 -0
  134. package/dist/training/trainer.d.ts +156 -16
  135. package/dist/training/trainer.d.ts.map +1 -1
  136. package/dist/training/trainer.js +327 -101
  137. package/dist/training/trainer.js.map +1 -1
  138. package/dist/utils/base64.d.ts +12 -0
  139. package/dist/utils/base64.d.ts.map +1 -0
  140. package/dist/utils/base64.js +27 -0
  141. package/dist/utils/base64.js.map +1 -0
  142. package/package.json +3 -1
  143. package/src/bench/adaptation.ts +180 -0
  144. package/src/bench/harness.ts +1 -1
  145. package/src/bench/index.ts +13 -2
  146. package/src/bench/types.ts +93 -0
  147. package/src/evermind/adapt.ts +61 -0
  148. package/src/evermind/delta_wire.ts +114 -0
  149. package/src/evermind/eval.ts +62 -0
  150. package/src/evermind/index.ts +32 -0
  151. package/src/evermind/merge.ts +106 -0
  152. package/src/{moe/moe_package.ts → evermind/package.ts} +148 -17
  153. package/src/import/foreign/adapter.ts +106 -0
  154. package/src/import/foreign/apply.ts +54 -0
  155. package/src/import/foreign/codestral_mamba.ts +224 -0
  156. package/src/import/foreign/falcon_mamba.ts +173 -0
  157. package/src/import/foreign/index.ts +37 -0
  158. package/src/import/foreign/plan.ts +294 -0
  159. package/src/import/foreign/registry.ts +126 -0
  160. package/src/import/index.ts +30 -0
  161. package/src/index.ts +94 -13
  162. package/src/kernels/selective_scan.ts +15 -9
  163. package/src/kernels/slice.ts +64 -0
  164. package/src/kernels/weight_update.ts +30 -4
  165. package/src/lm/evermind_lm.ts +244 -34
  166. package/src/lm/index.ts +6 -2
  167. package/src/lm/text_embedder.ts +84 -0
  168. package/src/model/attention_block.ts +9 -5
  169. package/src/model/mamba1_block.ts +28 -12
  170. package/src/model/mamba2_block.ts +14 -9
  171. package/src/model/mamba3_block.ts +14 -9
  172. package/src/moe/index.ts +0 -9
  173. package/src/optim/adamw.ts +60 -0
  174. package/src/tokenizer/bpe.ts +55 -0
  175. package/src/training/cpu_ops.ts +127 -0
  176. package/src/training/mamba1_cpu.ts +421 -0
  177. package/src/training/model_cpu.ts +360 -0
  178. package/src/training/trainer.ts +397 -154
  179. package/src/utils/base64.ts +28 -0
  180. package/dist/model/mamba_block.d.ts +0 -64
  181. package/dist/model/mamba_block.d.ts.map +0 -1
  182. package/dist/model/mamba_block.js +0 -303
  183. package/dist/model/mamba_block.js.map +0 -1
  184. package/dist/moe/moe_package.d.ts.map +0 -1
  185. package/dist/moe/moe_package.js.map +0 -1
  186. package/src/model/mamba_block.ts +0 -401
@@ -20,6 +20,7 @@ import { gaussianArray } from '../utils/rng.js';
20
20
  import { CONV1D_FORWARD_WGSL } from '../kernels/conv1d.js';
21
21
  import { LINEAR_FORWARD_WGSL } from '../kernels/linear_projection.js';
22
22
  import { ACTIVATIONS_WGSL } from '../kernels/activations.js';
23
+ import { COL_SLICE_WGSL, COL_SLICE_ENTRY, dispatchColumnSlice } from '../kernels/slice.js';
23
24
 
24
25
  import type { SequenceLayer, LayerForwardResult, LayerParam } from './sequence_layer.js';
25
26
 
@@ -210,6 +211,7 @@ export class Mamba1Block implements SequenceLayer {
210
211
  rmsnorm : createComputePipeline(d, ACTIVATIONS_WGSL, 'rmsnorm_forward'),
211
212
  scan_fwd : createComputePipeline(d, SELECTIVE_SCAN_FORWARD_WGSL, 'forward_scan'),
212
213
  scan_reduce : createComputePipeline(d, SELECTIVE_SCAN_FORWARD_WGSL, 'forward_reduce'),
214
+ colSlice : createComputePipeline(d, COL_SLICE_WGSL, COL_SLICE_ENTRY),
213
215
  elMul : createComputePipeline(d, MUL_SHADER, 'main'),
214
216
  elAdd : createComputePipeline(d, ADD_SHADER, 'main'),
215
217
  };
@@ -253,14 +255,18 @@ export class Mamba1Block implements SequenceLayer {
253
255
  dispatchKernel(d, this.pipelines['linear']!, bg, [cdiv(M, 16), cdiv(2 * D, 16), 1]);
254
256
  }
255
257
 
256
- // 3. Split into x and z
258
+ // 3. Split into x and z.
259
+ // in_proj writes (M, 2D) ROW-major, so x and z are interleaved COLUMN
260
+ // slices — one per row — not two contiguous halves of the buffer. A
261
+ // `copyBufferToBuffer` of the first M*D floats is only the right answer
262
+ // when M == 1; for any real sequence it hands the block the first half of
263
+ // the ROWS twice. Split with a strided gather instead.
257
264
  const xConvIn = createEmptyStorageBuffer(d, M * D * 4, true);
258
265
  const zBuf = createEmptyStorageBuffer(d, M * D * 4, true);
259
266
  {
260
- const enc = d.createCommandEncoder();
261
- enc.copyBufferToBuffer(inProjOut, 0, xConvIn, 0, M * D * 4);
262
- enc.copyBufferToBuffer(inProjOut, M * D * 4, zBuf, 0, M * D * 4);
263
- d.queue.submit([enc.finish()]);
267
+ const slice = this.pipelines['colSlice']!;
268
+ dispatchColumnSlice(d, slice, inProjOut, xConvIn, M, 2 * D, 0, D);
269
+ dispatchColumnSlice(d, slice, inProjOut, zBuf, M, 2 * D, D, D);
264
270
  }
265
271
  inProjOut.destroy();
266
272
  cache.zBuf = zBuf;
@@ -270,7 +276,11 @@ export class Mamba1Block implements SequenceLayer {
270
276
  const convOut = createEmptyStorageBuffer(d, M * D * 4, true);
271
277
  cache.convOut = convOut;
272
278
  {
273
- const params = new Uint32Array([L, D, dConv, B]).buffer;
279
+ // ConvParams is FIVE u32s (seq_len, d_channels, kernel_size, batch,
280
+ // groups). A four-element uniform is smaller than the struct's minimum
281
+ // binding size and WebGPU rejects the bind group outright. groups = 1
282
+ // is standard depthwise, matching Mamba-2/3's call.
283
+ const params = new Uint32Array([L, D, dConv, B, 1]).buffer;
274
284
  const pBuf = createUniformBuffer(d, params);
275
285
  const bg = createBindGroup(d, this.pipelines['conv1d']!,
276
286
  [pBuf, xConvIn, this.gpuWeights['wConv']!, this.gpuWeights['bConv']!, convOut]);
@@ -298,15 +308,17 @@ export class Mamba1Block implements SequenceLayer {
298
308
  dispatchKernel(d, this.pipelines['linear']!, bg, [cdiv(M, 16), cdiv(R + 2 * N, 16), 1]);
299
309
  }
300
310
 
311
+ // Same story as the (x, z) split: Δ, B and C are COLUMN slices of the
312
+ // (M, R+2N) projection, so they need a strided gather, not a prefix copy.
301
313
  const dtRaw = createEmptyStorageBuffer(d, M * R * 4, true);
302
314
  const B_raw = createEmptyStorageBuffer(d, B * L * N * 4, true);
303
315
  const C_raw = createEmptyStorageBuffer(d, B * L * N * 4, true);
304
316
  {
305
- const enc = d.createCommandEncoder();
306
- enc.copyBufferToBuffer(xProjOut, 0, dtRaw, 0, M * R * 4);
307
- enc.copyBufferToBuffer(xProjOut, M * R * 4, B_raw, 0, B * L * N * 4);
308
- enc.copyBufferToBuffer(xProjOut, M * (R + N) * 4, C_raw, 0, B * L * N * 4);
309
- d.queue.submit([enc.finish()]);
317
+ const P = R + 2 * N;
318
+ const slice = this.pipelines['colSlice']!;
319
+ dispatchColumnSlice(d, slice, xProjOut, dtRaw, M, P, 0, R);
320
+ dispatchColumnSlice(d, slice, xProjOut, B_raw, M, P, R, N);
321
+ dispatchColumnSlice(d, slice, xProjOut, C_raw, M, P, R + N, N);
310
322
  }
311
323
  xProjOut.destroy();
312
324
  cache.B_raw = B_raw;
@@ -335,7 +347,11 @@ export class Mamba1Block implements SequenceLayer {
335
347
  const bg1 = createBindGroup(d, this.pipelines['scan_fwd']!,
336
348
  [pBuf, siluOut, deltaFull, this.gpuWeights['A_log']!, B_raw, C_raw,
337
349
  this.gpuWeights['D_vec']!, scanY, hCache]);
338
- dispatchKernel(d, this.pipelines['scan_fwd']!, bg1, [cdiv(D, 8), cdiv(N, 8), B]);
350
+ // One workgroup per (d, n, batch): the kernel reads `wgid.x` as d and
351
+ // `wgid.y` as n, so the grid must be D x N x B. Dispatching
352
+ // ceil(D/8) x ceil(N/8) left all but a 1/64th corner of the state
353
+ // matrix unscanned (and unwritten in h_cache).
354
+ dispatchKernel(d, this.pipelines['scan_fwd']!, bg1, [D, N, B]);
339
355
 
340
356
  const bg2 = createBindGroup(d, this.pipelines['scan_reduce']!,
341
357
  [pBuf, siluOut, deltaFull, this.gpuWeights['A_log']!, B_raw, C_raw,
@@ -24,6 +24,7 @@ import {
24
24
  import { SSD_FORWARD_WGSL } from '../kernels/ssd.js';
25
25
  import { gaussianArray } from '../utils/rng.js';
26
26
  import { CONV1D_FORWARD_WGSL } from '../kernels/conv1d.js';
27
+ import { COL_SLICE_WGSL, COL_SLICE_ENTRY, dispatchColumnSlice } from '../kernels/slice.js';
27
28
  import { LINEAR_FORWARD_WGSL } from '../kernels/linear_projection.js';
28
29
  import { ACTIVATIONS_WGSL } from '../kernels/activations.js';
29
30
 
@@ -127,6 +128,7 @@ export class Mamba2Block implements SequenceLayer {
127
128
  this.pipelines = {
128
129
  linear : createComputePipeline(d, LINEAR_FORWARD_WGSL, 'linear_forward'),
129
130
  conv1d : createComputePipeline(d, CONV1D_FORWARD_WGSL, 'conv1d_forward'),
131
+ colSlice : createComputePipeline(d, COL_SLICE_WGSL, COL_SLICE_ENTRY),
130
132
  rmsnorm : createComputePipeline(d, ACTIVATIONS_WGSL, 'rmsnorm_forward'),
131
133
  ssd_fwd : createComputePipeline(d, SSD_FORWARD_WGSL, 'ssd_chunk_forward'),
132
134
  elAdd : createComputePipeline(d, ADD_SHADER, 'main'),
@@ -181,10 +183,13 @@ export class Mamba2Block implements SequenceLayer {
181
183
  const xConvBuf = createEmptyStorageBuffer(d, M * convD * 4, true);
182
184
  const dtBuf = createEmptyStorageBuffer(d, M * H * 4, true);
183
185
  {
184
- const enc = d.createCommandEncoder();
185
- enc.copyBufferToBuffer(inProjOut, 0, xConvBuf, 0, M * convD * 4);
186
- enc.copyBufferToBuffer(inProjOut, M * convD * 4, dtBuf, 0, M * H * 4);
187
- d.queue.submit([enc.finish()]);
186
+ // COLUMN split. `linear_forward` writes (M, convD + H) ROW-major, so each
187
+ // part is a range of columns WITHIN every row — a strided gather, not a
188
+ // contiguous copy (which only coincides for M == 1).
189
+ const slice = this.pipelines['colSlice']!;
190
+ const stride = convD + H;
191
+ dispatchColumnSlice(d, slice, inProjOut, xConvBuf, M, stride, 0, convD);
192
+ dispatchColumnSlice(d, slice, inProjOut, dtBuf, M, stride, convD, H);
188
193
  }
189
194
  inProjOut.destroy();
190
195
 
@@ -204,11 +209,11 @@ export class Mamba2Block implements SequenceLayer {
204
209
  const bProjBuf = createEmptyStorageBuffer(d, M * G * N * 4, true);
205
210
  const cProjBuf = createEmptyStorageBuffer(d, M * G * N * 4, true);
206
211
  {
207
- const enc = d.createCommandEncoder();
208
- enc.copyBufferToBuffer(convOut, 0, xSsdBuf, 0, M * D * 4);
209
- enc.copyBufferToBuffer(convOut, M * D * 4, bProjBuf, 0, M * G * N * 4);
210
- enc.copyBufferToBuffer(convOut, M * (D + G * N) * 4, cProjBuf, 0, M * G * N * 4);
211
- d.queue.submit([enc.finish()]);
212
+ // COLUMN split of the (M, convD) conv output — same reasoning as above.
213
+ const slice = this.pipelines['colSlice']!;
214
+ dispatchColumnSlice(d, slice, convOut, xSsdBuf, M, convD, 0, D);
215
+ dispatchColumnSlice(d, slice, convOut, bProjBuf, M, convD, D, G * N);
216
+ dispatchColumnSlice(d, slice, convOut, cProjBuf, M, convD, D + G * N, G * N);
212
217
  }
213
218
  convOut.destroy();
214
219
 
@@ -33,6 +33,7 @@ import {
33
33
  import { COMPLEX_SSD_FORWARD_WGSL } from '../kernels/complex_ssd.js';
34
34
  import { gaussianArray } from '../utils/rng.js';
35
35
  import { CONV1D_FORWARD_WGSL } from '../kernels/conv1d.js';
36
+ import { COL_SLICE_WGSL, COL_SLICE_ENTRY, dispatchColumnSlice } from '../kernels/slice.js';
36
37
  import { LINEAR_FORWARD_WGSL } from '../kernels/linear_projection.js';
37
38
  import { ACTIVATIONS_WGSL } from '../kernels/activations.js';
38
39
 
@@ -145,6 +146,7 @@ export class Mamba3Block implements SequenceLayer {
145
146
  this.pipelines = {
146
147
  linear : createComputePipeline(d, LINEAR_FORWARD_WGSL, 'linear_forward'),
147
148
  conv1d : createComputePipeline(d, CONV1D_FORWARD_WGSL, 'conv1d_forward'),
149
+ colSlice : createComputePipeline(d, COL_SLICE_WGSL, COL_SLICE_ENTRY),
148
150
  rmsnorm : createComputePipeline(d, ACTIVATIONS_WGSL, 'rmsnorm_forward'),
149
151
  cssd_fwd : createComputePipeline(d, COMPLEX_SSD_FORWARD_WGSL, 'complex_ssd_forward'),
150
152
  elAdd : createComputePipeline(d, ADD_SHADER, 'main'),
@@ -198,10 +200,13 @@ export class Mamba3Block implements SequenceLayer {
198
200
  const xConvBuf = createEmptyStorageBuffer(d, M * convD * 4, true);
199
201
  const dtBuf = createEmptyStorageBuffer(d, M * H * 4, true);
200
202
  {
201
- const enc = d.createCommandEncoder();
202
- enc.copyBufferToBuffer(inProjOut, 0, xConvBuf, 0, M * convD * 4);
203
- enc.copyBufferToBuffer(inProjOut, M * convD * 4, dtBuf, 0, M * H * 4);
204
- d.queue.submit([enc.finish()]);
203
+ // COLUMN split. `linear_forward` writes (M, convD + H) ROW-major, so each
204
+ // part is a range of columns WITHIN every row — a strided gather, not a
205
+ // contiguous copy (which only coincides for M == 1).
206
+ const slice = this.pipelines['colSlice']!;
207
+ const stride = convD + H;
208
+ dispatchColumnSlice(d, slice, inProjOut, xConvBuf, M, stride, 0, convD);
209
+ dispatchColumnSlice(d, slice, inProjOut, dtBuf, M, stride, convD, H);
205
210
  }
206
211
  inProjOut.destroy();
207
212
 
@@ -221,11 +226,11 @@ export class Mamba3Block implements SequenceLayer {
221
226
  const bProjBuf = createEmptyStorageBuffer(d, M * G * Nc * 2 * 4, true);
222
227
  const cProjBuf = createEmptyStorageBuffer(d, M * G * Nc * 2 * 4, true);
223
228
  {
224
- const enc = d.createCommandEncoder();
225
- enc.copyBufferToBuffer(convOut, 0, xSsdBuf, 0, M * D * 4);
226
- enc.copyBufferToBuffer(convOut, M * D * 4, bProjBuf, 0, M * G * Nc * 2 * 4);
227
- enc.copyBufferToBuffer(convOut, M * (D + G * Nc * 2) * 4, cProjBuf, 0, M * G * Nc * 2 * 4);
228
- d.queue.submit([enc.finish()]);
229
+ // COLUMN split of the (M, convD) conv output — same reasoning as above.
230
+ const slice = this.pipelines['colSlice']!;
231
+ dispatchColumnSlice(d, slice, convOut, xSsdBuf, M, convD, 0, D);
232
+ dispatchColumnSlice(d, slice, convOut, bProjBuf, M, convD, D, G * Nc * 2);
233
+ dispatchColumnSlice(d, slice, convOut, cProjBuf, M, convD, D + G * Nc * 2, G * Nc * 2);
229
234
  }
230
235
  convOut.destroy();
231
236
 
package/src/moe/index.ts CHANGED
@@ -13,12 +13,3 @@ export type { MoEConfig, MoEParam, RouteResult } from "./moe_model.js";
13
13
  export { MoETrainer } from "./moe_trainer.js";
14
14
  export type { MoESample, MoETrainOptions, MoEEpochResult } from "./moe_trainer.js";
15
15
 
16
- export { EvermindModelPackage } from "./moe_package.js";
17
- export type {
18
- EvermindModelManifest,
19
- EvermindModelCard,
20
- EvermindModelType,
21
- EvermindModality,
22
- PackageMeta,
23
- ValidationResult,
24
- } from "./moe_package.js";
@@ -118,3 +118,63 @@ export class AdamW {
118
118
  return n;
119
119
  }
120
120
  }
121
+
122
+ // ── The CPU mirror of WEIGHT_UPDATE_WGSL ─────────────────────────────────────
123
+
124
+ /** Hyperparameters of one {@link adamwUpdateInPlace} step. */
125
+ export interface AdamWKernelStep {
126
+ lr: number;
127
+ beta1: number;
128
+ beta2: number;
129
+ eps: number;
130
+ weightDecay: number;
131
+ /** beta1^t — the precomputed bias-correction term (matches the kernel uniform). */
132
+ beta1_t: number;
133
+ /** beta2^t. */
134
+ beta2_t: number;
135
+ /** Trust region: max |Δθ| per step. 0 disables the bound. */
136
+ maxDelta: number;
137
+ /** Global gradient-clip factor, applied to `grad` on the fly (1 = no clipping). */
138
+ gradScale?: number;
139
+ }
140
+
141
+ /**
142
+ * One AdamW step over a single parameter tensor — the exact CPU mirror of the
143
+ * `adamw_update` entry point in `WEIGHT_UPDATE_WGSL`, including its two safety
144
+ * properties:
145
+ *
146
+ * • the **NaN/Inf guard** — a non-finite step is dropped rather than written
147
+ * into a weight, so one bad gradient cannot permanently poison the model;
148
+ * • the **trust region** — `maxDelta` bounds how far a single step can move a
149
+ * weight, which is what keeps repeated write-through adapts reversible.
150
+ *
151
+ * Decoupled weight decay (`θ * (1 - lr·wd)`), matching the kernel — deliberately
152
+ * NOT the coupled form {@link AdamW} uses for the MoE/EvermindLM trainers.
153
+ *
154
+ * `param`, `m` and `v` are updated in place.
155
+ */
156
+ export function adamwUpdateInPlace(
157
+ param: Float32Array,
158
+ grad: Float32Array,
159
+ m: Float32Array,
160
+ v: Float32Array,
161
+ hp: AdamWKernelStep,
162
+ ): void {
163
+ const { lr, beta1, beta2, eps, weightDecay, beta1_t, beta2_t, maxDelta } = hp;
164
+ const gradScale = hp.gradScale ?? 1;
165
+ const bc1 = 1 - beta1_t;
166
+ const bc2 = 1 - beta2_t;
167
+ for (let i = 0; i < param.length; i++) {
168
+ const g = grad[i]! * gradScale;
169
+ const mNew = beta1 * m[i]! + (1 - beta1) * g;
170
+ const vNew = beta2 * v[i]! + (1 - beta2) * g * g;
171
+ m[i] = mNew;
172
+ v[i] = vNew;
173
+
174
+ let step = (lr * (mNew / bc1)) / (Math.sqrt(vNew / bc2) + eps);
175
+ if (!Number.isFinite(step)) step = 0;
176
+ if (maxDelta > 0) step = Math.min(maxDelta, Math.max(-maxDelta, step));
177
+
178
+ param[i] = param[i]! * (1 - lr * weightDecay) - step;
179
+ }
180
+ }
@@ -52,6 +52,14 @@ const BYTE_DECODER = new Map([...BYTE_ENCODER].map(([k, v]) => [v, k]));
52
52
  const PRE_TOKENIZE_RE =
53
53
  /(?:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+/gu;
54
54
 
55
+ /** A round-trippable snapshot of a {@link BPETokenizer} — see `toObject`/`deserialize`. */
56
+ export interface BPETokenizerSpec {
57
+ vocab: Record<string, number>;
58
+ /** Merge rules in rank order ("a b" strings). */
59
+ merges: string[];
60
+ specials?: { bos?: string; eos?: string; pad?: string; unk?: string };
61
+ }
62
+
55
63
  export class BPETokenizer {
56
64
  vocab: Map<string, number>;
57
65
  idToToken: Map<number, string>;
@@ -225,6 +233,53 @@ export class BPETokenizer {
225
233
  this.loadFromObjects(vocabObj, mergeArr);
226
234
  }
227
235
 
236
+ /**
237
+ * The tokenizer as a plain, JSON-serialisable object: vocab, merges and the
238
+ * four special-token strings.
239
+ *
240
+ * This is what lets an `.evermind` package EMBED its tokenizer instead of
241
+ * expecting a consumer to source the matching vocab separately — token ids are
242
+ * meaningless without the exact vocabulary that produced them, so shipping a
243
+ * checkpoint without one is shipping half an artifact.
244
+ */
245
+ toObject(): BPETokenizerSpec {
246
+ return {
247
+ vocab: Object.fromEntries(this.vocab),
248
+ merges: [...this.merges.entries()].sort((a, b) => a[1] - b[1]).map(([m]) => m),
249
+ specials: { bos: this.bosToken, eos: this.eosToken, pad: this.padToken, unk: this.unkToken },
250
+ };
251
+ }
252
+
253
+ /** Restore a tokenizer from {@link toObject}. */
254
+ loadFromSpec(spec: BPETokenizerSpec): void {
255
+ const sp = spec.specials;
256
+ if (sp?.bos) this.bosToken = sp.bos;
257
+ if (sp?.eos) this.eosToken = sp.eos;
258
+ if (sp?.pad) this.padToken = sp.pad;
259
+ if (sp?.unk) this.unkToken = sp.unk;
260
+ this.loadFromObjects(spec.vocab, spec.merges);
261
+ }
262
+
263
+ /** UTF-8 JSON bytes of {@link toObject} — the `.evermind` tokenizer section. */
264
+ serialize(): ArrayBuffer {
265
+ const bytes = new TextEncoder().encode(JSON.stringify(this.toObject()));
266
+ // Copy into a standalone ArrayBuffer (the view may be a slice of a pool).
267
+ const out = new ArrayBuffer(bytes.byteLength);
268
+ new Uint8Array(out).set(bytes);
269
+ return out;
270
+ }
271
+
272
+ /** Inverse of {@link serialize}. */
273
+ static deserialize(buffer: ArrayBuffer): BPETokenizer {
274
+ const spec = JSON.parse(new TextDecoder().decode(new Uint8Array(buffer))) as BPETokenizerSpec;
275
+ if (!spec || typeof spec.vocab !== 'object' || !Array.isArray(spec.merges)) {
276
+ throw new Error('BPETokenizer.deserialize: not a serialised BPE tokenizer (expected { vocab, merges })');
277
+ }
278
+ const tok = new BPETokenizer();
279
+ tok.loadFromSpec(spec);
280
+ return tok;
281
+ }
282
+
228
283
  /**
229
284
  * Fetch and import a Hugging Face `tokenizer.json` by URL — the IDE's
230
285
  * "import merges from a repo" path. Resolves once the tokenizer is loaded.
@@ -0,0 +1,127 @@
1
+ /**
2
+ * cpu_ops.ts — the small set of exact CPU primitives the reference forward and
3
+ * backward passes share (linear, RMSNorm, SiLU, softplus).
4
+ *
5
+ * Each one mirrors a WGSL kernel exactly, so a gradient computed here is the
6
+ * gradient of the function the GPU actually evaluates. They are deliberately
7
+ * plain loops over `Float32Array`: correctness first, and small enough that the
8
+ * finite-difference check in `tests/backprop.test.ts` is meaningful.
9
+ */
10
+
11
+ /** Epsilon used by `rmsnorm_forward` in `ACTIVATIONS_WGSL`. */
12
+ export const RMSNORM_EPS = 1e-6;
13
+
14
+ export function sigmoid(x: number): number {
15
+ return 1 / (1 + Math.exp(-x));
16
+ }
17
+
18
+ /** SiLU(x) = x * sigmoid(x) — mirrors `silu_forward`. */
19
+ export function silu(x: number): number {
20
+ return x / (1 + Math.exp(-x));
21
+ }
22
+
23
+ /** d/dx SiLU(x) = sigmoid(x) * (1 + x * (1 - sigmoid(x))). */
24
+ export function siluGrad(x: number): number {
25
+ const s = sigmoid(x);
26
+ return s * (1 + x * (1 - s));
27
+ }
28
+
29
+ /** Numerically stable softplus — mirrors the kernel's `max(x,0) + log1p(exp(-|x|))`. */
30
+ export function softplus(x: number): number {
31
+ return Math.max(x, 0) + Math.log1p(Math.exp(-Math.abs(x)));
32
+ }
33
+
34
+ /**
35
+ * Y = X @ W^T + b, mirroring `linear_forward`.
36
+ *
37
+ * @param X (M, K)
38
+ * @param W (N, K) row-major
39
+ * @param b (N,)
40
+ * @param out (M, N) — written, not accumulated
41
+ */
42
+ export function linearForward(
43
+ X: Float32Array, W: Float32Array, b: Float32Array,
44
+ M: number, K: number, N: number, out: Float32Array,
45
+ ): void {
46
+ for (let r = 0; r < M; r++) {
47
+ const xo = r * K;
48
+ const yo = r * N;
49
+ for (let n = 0; n < N; n++) {
50
+ let acc = b[n]!;
51
+ const wo = n * K;
52
+ for (let k = 0; k < K; k++) acc += X[xo + k]! * W[wo + k]!;
53
+ out[yo + n] = acc;
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Backward of {@link linearForward}. `dX` is written; `dW` and `db` are
60
+ * ACCUMULATED into (so one parameter can collect gradient from several call
61
+ * sites without a temporary).
62
+ */
63
+ export function linearBackward(
64
+ dY: Float32Array, X: Float32Array, W: Float32Array,
65
+ M: number, K: number, N: number,
66
+ dX: Float32Array, dW: Float32Array, db: Float32Array,
67
+ ): void {
68
+ dX.fill(0);
69
+ for (let r = 0; r < M; r++) {
70
+ const xo = r * K;
71
+ const yo = r * N;
72
+ for (let n = 0; n < N; n++) {
73
+ const g = dY[yo + n]!;
74
+ if (g === 0) continue;
75
+ db[n] = db[n]! + g;
76
+ const wo = n * K;
77
+ for (let k = 0; k < K; k++) {
78
+ dX[xo + k] = dX[xo + k]! + g * W[wo + k]!;
79
+ dW[wo + k] = dW[wo + k]! + g * X[xo + k]!;
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ /**
86
+ * RMSNorm forward — mirrors `rmsnorm_forward`:
87
+ * inv[r] = 1 / sqrt(mean(x[r]²) + eps); y[r,i] = x[r,i] * inv[r] * w[i]
88
+ */
89
+ export function rmsNormForward(
90
+ x: Float32Array, w: Float32Array, M: number, D: number,
91
+ out: Float32Array, inv: Float32Array, eps: number = RMSNORM_EPS,
92
+ ): void {
93
+ for (let r = 0; r < M; r++) {
94
+ const base = r * D;
95
+ let sq = 0;
96
+ for (let i = 0; i < D; i++) { const v = x[base + i]!; sq += v * v; }
97
+ const iv = 1 / Math.sqrt(sq / D + eps);
98
+ inv[r] = iv;
99
+ for (let i = 0; i < D; i++) out[base + i] = x[base + i]! * iv * w[i]!;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * RMSNorm backward. `dx` and `dw` are ACCUMULATED into — `dx` in particular is
105
+ * usually the residual path, which already carries a gradient.
106
+ */
107
+ export function rmsNormBackward(
108
+ dY: Float32Array, x: Float32Array, w: Float32Array, inv: Float32Array,
109
+ M: number, D: number,
110
+ dx: Float32Array, dw: Float32Array, _eps: number = RMSNORM_EPS,
111
+ ): void {
112
+ for (let r = 0; r < M; r++) {
113
+ const base = r * D;
114
+ const iv = inv[r]!;
115
+ let dot = 0; // Σ_i (dY[r,i] * w[i]) * x[r,i]
116
+ for (let i = 0; i < D; i++) {
117
+ const g = dY[base + i]! * w[i]!;
118
+ dot += g * x[base + i]!;
119
+ dw[i] = dw[i]! + dY[base + i]! * x[base + i]! * iv;
120
+ }
121
+ const scale = (iv * iv * iv) / D;
122
+ for (let i = 0; i < D; i++) {
123
+ const g = dY[base + i]! * w[i]!;
124
+ dx[base + i] = dx[base + i]! + g * iv - scale * dot * x[base + i]!;
125
+ }
126
+ }
127
+ }