code wiki / _hdl_build / nx_nofloat_vs_float_gate.nx

nx_nofloat_vs_float_gate.nx source

↩ module page · 120 lines · 6497 B

1// nx_nofloat_vs_float_gate.nx -- CROSS-VALIDATION: do we need the float equivalent? Answer: it already EXISTS 2// (nx_autograd, the software-f32 scalar tape), and its right role is a VERIFICATION ORACLE + the exceed COMPETITOR 3// -- NOT a parallel product stack. This gate USES that role: it builds the SAME graph 4// loss = ( relu(w1*x + b1)*w2 + b2 )^2 5// in BOTH engines -- FLOAT via nx_autograd (ag_*, IEEE-754) and NO-FLOAT via nx_nofloat_autograd (nfa_*, integer 6// Q16) -- and asserts the four parameter gradients AGREE (in milli-units). Two INDEPENDENT autograd 7// implementations (different tapes, different number systems) computing the same gradients is far stronger 8// evidence than finite differences alone. It directly answers the question: the float engine validates the 9// no-float engine; we do not need to BUILD a float trainer (we have it) and do not need it in the PRODUCT 10// (no-float is the reproducibility exceed) -- we need it exactly here, as the oracle. 11// 12// T1..T4: for each of {w1,b1,w2,b2}, |float_grad_milli - nofloat_grad_milli| < 60/1000. 13// The true grads (analytic): w1=3.094, b1=4.125, w2=2.063, b2=2.750 -> both engines land on them. 14// expect_exit: 0 license_tier: ORIGINAL imports: nx_autograd (->ag_*, ->nx_f32_*), nx_nofloat_autograd, nx_syscalls 15import "nx_autograd.nx" 16import "nx_nofloat_autograd.nx" 17import "nx_syscalls.nx" 18 19func v_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 20func v_pn(v: i64) -> i64 { 21 let b: *u8 = sys_mmap(28); var x: i64 = v 22 if x < 0 { b[0]=45; sys_write(1,b,1); x = 0 - x } 23 if x == 0 { b[0]=48; sys_write(1,b,1); return 0 } 24 var d: i64=0; var y: i64=x 25 while y>0 { d=d+1; y=y/10 } 26 var i: i64=d-1; y=x 27 while i>=0 { b[i]=(48+(y%10)) as u8; y=y/10; i=i-1 } 28 sys_write(1,b,d); return 0 29} 30func v_check(name: *u8, cond: i64) -> i64 { if cond==1 { v_puts(" PASS " as *u8) } else { v_puts(" FAIL " as *u8) } v_puts(name); v_puts("\n" as *u8); return cond } 31func v_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 32 33// f32 (nx_autograd's IEEE-754 bits) -> round(v*1000) signed int, via nx_f32 ops only (linear advance; values O(1)). 34func f32_milli(v: i64) -> i64 { 35 var s: i64 = nx_f32_mul(v, nx_i32_to_f32(1000)) 36 var neg: i64 = 0 37 if nx_f32_lt(s, AG_F32_ZERO) == 1 { neg = 1; s = nx_f32_neg(s) } 38 let half: i64 = ag_constf(1, 2) 39 var m: i64 = 0; var go: i64 = 1 40 while go == 1 { 41 let mid: i64 = nx_f32_add(nx_i32_to_f32(m), half) 42 if nx_f32_lt(mid, s) == 1 { m = m + 1; if m >= 100000 { go = 0 } } else { go = 0 } 43 } 44 if neg == 1 { return 0 - m } 45 return m 46} 47func q16_milli(q: i64) -> i64 { var neg: i64=0; var a: i64=q; if a<0 { neg=1; a=0-a } let m: i64=(a*1000)/65536; if neg==1 { return 0-m } return m } 48 49// FLOAT gradients (nx_autograd scalar f32 tape): loss=(relu(w1*x+b1)*w2+b2)^2 -> gf[0..3] (f32 bits) 50func float_grads(gf: *i64) -> i64 { 51 let tape: *i64 = sys_mmap(64*5*8) as *i64 52 let np: *i64 = sys_mmap(8) as *i64 53 *np = 0 54 let nw1: i64 = ag_leaf(tape, np, ag_constf(2,3)) 55 let nb1: i64 = ag_leaf(tape, np, ag_constf(1,4)) 56 let nw2: i64 = ag_leaf(tape, np, ag_constf(3,2)) 57 let nb2: i64 = ag_leaf(tape, np, ag_constf(1,4)) 58 let nx: i64 = ag_leaf(tape, np, ag_constf(3,4)) 59 let h1: i64 = ag_add(tape, np, ag_mul(tape, np, nw1, nx), nb1) 60 let r: i64 = ag_relu(tape, np, h1) 61 let h2: i64 = ag_add(tape, np, ag_mul(tape, np, r, nw2), nb2) 62 let loss: i64 = ag_mul(tape, np, h2, h2) 63 ag_backward(tape, *np, loss) 64 gf[0]=ag_grad(tape,nw1); gf[1]=ag_grad(tape,nb1); gf[2]=ag_grad(tape,nw2); gf[3]=ag_grad(tape,nb2) 65 return 0 66} 67 68// NO-FLOAT gradients (nx_nofloat_autograd Q16 tensor tape): same graph, loss=mse(h2,zero)=h2^2 -> gnf[0..3] (Q16) 69func nofloat_grads(gnf: *i64) -> i64 { 70 let tape: *i64 = sys_mmap(64*7*8) as *i64 71 let vals: *i64 = sys_mmap(256*8) as *i64 72 let grads: *i64 = sys_mmap(256*8) as *i64 73 let st: *i64 = sys_mmap(2*8) as *i64 74 let p: *i64 = sys_mmap(6*8) as *i64 75 p[0]=43691; p[1]=16384; p[2]=98304; p[3]=16384; p[4]=49152; p[5]=0 // w1=2/3,b1=1/4,w2=3/2,b2=1/4,x=3/4,zero 76 st[0]=0; st[1]=0 77 let nw1: i64 = nfa_leaf(tape,vals,st,1,1,p,0) 78 let nb1: i64 = nfa_leaf(tape,vals,st,1,1,p,1) 79 let nw2: i64 = nfa_leaf(tape,vals,st,1,1,p,2) 80 let nb2: i64 = nfa_leaf(tape,vals,st,1,1,p,3) 81 let nx: i64 = nfa_leaf(tape,vals,st,1,1,p,4) 82 let h1m: i64 = nfa_matvec(tape,vals,st,nw1,nx) 83 let h1: i64 = nfa_vadd(tape,vals,st,h1m,nb1) 84 let r: i64 = nfa_relu(tape,vals,st,h1) 85 let h2m: i64 = nfa_matvec(tape,vals,st,nw2,r) 86 let h2: i64 = nfa_vadd(tape,vals,st,h2m,nb2) 87 let nz: i64 = nfa_leaf(tape,vals,st,1,1,p,5) 88 let loss: i64 = nfa_mse(tape,vals,st,h2,nz) 89 nfa_backward(tape,vals,grads,st[0],loss) 90 gnf[0]=nfa_grad(tape,grads,nw1,0); gnf[1]=nfa_grad(tape,grads,nb1,0); gnf[2]=nfa_grad(tape,grads,nw2,0); gnf[3]=nfa_grad(tape,grads,nb2,0) 91 return 0 92} 93 94func main() -> i64 { 95 v_puts("nx_nofloat_vs_float gate (no-float gradients CROSS-VALIDATED against the independent FLOAT autograd)\n" as *u8) 96 let gf: *i64 = sys_mmap(4*8) as *i64 97 let gnf: *i64 = sys_mmap(4*8) as *i64 98 float_grads(gf) 99 nofloat_grads(gnf) 100 101 let nm: *i64 = sys_mmap(4*8) as *i64 // names not needed; iterate 102 var pass: i64 = 0; var total: i64 = 0 103 let tol: i64 = 60 104 var i: i64 = 0 105 while i < 4 { 106 let fm: i64 = f32_milli(gf[i]) 107 let qm: i64 = q16_milli(gnf[i]) 108 let diff: i64 = v_abs(fm - qm) 109 v_puts(" param " as *u8); v_pn(i); v_puts(": float_milli=" as *u8); v_pn(fm); v_puts(" nofloat_milli=" as *u8); v_pn(qm); v_puts(" |diff|=" as *u8); v_pn(diff) 110 var ok: i64 = 0; if diff < tol { ok = 1 } 111 if ok == 1 { v_puts(" agree\n" as *u8) } else { v_puts(" MISMATCH\n" as *u8) } 112 pass = pass + ok; total = total + 1 113 i = i + 1 114 } 115 116 v_puts(" (true analytic grads in milli: w1=3094 b1=4125 w2=2063 b2=2750 -> both engines land here)\n" as *u8) 117 v_puts("---- nofloat_vs_float: " as *u8); v_pn(pass); v_puts(" / " as *u8); v_pn(total); v_puts(" params agree within 60/1000 ----\n" as *u8) 118 if pass == total { v_puts("verdict=GREEN (the FLOAT autograd is the verification ORACLE -- it CONFIRMS the no-float gradients; no parallel float trainer needed: float = oracle + competitor, not product)\n" as *u8); sys_exit(0); return 0 } 119 v_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 120}