| |
| |
| |
| |
| const T = require("./public/traincore.js"); |
|
|
| function randn(n) { |
| const a = new Float32Array(n); |
| for (let i = 0; i < n; i++) { |
| let u = 0, v = 0; |
| while (u === 0) u = Math.random(); |
| while (v === 0) v = Math.random(); |
| a[i] = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v); |
| } |
| return a; |
| } |
|
|
| const din = 16, dout = 4, nPer = 128, steps = 400, lr = 0.05; |
|
|
| |
| const Wtrue = randn(din * dout); |
| function makeShard() { |
| const X = randn(nPer * din); |
| const y = T.matmul(X, Wtrue, nPer, din, dout); |
| return { X, y }; |
| } |
| const A = makeShard(), B = makeShard(); |
|
|
| |
| const W0 = randn(din * dout); |
| const Wa = Float32Array.from(W0), Wb = Float32Array.from(W0); |
|
|
| let loss = 0; |
| for (let s = 0; s < steps; s++) { |
| const ra = T.forwardLossGrad(A.X, A.y, Wa, nPer, din, dout); |
| const rb = T.forwardLossGrad(B.X, B.y, Wb, nPer, din, dout); |
| const avg = T.averageGrads([ra.gradW, rb.gradW]); |
| T.applyGrad(Wa, avg, lr); |
| T.applyGrad(Wb, avg, lr); |
| loss = (ra.loss + rb.loss) / 2; |
| if (s % 80 === 0 || s === steps - 1) |
| console.log(` step ${s} cluster-avg loss ${loss.toFixed(6)}`); |
| } |
|
|
| let maxDiff = 0; |
| for (let i = 0; i < Wa.length; i++) maxDiff = Math.max(maxDiff, Math.abs(Wa[i] - Wb[i])); |
| let recovery = 0; |
| for (let i = 0; i < Wtrue.length; i++) recovery = Math.max(recovery, Math.abs(Wa[i] - Wtrue[i])); |
|
|
| console.log(`\nreplica max param diff: ${maxDiff.toExponential(3)}`); |
| console.log(`max |W - W_true|: ${recovery.toExponential(3)}`); |
| const ok = loss < 1e-3 && maxDiff < 1e-9 && recovery < 0.05; |
| console.log(ok ? "\nCORE TEST PASSED — converged, replicas in sync." : "\nCORE TEST FAILED"); |
| process.exit(ok ? 0 : 1); |
|
|