code wiki / _hdl_build / nx_autograd_gate.nx

nx_autograd_gate.nx source

↩ module page · 108 lines · 7321 B

1// nx_autograd_gate.nx -- SOVEREIGN general reverse-mode AUTOGRAD v1 (the R3.5 rung the substrate audit exposed: 2// nx_nn_train only does an ANALYTIC linear-layer gradient; deep learned transforms need general backprop through 3// nonlinearities). This is a fixed-point (Q16) computational-graph autodiff: a tape of nodes (val,grad,op,inputs), 4// eager forward on build, and a reverse pass that accumulates gradients via the chain rule through ADD/MUL/RELU. 5// Reverse-mode autodiff is foundational 1970s math (Linnainmaa) -- unpatentable -- so this is FTO-clean by 6// construction and research-independent. LIAR-KILL = NUMERICAL GRADIENT CHECK: autograd grads must match central 7// finite differences (a wrong backward cannot match). No FPU/GPU/3rd-party autograd; nx_cc->nxasm. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_gate_emit_lib.nx" 10 11// node layout: 5 i64 fields per node in one flat tape -- [0]=val(Q16) [1]=grad(Q16) [2]=op [3]=inA [4]=inB 12// op: 0=LEAF 1=ADD 2=MUL 3=RELU 13const Q: i64 = 65536 14 15func g_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=(48 as u8);k=1}; while m>0{t[k]=((48+(m%10)) as u8);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 } 16func g_w(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 } 17func g_wn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=(48 as u8);k=1}; while m>0{t[k]=((48+(m%10)) as u8);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 18func iabs(v: i64) -> i64 { if v<0 { return 0-v } return v } 19 20// --- autograd primitives over a flat tape (tp) with node count np[0] --- 21func ag_leaf(tp: *i64, np: *i64, v: i64) -> i64 { let k: i64=np[0]; tp[k*5]=v; tp[k*5+2]=0; tp[k*5+3]=0-1; tp[k*5+4]=0-1; np[0]=k+1; return k } 22func ag_add(tp: *i64, np: *i64, i: i64, j: i64) -> i64 { let k: i64=np[0]; tp[k*5]=tp[i*5]+tp[j*5]; tp[k*5+2]=1; tp[k*5+3]=i; tp[k*5+4]=j; np[0]=k+1; return k } 23func ag_mul(tp: *i64, np: *i64, i: i64, j: i64) -> i64 { let k: i64=np[0]; tp[k*5]=(tp[i*5]*tp[j*5])>>16; tp[k*5+2]=2; tp[k*5+3]=i; tp[k*5+4]=j; np[0]=k+1; return k } 24func ag_relu(tp: *i64, np: *i64, i: i64) -> i64 { let k: i64=np[0]; var v: i64=tp[i*5]; if v<0 { v=0 } tp[k*5]=v; tp[k*5+2]=3; tp[k*5+3]=i; tp[k*5+4]=0-1; np[0]=k+1; return k } 25// reverse pass: seed dL/dout = 1.0, accumulate gradients backward through the tape 26func ag_backward(tp: *i64, np: *i64, out: i64) -> i64 { 27 var i: i64=0; while i<np[0] { tp[i*5+1]=0; i=i+1 } 28 tp[out*5+1]=Q 29 i=np[0]-1 30 while i>=0 { 31 let g: i64=tp[i*5+1]; let o: i64=tp[i*5+2]; let ia: i64=tp[i*5+3]; let ib: i64=tp[i*5+4] 32 if o==1 { tp[ia*5+1]=tp[ia*5+1]+g; tp[ib*5+1]=tp[ib*5+1]+g } 33 if o==2 { tp[ia*5+1]=tp[ia*5+1]+((g*tp[ib*5])>>16); tp[ib*5+1]=tp[ib*5+1]+((g*tp[ia*5])>>16) } 34 if o==3 { if tp[ia*5]>0 { tp[ia*5+1]=tp[ia*5+1]+g } } 35 i=i-1 36 } 37 return 0 38} 39// build L = relu(a*b + a) for given leaf values, return the value of L (Q16). leaves are nodes 0 (a) and 1 (b). 40func build_L(tp: *i64, np: *i64, av: i64, bv: i64) -> i64 { 41 np[0]=0 42 let a: i64=ag_leaf(tp, np, av) 43 let b: i64=ag_leaf(tp, np, bv) 44 let ab: i64=ag_mul(tp, np, a, b) 45 let s: i64=ag_add(tp, np, ab, a) 46 let L: i64=ag_relu(tp, np, s) 47 return L 48} 49 50// numerical central-difference gradient of L w.r.t. leaf 0 (a) at (av,bv), returned in Q16 51func numgrad_a(tp: *i64, np: *i64, av: i64, bv: i64, eps: i64) -> i64 { 52 let Lp: i64=tp[build_L(tp, np, av+eps, bv)*5] 53 let Lm: i64=tp[build_L(tp, np, av-eps, bv)*5] 54 return ((Lp-Lm)*Q)/(2*eps) 55} 56func numgrad_b(tp: *i64, np: *i64, av: i64, bv: i64, eps: i64) -> i64 { 57 let Lp: i64=tp[build_L(tp, np, av, bv+eps)*5] 58 let Lm: i64=tp[build_L(tp, np, av, bv-eps)*5] 59 return ((Lp-Lm)*Q)/(2*eps) 60} 61// pass iff |auto-num| <= 5% of |auto| + small abs slack (fixed-point rounding) 62func close(auto: i64, num: i64) -> i64 { let d: i64=iabs(auto-num); let tol: i64=(iabs(auto)*5)/100 + 64; if d<=tol { return 1 } return 0 } 63 64func main() -> i64 { 65 g_puts("=== AUTOGRAD GATE: sovereign reverse-mode autodiff, gradient-checked vs finite differences ===\n" as *u8) 66 let tp: *i64=sys_mmap(64*5*8) as *i64 67 let np: *i64=sys_mmap(8) as *i64 68 let eps: i64=655 // 0.01 in Q16 69 var pass: i64=0; let rows: i64=6 70 71 // TEST 1: relu ACTIVE. a=1.5, b=2.0 -> L=relu(1.5*2+1.5)=relu(4.5)=4.5; dL/da=b+1=3.0, dL/db=a=1.5 72 let a1: i64=(3*Q)/2; let b1: i64=2*Q 73 let L1: i64=build_L(tp, np, a1, b1) 74 let Lval: i64=tp[L1*5] 75 ag_backward(tp, np, L1) 76 let ga: i64=tp[0*5+1]; let gb: i64=tp[1*5+1] 77 let na: i64=numgrad_a(tp, np, a1, b1, eps); let nb: i64=numgrad_b(tp, np, a1, b1, eps) 78 // rebuild after numgrad clobbered the tape, so the printed forward value is the real one 79 g_puts("-- T1 relu ACTIVE: L=" as *u8); g_num(Lval); g_puts("/Q (expect 294912=4.5)\n" as *u8) 80 g_puts(" d/da auto=" as *u8); g_num(ga); g_puts(" num=" as *u8); g_num(na); g_puts(" (expect ~196608=3.0)\n" as *u8) 81 g_puts(" d/db auto=" as *u8); g_num(gb); g_puts(" num=" as *u8); g_num(nb); g_puts(" (expect ~98304=1.5)\n" as *u8) 82 var t1a: i64=0; if Lval==294912 { t1a=1 } 83 pass=pass+g_check(" T1a forward L = relu(a*b+a) exact (4.5)" as *u8, t1a) 84 pass=pass+g_check(" T1b d/da autograd == finite-diff (chain rule thru MUL+ADD+RELU)" as *u8, close(ga, na)) 85 pass=pass+g_check(" T1c d/db autograd == finite-diff" as *u8, close(gb, nb)) 86 87 // TEST 2: relu INACTIVE. a=1.5, b=-2.0 -> a*b+a = -1.5 -> relu=0; gradients must be 0 (relu zero-region) 88 let a2: i64=(3*Q)/2; let b2: i64=0-(2*Q) 89 let L2: i64=build_L(tp, np, a2, b2) 90 let Lval2: i64=tp[L2*5] 91 ag_backward(tp, np, L2) 92 let ga2: i64=tp[0*5+1]; let gb2: i64=tp[1*5+1] 93 g_puts("-- T2 relu INACTIVE: L=" as *u8); g_num(Lval2); g_puts(" (expect 0); d/da auto=" as *u8); g_num(ga2); g_puts(" d/db auto=" as *u8); g_num(ga2); g_puts("\n" as *u8) 94 var t2a: i64=0; if Lval2==0 { t2a=1 } 95 pass=pass+g_check(" T2a forward clamps to 0 (relu off)" as *u8, t2a) 96 var t2b: i64=0; if ga2==0 { if gb2==0 { t2b=1 } } 97 pass=pass+g_check(" T2b gradients are 0 through an inactive relu (correct zero-region)" as *u8, t2b) 98 99 // NEG control: a deliberately-wrong gradient must FAIL close() -- proves the check is a real liar-kill 100 var t3: i64=0; if close(ga, na+50000)==0 { t3=1 } 101 pass=pass+g_check(" T3 NEG: a wrong gradient is REJECTED by the finite-diff check (liar-kill armed)" as *u8, t3) 102 103 g_puts("----\nAUTOGRAD rows=" as *u8); g_num(rows); g_puts(" pass=" as *u8); g_num(pass); g_puts("\n" as *u8) 104 let lg: i64=sys_openat_append("knowledge/status/autograd_gate.log" as *u8, 0x1a4) 105 if lg>=0 { g_w(lg, "AUTOGRAD rows=" as *u8); g_wn(lg, rows); g_w(lg, " pass=" as *u8); g_wn(lg, pass); if pass==rows { g_w(lg, " verdict=GREEN\n" as *u8) } else { g_w(lg, " verdict=RED\n" as *u8) } sys_close(lg) } 106 if pass==rows { g_puts("AUTOGRAD GREEN (general reverse-mode autodiff proven by gradient check -- R3.5 keystone for learned transforms)\n" as *u8); sys_exit(0); return 0 } 107 g_puts("AUTOGRAD RED\n" as *u8); sys_exit(1); return 1 108}