@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.
- package/README.md +14 -0
- package/dist/bench/adaptation.d.ts +57 -0
- package/dist/bench/adaptation.d.ts.map +1 -0
- package/dist/bench/adaptation.js +167 -0
- package/dist/bench/adaptation.js.map +1 -0
- package/dist/bench/harness.d.ts +2 -0
- package/dist/bench/harness.d.ts.map +1 -1
- package/dist/bench/harness.js +1 -1
- package/dist/bench/harness.js.map +1 -1
- package/dist/bench/index.d.ts +5 -3
- package/dist/bench/index.d.ts.map +1 -1
- package/dist/bench/index.js +4 -2
- package/dist/bench/index.js.map +1 -1
- package/dist/bench/types.d.ts +88 -0
- package/dist/bench/types.d.ts.map +1 -1
- package/dist/evermind/adapt.d.ts +40 -0
- package/dist/evermind/adapt.d.ts.map +1 -0
- package/dist/evermind/adapt.js +25 -0
- package/dist/evermind/adapt.js.map +1 -0
- package/dist/evermind/delta_wire.d.ts +43 -0
- package/dist/evermind/delta_wire.d.ts.map +1 -0
- package/dist/evermind/delta_wire.js +94 -0
- package/dist/evermind/delta_wire.js.map +1 -0
- package/dist/evermind/eval.d.ts +31 -0
- package/dist/evermind/eval.d.ts.map +1 -0
- package/dist/evermind/eval.js +36 -0
- package/dist/evermind/eval.js.map +1 -0
- package/dist/evermind/index.d.ts +16 -0
- package/dist/evermind/index.d.ts.map +1 -0
- package/dist/evermind/index.js +11 -0
- package/dist/evermind/index.js.map +1 -0
- package/dist/evermind/merge.d.ts +21 -0
- package/dist/evermind/merge.d.ts.map +1 -0
- package/dist/evermind/merge.js +79 -0
- package/dist/evermind/merge.js.map +1 -0
- package/dist/{moe/moe_package.d.ts → evermind/package.d.ts} +55 -5
- package/dist/evermind/package.d.ts.map +1 -0
- package/dist/{moe/moe_package.js → evermind/package.js} +118 -19
- package/dist/evermind/package.js.map +1 -0
- package/dist/import/foreign/adapter.d.ts +59 -0
- package/dist/import/foreign/adapter.d.ts.map +1 -0
- package/dist/import/foreign/adapter.js +76 -0
- package/dist/import/foreign/adapter.js.map +1 -0
- package/dist/import/foreign/apply.d.ts +23 -0
- package/dist/import/foreign/apply.d.ts.map +1 -0
- package/dist/import/foreign/apply.js +43 -0
- package/dist/import/foreign/apply.js.map +1 -0
- package/dist/import/foreign/codestral_mamba.d.ts +57 -0
- package/dist/import/foreign/codestral_mamba.d.ts.map +1 -0
- package/dist/import/foreign/codestral_mamba.js +174 -0
- package/dist/import/foreign/codestral_mamba.js.map +1 -0
- package/dist/import/foreign/falcon_mamba.d.ts +51 -0
- package/dist/import/foreign/falcon_mamba.d.ts.map +1 -0
- package/dist/import/foreign/falcon_mamba.js +131 -0
- package/dist/import/foreign/falcon_mamba.js.map +1 -0
- package/dist/import/foreign/index.d.ts +21 -0
- package/dist/import/foreign/index.d.ts.map +1 -0
- package/dist/import/foreign/index.js +18 -0
- package/dist/import/foreign/index.js.map +1 -0
- package/dist/import/foreign/plan.d.ts +122 -0
- package/dist/import/foreign/plan.d.ts.map +1 -0
- package/dist/import/foreign/plan.js +171 -0
- package/dist/import/foreign/plan.js.map +1 -0
- package/dist/import/foreign/registry.d.ts +35 -0
- package/dist/import/foreign/registry.d.ts.map +1 -0
- package/dist/import/foreign/registry.js +89 -0
- package/dist/import/foreign/registry.js.map +1 -0
- package/dist/import/index.d.ts +7 -0
- package/dist/import/index.d.ts.map +1 -1
- package/dist/import/index.js +7 -0
- package/dist/import/index.js.map +1 -1
- package/dist/index.d.ts +25 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -9
- package/dist/index.js.map +1 -1
- package/dist/kernels/selective_scan.d.ts.map +1 -1
- package/dist/kernels/selective_scan.js +15 -9
- package/dist/kernels/selective_scan.js.map +1 -1
- package/dist/kernels/slice.d.ts +26 -0
- package/dist/kernels/slice.d.ts.map +1 -0
- package/dist/kernels/slice.js +52 -0
- package/dist/kernels/slice.js.map +1 -0
- package/dist/kernels/weight_update.d.ts.map +1 -1
- package/dist/kernels/weight_update.js +30 -4
- package/dist/kernels/weight_update.js.map +1 -1
- package/dist/lm/evermind_lm.d.ts +110 -1
- package/dist/lm/evermind_lm.d.ts.map +1 -1
- package/dist/lm/evermind_lm.js +220 -35
- package/dist/lm/evermind_lm.js.map +1 -1
- package/dist/lm/index.d.ts +4 -2
- package/dist/lm/index.d.ts.map +1 -1
- package/dist/lm/index.js +3 -1
- package/dist/lm/index.js.map +1 -1
- package/dist/lm/text_embedder.d.ts +55 -0
- package/dist/lm/text_embedder.d.ts.map +1 -0
- package/dist/lm/text_embedder.js +62 -0
- package/dist/lm/text_embedder.js.map +1 -0
- package/dist/model/attention_block.d.ts.map +1 -1
- package/dist/model/attention_block.js +9 -5
- package/dist/model/attention_block.js.map +1 -1
- package/dist/model/mamba1_block.d.ts.map +1 -1
- package/dist/model/mamba1_block.js +28 -12
- package/dist/model/mamba1_block.js.map +1 -1
- package/dist/model/mamba2_block.d.ts.map +1 -1
- package/dist/model/mamba2_block.js +14 -9
- package/dist/model/mamba2_block.js.map +1 -1
- package/dist/model/mamba3_block.d.ts.map +1 -1
- package/dist/model/mamba3_block.js +14 -9
- package/dist/model/mamba3_block.js.map +1 -1
- package/dist/moe/index.d.ts +0 -2
- package/dist/moe/index.d.ts.map +1 -1
- package/dist/moe/index.js +0 -1
- package/dist/moe/index.js.map +1 -1
- package/dist/optim/adamw.d.ts +32 -0
- package/dist/optim/adamw.d.ts.map +1 -1
- package/dist/optim/adamw.js +34 -0
- package/dist/optim/adamw.js.map +1 -1
- package/dist/tokenizer/bpe.d.ts +28 -0
- package/dist/tokenizer/bpe.d.ts.map +1 -1
- package/dist/tokenizer/bpe.js +47 -0
- package/dist/tokenizer/bpe.js.map +1 -1
- package/dist/training/cpu_ops.d.ts +44 -0
- package/dist/training/cpu_ops.d.ts.map +1 -0
- package/dist/training/cpu_ops.js +111 -0
- package/dist/training/cpu_ops.js.map +1 -0
- package/dist/training/mamba1_cpu.d.ts +109 -0
- package/dist/training/mamba1_cpu.d.ts.map +1 -0
- package/dist/training/mamba1_cpu.js +316 -0
- package/dist/training/mamba1_cpu.js.map +1 -0
- package/dist/training/model_cpu.d.ts +100 -0
- package/dist/training/model_cpu.d.ts.map +1 -0
- package/dist/training/model_cpu.js +252 -0
- package/dist/training/model_cpu.js.map +1 -0
- package/dist/training/trainer.d.ts +156 -16
- package/dist/training/trainer.d.ts.map +1 -1
- package/dist/training/trainer.js +327 -101
- package/dist/training/trainer.js.map +1 -1
- package/dist/utils/base64.d.ts +12 -0
- package/dist/utils/base64.d.ts.map +1 -0
- package/dist/utils/base64.js +27 -0
- package/dist/utils/base64.js.map +1 -0
- package/package.json +3 -1
- package/src/bench/adaptation.ts +180 -0
- package/src/bench/harness.ts +1 -1
- package/src/bench/index.ts +13 -2
- package/src/bench/types.ts +93 -0
- package/src/evermind/adapt.ts +61 -0
- package/src/evermind/delta_wire.ts +114 -0
- package/src/evermind/eval.ts +62 -0
- package/src/evermind/index.ts +32 -0
- package/src/evermind/merge.ts +106 -0
- package/src/{moe/moe_package.ts → evermind/package.ts} +148 -17
- package/src/import/foreign/adapter.ts +106 -0
- package/src/import/foreign/apply.ts +54 -0
- package/src/import/foreign/codestral_mamba.ts +224 -0
- package/src/import/foreign/falcon_mamba.ts +173 -0
- package/src/import/foreign/index.ts +37 -0
- package/src/import/foreign/plan.ts +294 -0
- package/src/import/foreign/registry.ts +126 -0
- package/src/import/index.ts +30 -0
- package/src/index.ts +94 -13
- package/src/kernels/selective_scan.ts +15 -9
- package/src/kernels/slice.ts +64 -0
- package/src/kernels/weight_update.ts +30 -4
- package/src/lm/evermind_lm.ts +244 -34
- package/src/lm/index.ts +6 -2
- package/src/lm/text_embedder.ts +84 -0
- package/src/model/attention_block.ts +9 -5
- package/src/model/mamba1_block.ts +28 -12
- package/src/model/mamba2_block.ts +14 -9
- package/src/model/mamba3_block.ts +14 -9
- package/src/moe/index.ts +0 -9
- package/src/optim/adamw.ts +60 -0
- package/src/tokenizer/bpe.ts +55 -0
- package/src/training/cpu_ops.ts +127 -0
- package/src/training/mamba1_cpu.ts +421 -0
- package/src/training/model_cpu.ts +360 -0
- package/src/training/trainer.ts +397 -154
- package/src/utils/base64.ts +28 -0
- package/dist/model/mamba_block.d.ts +0 -64
- package/dist/model/mamba_block.d.ts.map +0 -1
- package/dist/model/mamba_block.js +0 -303
- package/dist/model/mamba_block.js.map +0 -1
- package/dist/moe/moe_package.d.ts.map +0 -1
- package/dist/moe/moe_package.js.map +0 -1
- 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
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
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
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
d
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
d
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
d
|
|
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";
|
package/src/optim/adamw.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/tokenizer/bpe.ts
CHANGED
|
@@ -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
|
+
}
|