| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const fs = require("fs"); |
| const path = require("path"); |
| const V = require("./public/verified_core.js"); |
| const L = { mul: new Int16Array(fs.readFileSync(path.join(__dirname, "public", "mul_lut.bin")).buffer.slice(0)) }; |
|
|
| |
| function bitLength(n) { let b = 0; while (n > 0n) { n >>= 1n; b++; } return b; } |
|
|
| |
| function decomposeF32(x) { |
| const dv = new DataView(new ArrayBuffer(4)); |
| dv.setFloat32(0, x); |
| const bits = dv.getUint32(0); |
| const s = bits >>> 31, e = (bits >>> 23) & 0xFF, m = bits & 0x7FFFFF; |
| if (e === 0xFF) throw new Error("inf/nan out of scope"); |
| |
| if (e === 0) return { neg: s === 1, mant: BigInt(m), exp2: -149 }; |
| |
| return { neg: s === 1, mant: BigInt(m) | (1n << 23n), exp2: e - 150 }; |
| } |
|
|
| |
| |
| |
| |
| |
| function roundToF32(neg, mant, exp2) { |
| if (mant === 0n) return neg ? -0 : 0; |
| const bl = bitLength(mant); |
| const shift = Math.max(bl - 24, -exp2 - 149); |
| let r, e; |
| if (shift <= 0) { |
| r = mant << BigInt(-shift); |
| e = exp2 + shift; |
| } else { |
| const sh = BigInt(shift); |
| const keep = mant >> sh; |
| const rem = mant - (keep << sh); |
| const half = 1n << (sh - 1n); |
| r = keep; |
| if (rem > half || (rem === half && (keep & 1n) === 1n)) r += 1n; |
| e = exp2 + shift; |
| } |
| if (bitLength(r) > 24) { r >>= 1n; e += 1; } |
| if (e + bitLength(r) - 1 > 127) return neg ? -Infinity : Infinity; |
| const val = Number(r) * Math.pow(2, e); |
| return neg ? -val : val; |
| } |
|
|
| const i32ToF32Spec = (s) => s === 0 ? 0 : roundToF32(s < 0, BigInt(Math.abs(s)), 0); |
| |
| |
| |
| |
| |
| |
| const signbit = (x) => x < 0 || Object.is(x, -0); |
| function mulF32Spec(a, b) { |
| if (a === 0 || b === 0) return (signbit(a) !== signbit(b)) ? -0 : 0; |
| const A = decomposeF32(a), B = decomposeF32(b); |
| return roundToF32(A.neg !== B.neg, A.mant * B.mant, A.exp2 + B.exp2); |
| } |
| |
| const epiSpec = (s, a, b) => mulF32Spec(mulF32Spec(i32ToF32Spec(s), a), b); |
|
|
| |
| |
| |
| function addF32Spec(a, b) { |
| if (a === 0 && b === 0) return (signbit(a) && signbit(b)) ? -0 : 0; |
| if (a === 0) return b; |
| if (b === 0) return a; |
| const A = decomposeF32(a), B = decomposeF32(b); |
| const e0 = Math.min(A.exp2, B.exp2); |
| const T = (A.neg ? -A.mant : A.mant) * (1n << BigInt(A.exp2 - e0)) |
| + (B.neg ? -B.mant : B.mant) * (1n << BigInt(B.exp2 - e0)); |
| if (T === 0n) return 0; |
| return roundToF32(T < 0n, T < 0n ? -T : T, e0); |
| } |
|
|
| |
| |
| |
| |
| |
| function fmaF32Spec(a, b, c) { |
| const spNeg = signbit(a) !== signbit(b); |
| let P = 0n, ep = 0; |
| if (a !== 0 && b !== 0) { const A = decomposeF32(a), B = decomposeF32(b); P = A.mant * B.mant; ep = A.exp2 + B.exp2; } |
| let Cm = 0n, ec = 0; |
| const scNeg = signbit(c); |
| if (c !== 0) { const C = decomposeF32(c); Cm = C.mant; ec = C.exp2; } |
| const e0 = Math.min(ep, ec); |
| const T = (spNeg ? -P : P) * (1n << BigInt(ep - e0)) + (scNeg ? -Cm : Cm) * (1n << BigInt(ec - e0)); |
| if (T === 0n) return (spNeg && scNeg) ? -0 : 0; |
| return roundToF32(T < 0n, T < 0n ? -T : T, e0); |
| } |
|
|
| |
| let pass = true; |
| const ok = (c, msg) => { console.log(`${c ? " ok " : " FAIL"} ${msg}`); if (!c) pass = false; }; |
| const f32 = Math.fround; |
|
|
| console.log("\nthe oracle reproduces known binary32 behaviour:"); |
| ok(i32ToF32Spec(1 << 24) === 16777216, "2^24 is exact"); |
| ok(i32ToF32Spec((1 << 24) + 1) === 16777216, "2^24+1 ties down to even (not 2^24+2)"); |
| ok(i32ToF32Spec((1 << 24) + 3) === 16777220, "2^24+3 rounds up to the even neighbour"); |
| ok(i32ToF32Spec(-((1 << 24) + 1)) === -16777216, "sign symmetry on the tie"); |
| ok(mulF32Spec(f32(0.1), f32(0.1)) === f32(f32(0.1) * f32(0.1)), "0.1*0.1 matches a correctly-rounded f32 multiply"); |
| ok(mulF32Spec(1.5, 2) === 3, "exact small product"); |
| ok(mulF32Spec(f32(1e-30), f32(1e-15)) === f32(f32(1e-30) * f32(1e-15)), "underflow into subnormals"); |
| ok(i32ToF32Spec(0) === 0 && mulF32Spec(0, 5) === 0, "zeros"); |
| ok(Object.is(mulF32Spec(5, -0), -0) && Object.is(mulF32Spec(-0, -0), 0), "signed zero: sign of a zero product is the XOR of the operand signs"); |
|
|
| |
| |
| console.log("\nthe oracle agrees with an independent correctly-rounded implementation:"); |
| const f32buf = new Float32Array(1); |
| const asF32 = (x) => { f32buf[0] = x; return f32buf[0]; }; |
| { |
| let bad = 0, n = 0; |
| const cases = []; |
| for (let t = 0; t < 200000; t++) cases.push(((Math.random() * 2 - 1) * 2.1e9) | 0); |
| for (const s of cases) { n++; if (!Object.is(i32ToF32Spec(s), f32(s))) bad++; } |
| ok(bad === 0, `i32ToF32Spec matches Math.fround on ${n} random int32 (incl. |s| > 2^24)`); |
| } |
| { |
| |
| |
| |
| let bad = 0, n = 0; |
| const mags = [1, 1e-3, 1e-8, 1e-20, 1e-38, 1e-40, 1e-44, 3e38, 1e30, 127, 1 / 127]; |
| for (let t = 0; t < 300000; t++) { |
| const a = asF32((Math.random() * 2 - 1) * mags[(Math.random() * mags.length) | 0]); |
| const b = asF32((Math.random() * 2 - 1) * mags[(Math.random() * mags.length) | 0]); |
| n++; |
| if (!Object.is(mulF32Spec(a, b), f32(a * b))) { |
| if (bad++ < 3) console.log(` ${a} * ${b} spec=${mulF32Spec(a, b)} fround=${f32(a * b)}`); |
| } |
| } |
| ok(bad === 0, `mulF32Spec matches the correctly-rounded product on ${n} draws (subnormal..overflow)`); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| console.log("\naddF32Spec vs Math.fround(a+b), including extreme exponent gaps:"); |
| { |
| let bad = 0, n = 0; |
| const mags = [1, 1e-3, 1e-8, 1e-20, 1e-38, 1e-42, 3e38, 1e30, 0.5, 127]; |
| for (let t = 0; t < 300000; t++) { |
| const a = asF32((Math.random() * 2 - 1) * mags[(Math.random() * mags.length) | 0]); |
| const b = asF32((Math.random() * 2 - 1) * mags[(Math.random() * mags.length) | 0]); |
| n++; |
| if (!Object.is(addF32Spec(a, b), f32(a + b))) { |
| if (bad++ < 3) console.log(` ${a} + ${b} spec=${addF32Spec(a, b)} fround=${f32(a + b)}`); |
| } |
| } |
| ok(bad === 0, `addF32Spec matches fround(a+b) on ${n} draws — the per-add mirror schedule is the correctly-rounded sum`); |
| ok(Object.is(addF32Spec(-0, -0), -0) && Object.is(addF32Spec(asF32(1e-20), asF32(-1e-20)), 0), |
| "signed zero on sums: -0 + -0 = -0, exact cancellation = +0"); |
| } |
|
|
| |
| console.log("\nfmaF32Spec: single rounding, cross-checked against an independent oracle:"); |
| { |
| |
| |
| let differ = 0, n = 0; |
| for (let t = 0; t < 100000; t++) { |
| const a = asF32((Math.random() * 2 - 1) * 1e3); |
| const b = asF32((Math.random() * 2 - 1) * 1e3); |
| const c = asF32(-(a * b)); |
| n++; |
| if (!Object.is(fmaF32Spec(a, b, c), f32(f32(a * b) + c))) differ++; |
| } |
| ok(differ > 0, `fused differs from round-twice on ${differ}/${n} cancellation cases (single rounding is real)`); |
|
|
| |
| |
| |
| let vecs = null; |
| try { vecs = JSON.parse(fs.readFileSync(path.join(__dirname, "test_vectors_fma.json"), "utf8")); } catch (e) {} |
| if (vecs) { |
| const buf = new ArrayBuffer(4), dv = new DataView(buf); |
| const fromBits = (u) => { dv.setUint32(0, u); return dv.getFloat32(0); }; |
| const toBits = (x) => { dv.setFloat32(0, x); return dv.getUint32(0); }; |
| let bad = 0; |
| for (const [ah, bh, ch, rh] of vecs.vectors) { |
| const r = fmaF32Spec(fromBits(parseInt(ah, 16)), fromBits(parseInt(bh, 16)), fromBits(parseInt(ch, 16))); |
| if (toBits(r) !== parseInt(rh, 16)) { |
| if (bad++ < 3) console.log(` ${ah},${bh},${ch}: js=${toBits(r).toString(16)} py=${rh}`); |
| } |
| } |
| ok(bad === 0, `agrees bit-for-bit with neural-rdna2's big-int fma golden on ${vecs.vectors.length} vectors (quantize-domain + cancellation + raw finite bits)`); |
| } else { |
| console.log(" note test_vectors_fma.json not present — cross-oracle check skipped"); |
| } |
|
|
| |
| |
| |
| |
| |
| { |
| let vecs = null; |
| try { vecs = JSON.parse(fs.readFileSync(path.join(__dirname, "test_vectors_fp32.json"), "utf8")); } catch (e) {} |
| if (vecs) { |
| const buf = new ArrayBuffer(4), dv = new DataView(buf); |
| const fromBits = (u) => { dv.setUint32(0, u); return dv.getFloat32(0); }; |
| const toBits = (x) => { dv.setFloat32(0, x); return dv.getUint32(0); }; |
| let badM = 0, badA = 0; |
| for (const [ah, bh, mh, sh] of vecs.vectors) { |
| const a = fromBits(parseInt(ah, 16)), b = fromBits(parseInt(bh, 16)); |
| if (toBits(mulF32Spec(a, b)) !== parseInt(mh, 16)) badM++; |
| if (toBits(addF32Spec(a, b)) !== parseInt(sh, 16)) badA++; |
| } |
| ok(badM === 0, `mulF32Spec agrees bit-for-bit with the LUT-backed RDNA2 core on ${vecs.vectors.length} vectors`); |
| ok(badA === 0, `addF32Spec agrees bit-for-bit with the LUT-backed RDNA2 core on ${vecs.vectors.length} vectors`); |
| } else { |
| console.log(" note test_vectors_fp32.json not present — fp32 cross-oracle check skipped"); |
| } |
| } |
|
|
| |
| |
| |
| |
| let embad = 0, en = 0; |
| for (let t = 0; t < 300000; t++) { |
| const inv = asF32(0.25 + Math.random() * 500); |
| const x = asF32(Math.random() * 127.4 / inv); |
| en++; |
| if (!Object.is(f32(x * inv + 0.5), fmaF32Spec(x, inv, 0.5))) embad++; |
| } |
| ok(embad === 0, `on the quantize domain the f64 emulation equals the true fma (${en} draws) — test_b2b's immunity scan stands on a checked fact`); |
|
|
| |
| |
| let raw = 0, fl = 0; |
| for (let k = 1; k <= 7; k++) { |
| const u = Math.pow(2, k - 23); |
| for (let t = 0; t < 150000; t++) { |
| const inv = asF32(0.25 + Math.random() * 500); |
| const x = asF32((Math.pow(2, k) - 0.5 + (Math.random() * 8 - 6) * u) / inv); |
| if (!(x >= 0)) continue; |
| const two = f32(f32(x * inv) + 0.5), fused = fmaF32Spec(x, inv, 0.5); |
| if (!Object.is(two, fused)) { raw++; if (Math.floor(two) !== Math.floor(fused)) fl++; } |
| } |
| } |
| ok(raw > 0 && fl === 0, `true-fma immunity: ${raw} last-ulp diffs at binade edges, ${fl} floor-visible`); |
| } |
|
|
| |
| console.log("\nVerified.epi vs the oracle, over the range the kernels produce:"); |
| let mism = 0, n = 0; |
| for (let t = 0; t < 200000; t++) { |
| |
| |
| const s = Math.round((Math.random() * 2 - 1) * 2.6e8); |
| const a = f32(Math.random() * 2e-2 + 1e-8); |
| const b = f32(Math.random() * 2e-2 + 1e-8); |
| const mine = V.epi(s, a, b), spec = epiSpec(s, a, b); |
| n++; |
| if (mine !== spec && !(Number.isNaN(mine) && Number.isNaN(spec))) { |
| if (mism++ < 3) console.log(` s=${s} a=${a} b=${b} epi=${mine} spec=${spec}`); |
| } |
| } |
| ok(mism === 0, `${n} random triples, ${mism} disagreements`); |
|
|
| |
| console.log("\nsame check pinned to the awkward values:"); |
| let em = 0; |
| for (const s of [0, 1, -1, (1 << 24) - 1, 1 << 24, (1 << 24) + 1, (1 << 24) + 3, -(1 << 24) - 1, 2 ** 30, -(2 ** 30)]) |
| for (const a of [f32(1), f32(0.5), f32(1 / 3), f32(1e-20), f32(3e-8)]) |
| for (const b of [f32(1), f32(2), f32(1 / 7), f32(1e-20)]) { |
| if (V.epi(s, a, b) !== epiSpec(s, a, b)) { |
| if (em++ < 3) console.log(` s=${s} a=${a} b=${b} epi=${V.epi(s, a, b)} spec=${epiSpec(s, a, b)}`); |
| } |
| } |
| ok(em === 0, `edge grid, ${em} disagreements`); |
|
|
| |
| |
| |
| { |
| let bad = 0, n = 0; |
| for (let k = 0; k <= 20; k++) { |
| const a = f32(Math.pow(2, -k)), b = f32(1); |
| for (let s = (1 << 24) - 4; s <= (1 << 24) + 4; s++) { |
| n += 2; |
| if (V.epi(s, a, b) !== epiSpec(s, a, b)) bad++; |
| if (V.epi(-s, a, b) !== epiSpec(-s, a, b)) bad++; |
| } |
| } |
| ok(bad === 0, `tie-to-even ladder around 2^24, ${n} cases, ${bad} disagreements`); |
| } |
|
|
| |
| |
| |
| { |
| const d = { m: 7, k: 40, n: 6, batch: 2 }; |
| const Xf = Float32Array.from({ length: d.batch * d.m * d.k }, () => Math.random() * 2 - 1); |
| const Wf = Float32Array.from({ length: d.batch * d.k * d.n }, () => Math.random() * 2 - 1); |
| const x = V.quantizeRows(Xf, d.batch * d.m, d.k); |
| const wq = new Int8Array(d.batch * d.k * d.n), ws = new Float32Array(d.batch * d.n); |
| for (let bz = 0; bz < d.batch; bz++) { |
| const w = V.quantizeCols(Wf.subarray(bz * d.k * d.n, (bz + 1) * d.k * d.n), d.k, d.n); |
| wq.set(w.q, bz * d.k * d.n); ws.set(w.s, bz * d.n); |
| } |
| const raw = V.bgemmJS(x.q, wq, x.s, ws, { ...d, acc: true }, L); |
| const fin = V.bgemmJS(x.q, wq, x.s, ws, d, L); |
| let bad = 0; |
| for (let bz = 0; bz < d.batch; bz++) |
| for (let i = 0; i < d.m; i++) |
| for (let j = 0; j < d.n; j++) { |
| const idx = (bz * d.m + i) * d.n + j; |
| if (!Object.is(epiSpec(raw[idx], x.s[bz * d.m + i], ws[bz * d.n + j]), fin[idx])) bad++; |
| } |
| ok(bad === 0, `bgemmJS outputs rebuilt from the raw accumulator via the oracle (${d.batch}x${d.m}x${d.n}), ${bad} disagreements`); |
| } |
|
|
| |
| |
| |
| console.log("\nthe oracle rejects the mirror I actually shipped before:"); |
| const oldEpi = (s, a, b) => f32(s * a * b); |
| let oldBad = 0; |
| for (let t = 0; t < 200000; t++) { |
| const s = Math.round((Math.random() * 2 - 1) * 2.6e8); |
| const a = f32(Math.random() * 2e-2 + 1e-8); |
| const b = f32(Math.random() * 2e-2 + 1e-8); |
| if (oldEpi(s, a, b) !== epiSpec(s, a, b)) oldBad++; |
| } |
| ok(oldBad > 0, `old single-rounding mirror disagrees on ${oldBad} of 200000 (${(oldBad / 2000).toFixed(2)}% — the bug the 1e-6 tolerance hid)`); |
| |
| const factorBug = (s, a, b) => f32(V.epi(s, a, b) * 2); |
| ok(factorBug(12345, f32(0.01), f32(0.02)) !== epiSpec(12345, f32(0.01), f32(0.02)), |
| "oracle catches a dropped-constant-factor bug (corpus: gelu_triton_buggy)"); |
|
|
| console.log(pass ? "\nIEEE ORACLE TEST PASSED — the mirror's epilogue IS the spec, not merely agreeable." |
| : "\nIEEE ORACLE TEST FAILED"); |
| process.exit(pass ? 0 : 1); |
|
|