Quazim0t0 commited on
Commit
7fcb6bd
·
verified ·
1 Parent(s): 48d59d3

Upload web/public/verified_core.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. web/public/verified_core.js +88 -1
web/public/verified_core.js CHANGED
@@ -255,6 +255,92 @@
255
  return null;
256
  }
257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  // block-scaled verified GEMM, float in → float out.
259
  // d = { m, k, n, batch=1, relu=false }; X is (batch·m)×k, W is batch×(k×n)
260
  // gpuBgemm (from webgpu.js) runs the batched kernel with the fused epilogue;
@@ -403,7 +489,8 @@
403
 
404
  const api = { quantize, quantize2, quantizeRows, quantizeCols, quantizeHeadCols, lutMatmulJS, lutMatmul3JS, lutMatmul3,
405
  bgemmJS, vgemmBlock, auditTile, epi, attScoresJS, attCtxJS, linearFwd, forward, backward, splitApply,
406
- rowAbsMax, scalesFromAbsMax, quantizeRowsInv, vmlpBlock, bitDiff };
 
407
  if (typeof module !== "undefined" && module.exports) { TC = require("./traincore.js"); module.exports = api; }
408
  else { TC = root.TrainCore; root.Verified = api; }
409
  })(typeof self !== "undefined" ? self : this);
 
255
  return null;
256
  }
257
 
258
+ // ---- exact mirror of the split-K f32 GEMM ----------------------------------
259
+ // The f32 backward GEMM was the last kernel gated by a TOLERANCE (allclose at
260
+ // 1e-3) — and this project's own gate mutation test shows allclose waving
261
+ // through real bugs. The reason was real though: split-K accumulates in a
262
+ // different ORDER than a naive reference, so bit-equality against the naive
263
+ // one is impossible. The fix is the same as the epilogue mirror: reproduce
264
+ // the kernel's order exactly, then compare with `!==`.
265
+ // partials: for z in 0..S-1, sum p in [z*ks, min(k,(z+1)*ks)) in order
266
+ // reduce: sum the S partials in ascending z
267
+ // `fma` selects the rounding schedule for `s + a*b`: WGSL PERMITS a compiler
268
+ // to contract that into a fused multiply-add (one rounding) instead of two.
269
+ // Which one the device does is a fact about the device, so the gate tries
270
+ // both and reports which matches rather than assuming.
271
+ function fgemmMirror(A, Bm, d, fma) {
272
+ const { m, k, n } = d, transA = !!d.transA;
273
+ const S = k > 4096 ? Math.min(16, Math.ceil(k / 2048)) : 1;
274
+ const ks = Math.ceil(k / S);
275
+ const out = new Float32Array(m * n);
276
+ for (let row = 0; row < m; row++)
277
+ for (let col = 0; col < n; col++) {
278
+ let acc = 0; // reduce pass, ascending z
279
+ for (let z = 0; z < S; z++) {
280
+ const p0 = z * ks, p1 = Math.min(k, p0 + ks);
281
+ let s = 0; // one partial, in order
282
+ for (let p = p0; p < p1; p++) {
283
+ const a = transA ? A[p * m + row] : A[row * k + p];
284
+ s = fma ? f32(s + a * Bm[p * n + col]) // single rounding
285
+ : f32(s + f32(a * Bm[p * n + col]));
286
+ }
287
+ acc = f32(acc + s);
288
+ }
289
+ out[row * n + col] = acc;
290
+ }
291
+ return out;
292
+ }
293
+
294
+ // ---- live audits for the fused attention kernels ---------------------------
295
+ // The attention kernels had exact INIT gates but nothing at live shapes —
296
+ // the exact gap the GEMM audit exists to close, left open on the kernels with
297
+ // the trickiest indexing (head-strided gather, scatter write-back). These
298
+ // recompute individual output cells from the units, stratified like
299
+ // auditTile: last/first token pair, last head, last channel first, then
300
+ // random. Cost is hd (or T) multiply-adds per cell.
301
+ function auditAttScores(qq, kq, qs, ks, d, got, L, nCells) {
302
+ const { B, T, heads, hd } = d, C = heads * hd, mul = L.mul, raw = !!d.acc;
303
+ const N = nCells || 8;
304
+ const edges = [[B - 1, heads - 1, T - 1, T - 1], [0, 0, 0, 0],
305
+ [0, heads - 1, T - 1, 0], [B - 1, 0, 0, T - 1]];
306
+ for (let t = 0; t < N; t++) {
307
+ let bi, h, ti, tj;
308
+ if (t < edges.length) { bi = edges[t][0]; h = edges[t][1]; ti = edges[t][2]; tj = edges[t][3]; }
309
+ else { bi = (Math.random() * B) | 0; h = (Math.random() * heads) | 0;
310
+ ti = (Math.random() * T) | 0; tj = (Math.random() * T) | 0; }
311
+ const bz = bi * heads + h;
312
+ const qo = (bi * T + ti) * C + h * hd, ko = (bi * T + tj) * C + h * hd;
313
+ let acc = 0;
314
+ for (let p = 0; p < hd; p++) acc += mul[(qq[qo + p] & 0xFF) * 256 + (kq[ko + p] & 0xFF)];
315
+ const v = raw ? acc : epi(acc, qs[(bi * T + ti) * heads + h], ks[(bi * T + tj) * heads + h]);
316
+ const idx = (bz * T + ti) * T + tj;
317
+ if (raw ? got[idx] !== v : bitDiff(got[idx], v))
318
+ return `att.scores audit failed at [b${bi},h${h},${ti},${tj}] B${B}T${T}H${heads}d${hd}: kernel ${got[idx]} vs units ${v}`;
319
+ }
320
+ return null;
321
+ }
322
+ function auditAttCtx(aq, vq, as, vs, d, got, L, nCells) {
323
+ const { B, T, heads, hd } = d, C = heads * hd, mul = L.mul, raw = !!d.acc;
324
+ const N = nCells || 8;
325
+ const edges = [[B - 1, heads - 1, T - 1, hd - 1], [0, 0, 0, 0],
326
+ [0, heads - 1, T - 1, 0], [B - 1, 0, 0, hd - 1]];
327
+ for (let t = 0; t < N; t++) {
328
+ let bi, h, ti, j;
329
+ if (t < edges.length) { bi = edges[t][0]; h = edges[t][1]; ti = edges[t][2]; j = edges[t][3]; }
330
+ else { bi = (Math.random() * B) | 0; h = (Math.random() * heads) | 0;
331
+ ti = (Math.random() * T) | 0; j = (Math.random() * hd) | 0; }
332
+ const bz = bi * heads + h, ao = (bz * T + ti) * T;
333
+ let acc = 0;
334
+ for (let tj = 0; tj < T; tj++)
335
+ acc += mul[(aq[ao + tj] & 0xFF) * 256 + (vq[(bi * T + tj) * C + h * hd + j] & 0xFF)];
336
+ const v = raw ? acc : epi(acc, as[bz * T + ti], vs[(bi * heads + h) * hd + j]);
337
+ const idx = (bi * T + ti) * C + h * hd + j;
338
+ if (raw ? got[idx] !== v : bitDiff(got[idx], v))
339
+ return `att.ctx audit failed at [b${bi},h${h},${ti},${j}] B${B}T${T}H${heads}d${hd}: kernel ${got[idx]} vs units ${v}`;
340
+ }
341
+ return null;
342
+ }
343
+
344
  // block-scaled verified GEMM, float in → float out.
345
  // d = { m, k, n, batch=1, relu=false }; X is (batch·m)×k, W is batch×(k×n)
346
  // gpuBgemm (from webgpu.js) runs the batched kernel with the fused epilogue;
 
489
 
490
  const api = { quantize, quantize2, quantizeRows, quantizeCols, quantizeHeadCols, lutMatmulJS, lutMatmul3JS, lutMatmul3,
491
  bgemmJS, vgemmBlock, auditTile, epi, attScoresJS, attCtxJS, linearFwd, forward, backward, splitApply,
492
+ rowAbsMax, scalesFromAbsMax, quantizeRowsInv, vmlpBlock, bitDiff,
493
+ auditAttScores, auditAttCtx, fgemmMirror };
494
  if (typeof module !== "undefined" && module.exports) { TC = require("./traincore.js"); module.exports = api; }
495
  else { TC = root.TrainCore; root.Verified = api; }
496
  })(typeof self !== "undefined" ? self : this);