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}