code wiki / _hdl_build / nx_nofloat_autograd_gate.nx

nx_nofloat_autograd_gate.nx source

↩ module page · 221 lines · 12750 B

1// nx_nofloat_autograd_gate.nx -- HARD-EVIDENCE gate for the NO-FLOAT (integer Q16) general autograd 2// (nx_nofloat_autograd). Proves, by RUNNING in pure integer arithmetic, that the team can do general 3// reverse-mode backprop + train a model WITHOUT any float -- the genuine missing generation on the 4// no-float DeepMind ladder. Mirrors the proven nx_train_r1_gate evidence shape, but Q16-integer: 5// 6// A GRADCHECK (oracle = mathematics): loss = ( relu(w1*x+b1)*w2 + b2 )^2 at params OFF the relu kink 7// (w1=2/3, b1=1/4, w2=3/2, b2=1/4, x=3/4, all Q16). For each param: analytic grad (the tape's reverse 8// sweep) vs CENTRAL finite difference (L(p+h)-L(p-h))/2h, h=1/128, all in Q16. Relative error < 1/32 9// (floor 1/16). This exercises matvec + vadd + RELU + mse backward -> a GENERAL graph, not one layer. 10// D THE GRADCHECK HAS TEETH (neg-control): negate one analytic grad and assert the SAME check now FAILS 11// -- a wrong gradient is provably caught (so gate A is not vacuously passing). 12// B A MODEL PROVABLY LEARNS: the EXACT task nx_nn_train solves (linear layer W[1x2], target W*=[1.0,-0.5] 13// Q16, 4 samples), but trained by the GENERAL TAPE (matvec->mse summed, one reverse sweep, nfa_sgd) 14// instead of a hand-coded analytic gradient. Assert SSE loss collapses >=95% AND both weights converge 15// >=60% of the way to target with correct sign. The general autograd REPRODUCES the special trainer. 16// C BIT-EXACT REPRODUCIBLE: train twice from zero; assert the final integer weights are IDENTICAL. 17// Integer add is EXACTLY associative -> determinism is STRUCTURAL here (a stronger exceed-axis than the 18// f32 tower, which is only reproducible because it pins one summation order). 19// 20// Evidence -> knowledge/status/nofloat_autograd.log. Sovereign: imports nx_nofloat_autograd (pure integer; 21// NO nx_f32 anywhere in this organ's import graph) + nx_syscalls. license_tier: ORIGINAL expect_exit: 0 22import "nx_nofloat_autograd.nx" 23import "nx_g_check_lib.nx" 24import "nx_g_pn_lib.nx" 25import "nx_g_puts_lib.nx" 26import "nx_syscalls.nx" 27 28const NLOG: *u8 = "knowledge/status/nofloat_autograd.log" 29const Q16: i64 = 65536 30 31func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 32func q_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)/Q16; if neg==1 { return 0-m } return m } 33 34// ---- evidence emit helpers (defined before nl_emit so it can call them) ---- 35func nl_ws(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 36func nl_wn(fd: i64, v: i64) -> i64 { 37 let b: *u8 = sys_mmap(28); var m: i64=v; if m<0 { sys_write(fd,"-" as *u8,1); m=0-m } 38 let t: *u8 = sys_mmap(28); var k: i64=0; if m==0 { t[0]=48; k=1 } 39 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 40 var i: i64=0; while i<k { b[i]=t[k-1-i]; i=i+1 } sys_write(fd,b,k); return 0 41} 42func nl_emit(fd: i64, r: *i64) -> i64 { 43 nl_ws(fd, "NOFLOATAUTOGRAD authored=organ engine=Q16-integer-tensor-tape-autograd" as *u8) 44 nl_ws(fd, " | A_gradcheck_pass=" as *u8); nl_wn(fd, r[0]); nl_ws(fd, " worst_rel_milli=" as *u8); nl_wn(fd, r[1]) 45 nl_ws(fd, " | D_negcontrol_caught=" as *u8); nl_wn(fd, r[2]) 46 nl_ws(fd, " | B_learns_pass=" as *u8); nl_wn(fd, r[3]) 47 nl_ws(fd, " w0_milli=" as *u8); nl_wn(fd, r[4]); nl_ws(fd, " w1_milli=" as *u8); nl_wn(fd, r[5]) 48 nl_ws(fd, " loss_first=" as *u8); nl_wn(fd, r[6]); nl_ws(fd, " loss_last=" as *u8); nl_wn(fd, r[7]) 49 nl_ws(fd, " | C_bitexact_pass=" as *u8); nl_wn(fd, r[8]) 50 if r[9]==1 { nl_ws(fd, " verdict=GREEN\n" as *u8) } else { nl_ws(fd, " verdict=RED\n" as *u8) } 51 return 0 52} 53 54// ---- Gate A graph: loss = ( relu(w1*x+b1)*w2 + b2 )^2, built on the general tape. p=[w1,b1,w2,b2,x,zero]. 55// loss = mse(h2, zero) reuses the squared-error head as a square. leaves[0..3] = the 4 param leaf nodes. 56func gA_loss(tape: *i64, vals: *i64, st: *i64, p: *i64, leaves: *i64) -> i64 { 57 st[0]=0; st[1]=0 58 let nw1: i64 = nfa_leaf(tape,vals,st,1,1,p,0) 59 let nb1: i64 = nfa_leaf(tape,vals,st,1,1,p,1) 60 let nw2: i64 = nfa_leaf(tape,vals,st,1,1,p,2) 61 let nb2: i64 = nfa_leaf(tape,vals,st,1,1,p,3) 62 let nx: i64 = nfa_leaf(tape,vals,st,1,1,p,4) 63 let h1m: i64 = nfa_matvec(tape,vals,st,nw1,nx) 64 let h1: i64 = nfa_vadd(tape,vals,st,h1m,nb1) 65 let rr: i64 = nfa_relu(tape,vals,st,h1) 66 let h2m: i64 = nfa_matvec(tape,vals,st,nw2,rr) 67 let h2: i64 = nfa_vadd(tape,vals,st,h2m,nb2) 68 let nz: i64 = nfa_leaf(tape,vals,st,1,1,p,5) 69 let loss: i64 = nfa_mse(tape,vals,st,h2,nz) 70 leaves[0]=nw1; leaves[1]=nb1; leaves[2]=nw2; leaves[3]=nb2 71 return loss 72} 73func gA_lossval(tape: *i64, vals: *i64, st: *i64, p: *i64) -> i64 { 74 let lv: *i64 = sys_mmap(4*8) as *i64 75 let loss: i64 = gA_loss(tape,vals,st,p,lv) 76 return nfa_val(tape,vals,loss,0) 77} 78func gA_analytic(tape: *i64, vals: *i64, grads: *i64, st: *i64, p: *i64, gout: *i64) -> i64 { 79 let lv: *i64 = sys_mmap(4*8) as *i64 80 let loss: i64 = gA_loss(tape,vals,st,p,lv) 81 nfa_backward(tape,vals,grads,st[0],loss) 82 gout[0]=nfa_grad(tape,grads,lv[0],0); gout[1]=nfa_grad(tape,grads,lv[1],0) 83 gout[2]=nfa_grad(tape,grads,lv[2],0); gout[3]=nfa_grad(tape,grads,lv[3],0) 84 return 0 85} 86// central finite-difference grad wrt param pi, Q16 step h: ((L(p+h)-L(p-h)) * Q16) / (2h) -> Q16 grad. 87func gA_fd(tape: *i64, vals: *i64, st: *i64, p: *i64, pi: i64, h: i64) -> i64 { 88 let pp: *i64 = sys_mmap(6*8) as *i64 89 let pm: *i64 = sys_mmap(6*8) as *i64 90 var i: i64 = 0 91 while i < 6 { pp[i]=p[i]; pm[i]=p[i]; i=i+1 } 92 pp[pi]=p[pi]+h; pm[pi]=p[pi]-h 93 let lp: i64 = gA_lossval(tape,vals,st,pp) 94 let lm: i64 = gA_lossval(tape,vals,st,pm) 95 return ((lp - lm) * Q16) / (2 * h) 96} 97 98// ---- Gate B/C: train W[1x2] to target W*=[1.0,-0.5] via the GENERAL tape. SSE over 4 samples. 99func gB_build(tape: *i64, vals: *i64, st: *i64, Wp: *i64, X: *i64, Y: *i64, T: i64, outW: *i64) -> i64 { 100 st[0]=0; st[1]=0 101 let nW: i64 = nfa_leaf(tape,vals,st,1,2,Wp,0) 102 outW[0]=nW 103 var root: i64 = 0 - 1 104 var t: i64 = 0 105 while t < T { 106 let nx: i64 = nfa_leaf(tape,vals,st,2,1,X,t*2) 107 let np: i64 = nfa_matvec(tape,vals,st,nW,nx) 108 let ny: i64 = nfa_leaf(tape,vals,st,1,1,Y,t) 109 let nm: i64 = nfa_mse(tape,vals,st,np,ny) 110 if root < 0 { root = nm } else { root = nfa_vadd(tape,vals,st,root,nm) } 111 t = t + 1 112 } 113 return root 114} 115func gB_train(tape: *i64, vals: *i64, grads: *i64, st: *i64, epochs: i64, lr_q: i64, Wout: *i64, lf: *i64, ll: *i64) -> i64 { 116 let T: i64 = 4 117 let Wt: *i64 = sys_mmap(2*8) as *i64; Wt[0]=65536; Wt[1]=0-32768 // target [1.0, -0.5] Q16 118 let X: *i64 = sys_mmap(T*2*8) as *i64 119 X[0]=65536; X[1]=65536; X[2]=131072; X[3]=32768; X[4]=32768; X[5]=131072; X[6]=98304; X[7]=65536 120 let Y: *i64 = sys_mmap(T*8) as *i64 121 var t: i64 = 0 122 while t < T { Y[t] = (Wt[0]*X[t*2] + Wt[1]*X[t*2+1]) >> 16; t = t + 1 } // same accumulate-shift as matvec 123 let Wp: *i64 = sys_mmap(2*8) as *i64; Wp[0]=0; Wp[1]=0 // learn from ZERO 124 let outW: *i64 = sys_mmap(8) as *i64 125 let g: *i64 = sys_mmap(2*8) as *i64 126 var ep: i64 = 0 127 while ep < epochs { 128 let root: i64 = gB_build(tape,vals,st,Wp,X,Y,T,outW) 129 nfa_backward(tape,vals,grads,st[0],root) 130 if ep == 0 { *lf = nfa_val(tape,vals,root,0) } 131 *ll = nfa_val(tape,vals,root,0) 132 g[0] = nfa_grad(tape,grads,outW[0],0) 133 g[1] = nfa_grad(tape,grads,outW[0],1) 134 nfa_sgd(Wp, g, 2, lr_q) 135 ep = ep + 1 136 } 137 Wout[0]=Wp[0]; Wout[1]=Wp[1] 138 return 0 139} 140 141func main() -> i64 { 142 g_puts("nx_nofloat_autograd gate (GENERAL reverse-mode autograd in PURE INTEGER Q16 -- no float, MEASURED)\n" as *u8) 143 var pass: i64 = 0; var total: i64 = 0 144 let tape: *i64 = sys_mmap(256*7*8) as *i64 145 let vals: *i64 = sys_mmap(2048*8) as *i64 146 let grads: *i64 = sys_mmap(2048*8) as *i64 147 let st: *i64 = sys_mmap(2*8) as *i64 148 149 // ---------- Gate A: gradcheck ---------- 150 let p: *i64 = sys_mmap(6*8) as *i64 151 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 152 let ana: *i64 = sys_mmap(4*8) as *i64 153 gA_analytic(tape,vals,grads,st,p,ana) 154 let h: i64 = 512 // 1/128 in Q16 155 let tol_q: i64 = 2048 // 1/32 relative tolerance 156 let floor_q: i64 = 4096 // 1/16 grad-magnitude floor (so tiny grads don't blow up rel error) 157 var gradcheck_pass: i64 = 1 158 var worst_milli: i64 = 0 159 var pi: i64 = 0 160 while pi < 4 { 161 let fd: i64 = gA_fd(tape,vals,st,p,pi,h) 162 let num: i64 = g_abs(fd - ana[pi]) 163 var den: i64 = g_abs(ana[pi]); if den < floor_q { den = floor_q } 164 let thresh: i64 = (tol_q * den) >> 16 165 if num < thresh { g_puts(" [grad p" as *u8); g_pn(pi); g_puts("] ana_milli=" as *u8); g_pn(q_milli(ana[pi])); g_puts(" fd_milli=" as *u8); g_pn(q_milli(fd)); g_puts(" ok\n" as *u8) } else { gradcheck_pass = 0; g_puts(" [grad p" as *u8); g_pn(pi); g_puts("] ana_milli=" as *u8); g_pn(q_milli(ana[pi])); g_puts(" fd_milli=" as *u8); g_pn(q_milli(fd)); g_puts(" MISMATCH\n" as *u8) } 166 let rel_milli: i64 = (num * 1000) / den 167 if rel_milli > worst_milli { worst_milli = rel_milli } 168 pi = pi + 1 169 } 170 g_puts(" [measure] worst relative grad error = " as *u8); g_pn(worst_milli); g_puts(" / 1000 (tol=31/1000)\n" as *u8) 171 pass = pass + g_check("A: gradcheck -- tape reverse-sweep grads == finite differences (matvec+vadd+relu+mse)" as *u8, gradcheck_pass); total = total + 1 172 173 // ---------- Gate D: the gradcheck has teeth (neg-control) ---------- 174 // negate the analytic grad of param 0 and assert the SAME criterion now FAILS = a wrong grad is caught. 175 let fd0: i64 = gA_fd(tape,vals,st,p,0,h) 176 let bad: i64 = 0 - ana[0] 177 let num_bad: i64 = g_abs(fd0 - bad) 178 var den0: i64 = g_abs(ana[0]); if den0 < floor_q { den0 = floor_q } 179 let thresh0: i64 = (tol_q * den0) >> 16 180 var negcontrol_caught: i64 = 1 181 if num_bad < thresh0 { negcontrol_caught = 0 } 182 pass = pass + g_check("D: neg-control -- a deliberately WRONG gradient is rejected by the gradcheck (teeth)" as *u8, negcontrol_caught); total = total + 1 183 184 // ---------- Gate B: a model provably learns (general tape reproduces nx_nn_train's result) ---------- 185 let Wfin: *i64 = sys_mmap(2*8) as *i64 186 let lf: *i64 = sys_mmap(8) as *i64 187 let ll: *i64 = sys_mmap(8) as *i64 188 gB_train(tape,vals,grads,st, 4000, 2048, Wfin, lf, ll) 189 g_puts(" [measure] SSE loss: start=" as *u8); g_pn(*lf); g_puts(" after 4000 GD steps=" as *u8); g_pn(*ll); g_puts("\n" as *u8) 190 g_puts(" learned W=[" as *u8); g_pn(Wfin[0]); g_puts("," as *u8); g_pn(Wfin[1]); g_puts("] vs target [65536,-32768] (Q16; fixed-point-floor-limited)\n" as *u8) 191 var learns_pass: i64 = 1 192 if (*ll) * 20 > (*lf) { learns_pass = 0 } // >= 95% loss reduction 193 if Wfin[0] < 39322 { learns_pass = 0 } // w0 >= 60% of 65536 194 if Wfin[1] > 0 - 19661 { learns_pass = 0 } // w1 <= -60% of -32768, correct sign 195 if (*lf) <= 0 { learns_pass = 0 } // started genuinely untrained 196 pass = pass + g_check("B: a model LEARNS in pure integer -- SSE collapses >=95% and W converges to target" as *u8, learns_pass); total = total + 1 197 198 // ---------- Gate C: bit-exact reproducible ---------- 199 let Wfin2: *i64 = sys_mmap(2*8) as *i64 200 let lf2: *i64 = sys_mmap(8) as *i64 201 let ll2: *i64 = sys_mmap(8) as *i64 202 gB_train(tape,vals,grads,st, 4000, 2048, Wfin2, lf2, ll2) 203 var bitexact_pass: i64 = 1 204 if Wfin2[0] != Wfin[0] { bitexact_pass = 0 } 205 if Wfin2[1] != Wfin[1] { bitexact_pass = 0 } 206 pass = pass + g_check("C: bit-exact -- training twice from zero gives IDENTICAL integer weights (determinism)" as *u8, bitexact_pass); total = total + 1 207 208 // ---------- emit ---------- 209 var okall: i64 = 0 210 if pass == total { okall = 1 } 211 let r: *i64 = sys_mmap(10*8) as *i64 212 r[0]=gradcheck_pass; r[1]=worst_milli; r[2]=negcontrol_caught; r[3]=learns_pass 213 r[4]=q_milli(Wfin[0]); r[5]=q_milli(Wfin[1]); r[6]=(*lf); r[7]=(*ll); r[8]=bitexact_pass; r[9]=okall 214 nl_emit(1, r) 215 let logf: i64 = sys_openat_append(NLOG, 420) 216 if logf >= 0 { nl_emit(logf, r); sys_close(logf) } 217 218 g_puts("---- nofloat_autograd gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 219 if okall == 1 { g_puts("verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 220 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 221}