code wiki / _hdl_build / nx_intfp_mlp_gradcheck_gate.nx
nx_intfp_mlp_gradcheck_gate.nx source
↩ module page · 102 lines · 6155 B
1// nx_intfp_mlp_gradcheck_gate.nx -- SECOND brick of integer training: prove the CHAIN RULE in fixed-point.
2// A 2-layer MLP h=W1@x ; a=ReLU(h) ; y=W2@a ; L=sum(y^2) done ENTIRELY in Q16 integer, with full backprop
3// (gradients flow y<-a<-h<-W1 through the ReLU kink) and a gold-standard INTEGER finite-difference gradcheck on
4// BOTH W1 (the DEEP matrix -- its gradient traverses the whole chain) and W2. If the deep gradients match numeric,
5// the fixed-point chain rule is sound and a multi-op integer autograd tape is buildable. No float anywhere.
6// Q16 (S=2^16): h_q=(W1_q@x_q)>>16 ; a=max(0,h) ; y_q=(W2_q@a_q)>>16 ; L_q32=sum(y_q^2).
7// gy=2y ; gW2=(gy*a)>>16 ; ga=(sum_o gy*W2)>>16 ; gh=ga*[h>0] ; gW1=(gh*x)>>16 (all Q16 of the real grad)
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10
11func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func wn(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} let t: *u8=sys_mmap(24); var k: i64=0; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let o: *u8=sys_mmap(24); var q: i64=k-1; var i: i64=0; while q>=0{o[i]=t[q];i=i+1;q=q-1} sys_write(1,o,i); return 0 }
13func iabs(v: i64) -> i64 { if v<0 { return 0-v } return v }
14
15const S: i64 = 65536
16const K: i64 = 3
17const H: i64 = 4
18const O: i64 = 2
19
20// forward, fills h,a,y buffers; returns L_q32 = sum(y_q^2)
21func fwd(W1: *i64, x: *i64, W2: *i64, hb: *i64, ab: *i64, yb: *i64) -> i64 {
22 var i: i64=0
23 while i<H {
24 var acc: i64=0; var kk: i64=0
25 while kk<K { acc=acc + W1[i*K+kk]*x[kk]; kk=kk+1 }
26 let hv: i64=acc/S; hb[i]=hv; if hv<0 { ab[i]=0 } else { ab[i]=hv } // ReLU
27 i=i+1
28 }
29 var Lq: i64=0; var o: i64=0
30 while o<O {
31 var acc2: i64=0; var j: i64=0
32 while j<H { acc2=acc2 + W2[o*H+j]*ab[j]; j=j+1 }
33 let y: i64=acc2/S; yb[o]=y; Lq=Lq + y*y; o=o+1
34 }
35 return Lq
36}
37
38func main() -> i64 {
39 w("=== nx_intfp_mlp_gradcheck: Q16 2-layer MLP (W1->ReLU->W2->L=sum y^2) -- CHAIN-RULE gradcheck, no float ===\n\n" as *u8)
40 let W1: *i64=sys_mmap(H*K*8) as *i64; let W2: *i64=sys_mmap(O*H*8) as *i64
41 let x: *i64=sys_mmap(K*8) as *i64
42 let hb: *i64=sys_mmap(H*8) as *i64; let ab: *i64=sys_mmap(H*8) as *i64; let yb: *i64=sys_mmap(O*8) as *i64
43 // fill: mixed signs so some ReLU units are DEAD (exercises the kink/branch)
44 var i: i64=0; while i<H { var kk: i64=0; while kk<K { W1[i*K+kk]=((i*2-kk*3+1)*S)/10; kk=kk+1 } i=i+1 }
45 var o: i64=0; while o<O { var j: i64=0; while j<H { W2[o*H+j]=((o-j+2)*S)/10; j=j+1 } o=o+1 }
46 var kk2: i64=0; while kk2<K { x[kk2]=((kk2+2)*S)/10; kk2=kk2+1 } // 0.2,0.3,0.4
47
48 let L0: i64=fwd(W1, x, W2, hb, ab, yb)
49 w(" forward L_q32=" as *u8); wn(L0); w(" h_q=[" as *u8); i=0; while i<H { wn(hb[i]); if i<H-1 { w("," as *u8) } i=i+1 } w("] a_q=[" as *u8); i=0; while i<H { wn(ab[i]); if i<H-1 { w("," as *u8) } i=i+1 } w("] (dead ReLU units test the chain)\n\n" as *u8)
50
51 // analytic grads at base point
52 let gy: *i64=sys_mmap(O*8) as *i64; o=0; while o<O { gy[o]=2*yb[o]; o=o+1 }
53 let gW2: *i64=sys_mmap(O*H*8) as *i64; o=0; while o<O { var j: i64=0; while j<H { gW2[o*H+j]=(gy[o]*ab[j])/S; j=j+1 } o=o+1 }
54 let ga: *i64=sys_mmap(H*8) as *i64; var j2: i64=0; while j2<H { var acc: i64=0; o=0; while o<O { acc=acc + gy[o]*W2[o*H+j2]; o=o+1 } ga[j2]=acc/S; j2=j2+1 }
55 let gh: *i64=sys_mmap(H*8) as *i64; i=0; while i<H { if hb[i]<0 { gh[i]=0 } else { gh[i]=ga[i] } i=i+1 }
56 let gW1: *i64=sys_mmap(H*K*8) as *i64; i=0; while i<H { var kk: i64=0; while kk<K { gW1[i*K+kk]=(gh[i]*x[kk])/S; kk=kk+1 } i=i+1 }
57
58 let DELTA: i64=66; let TOLP: i64=80
59 var npass: i64=0; var ncell: i64=0; var worst: i64=0
60
61 // gradcheck W2 (shallow)
62 w(" --- W2 (shallow) --- o j analytic numeric rel(permille)\n" as *u8)
63 o=0
64 while o<O {
65 var j: i64=0
66 while j<H {
67 let ana: i64=gW2[o*H+j]; let save: i64=W2[o*H+j]
68 W2[o*H+j]=save+DELTA; let Lp: i64=fwd(W1,x,W2,hb,ab,yb)
69 W2[o*H+j]=save-DELTA; let Lm: i64=fwd(W1,x,W2,hb,ab,yb)
70 W2[o*H+j]=save; let d: i64=fwd(W1,x,W2,hb,ab,yb)
71 let num: i64=(Lp-Lm)/(2*DELTA); let rel: i64=(iabs(num-ana)*1000)/(iabs(ana)+100)
72 w(" " as *u8); wn(o); w(" " as *u8); wn(j); w(" " as *u8); wn(ana); w(" " as *u8); wn(num); w(" " as *u8); wn(rel)
73 ncell=ncell+1; if rel<=TOLP { npass=npass+1; w(" ok\n" as *u8) } else { w(" FAIL\n" as *u8) }
74 if rel>worst { worst=rel }
75 j=j+1
76 }
77 o=o+1
78 }
79 // gradcheck W1 (DEEP -- gradient flows through ReLU and W2)
80 w(" --- W1 (DEEP, thru ReLU+W2) --- i k analytic numeric rel(permille)\n" as *u8)
81 i=0
82 while i<H {
83 var kk: i64=0
84 while kk<K {
85 let ana: i64=gW1[i*K+kk]; let save: i64=W1[i*K+kk]
86 W1[i*K+kk]=save+DELTA; let Lp: i64=fwd(W1,x,W2,hb,ab,yb)
87 W1[i*K+kk]=save-DELTA; let Lm: i64=fwd(W1,x,W2,hb,ab,yb)
88 W1[i*K+kk]=save; let d: i64=fwd(W1,x,W2,hb,ab,yb)
89 let num: i64=(Lp-Lm)/(2*DELTA); let rel: i64=(iabs(num-ana)*1000)/(iabs(ana)+100)
90 w(" " as *u8); wn(i); w(" " as *u8); wn(kk); w(" " as *u8); wn(ana); w(" " as *u8); wn(num); w(" " as *u8); wn(rel)
91 ncell=ncell+1; if rel<=TOLP { npass=npass+1; w(" ok\n" as *u8) } else { w(" FAIL\n" as *u8) }
92 if rel>worst { worst=rel }
93 kk=kk+1
94 }
95 i=i+1
96 }
97 w("\n CHAIN-RULE gradcheck: " as *u8); wn(npass); w("/" as *u8); wn(ncell); w(" cells within " as *u8); wn(TOLP); w(" permille; worst=" as *u8); wn(worst); w("\n" as *u8)
98 w("NX-INTFP-MLP-GRADCHECK verdict=" as *u8)
99 if npass==ncell { w("GREEN passes=" as *u8); wn(npass); w("/" as *u8); wn(ncell); w(" -- fixed-point CHAIN RULE proven (deep grads thru ReLU); integer tape is buildable\n" as *u8) }
100 else { w("RED passes=" as *u8); wn(npass); w("/" as *u8); wn(ncell); w(" -- chain-rule Q-format bug\n" as *u8) }
101 return 0
102}