code wiki / _hdl_build / nx_nofloat_adamw32_gate.nx

nx_nofloat_adamw32_gate.nx source

↩ module page · 78 lines · 5029 B

1// nx_nofloat_adamw32_gate.nx -- the REAL win from the R4 scale-up work: a numerically STABLE fixed-point Adam. 2// DIAGNOSIS: the Q16 AdamW (lib nfa_adamw) DIVERGES/STALLS on small gradients because g^2 UNDERFLOWS in Q16 -- 3// e.g. g=0.06 (Q16 3932) -> qmul(g,g)=235 (0.0036) and the (1-b2)*g^2 increment qmul(66,235)=0, so the 2nd 4// moment v never accumulates, the denominator collapses to eps, and the update mhat/eps explodes (or stalls). 5// FIX: accumulate v in Q32 and compute g^2 = g*g at FULL precision (no >>16) so small gradients survive. 6// T1: Q32-moment AdamW CONVERGES on a small-gradient fit (loss -> ~0). 7// T2 (measured exceed): Q32 final loss << Q16 final loss on the SAME problem (the underflow fix is real). 8// Self-contained controlled experiment (n=8 fit, hand-coded grad 2*(w-t), no transformer). Pure integer. 9// expect_exit: 0 Sovereign: nx_nofloat_autograd (for nfa_adamw + nfa_isqrt) + nx_syscalls. 10import "nx_nofloat_autograd.nx" 11import "nx_syscalls.nx" 12import "nx_gate_emit_lib.nx" 13const Q16: i64 = 65536 14 15 16 17// Q32-moment AdamW (the fix): v in Q32, g^2 = g*g full precision; sqrt(vhat_Q32) via isqrt = Q16 sqrt directly. 18func adamw32_one(w: *i64, g: *i64, m: *i64, v: *i64, n: i64, lr: i64, b1: i64, b2: i64, eps: i64, t: i64) -> i64 { 19 var c1: i64=65536; var c2: i64=65536; var k: i64=0 20 while k<t { if c2==0 { k=t } else { c1=(c1*b1)>>16; c2=(c2*b2)>>16; k=k+1 } } 21 let bc1: i64=65536-c1; let bc2: i64=65536-c2 22 var i: i64=0 23 while i<n { 24 m[i] = ((b1*m[i])>>16) + (((65536-b1)*g[i])>>16) // m Q16 25 let g2: i64 = g[i]*g[i] // Q32 -- FULL precision, no underflow 26 v[i] = ((b2*v[i])>>16) + (((65536-b2)*g2)>>16) // v Q32 27 var mh: i64=m[i]; if bc1>0 { mh=(m[i]<<16)/bc1 } 28 var vh: i64=v[i]; if bc2>0 { vh=(v[i]<<16)/bc2 } 29 if vh<0 { vh=0 } 30 let sq: i64 = nfa_isqrt(vh) // sqrt of Q32 = Q16 sqrt directly 31 let denom: i64 = sq + eps 32 var upd: i64=0; if denom>0 { upd=(mh<<16)/denom } 33 w[i] = w[i] - ((lr*upd)>>16) 34 i=i+1 35 } 36 return 0 37} 38// loss = sum (w-t)^2 in Q32 (raw d*d, so tiny residuals don't underflow the metric itself) 39func loss_q32(w: *i64, t: *i64, n: i64) -> i64 { var s: i64=0; var i: i64=0; while i<n { let d: i64=w[i]-t[i]; s=s+d*d; i=i+1 } return s } 40// grad of (w-t)^2 = 2(w-t), exact in Q16 (linear) 41func fill_grad(w: *i64, t: *i64, g: *i64, n: i64) -> i64 { var i: i64=0; while i<n { g[i]=2*(w[i]-t[i]); i=i+1 } return 0 } 42 43func main() -> i64 { 44 g_puts("nx_nofloat_adamw32 gate (Q32 moments FIX the Q16 AdamW g^2-underflow divergence)\n" as *u8) 45 let n: i64=8; let steps: i64=4000 46 let t: *i64 = sys_mmap(n*8) as *i64 47 t[0]=1966; t[1]=3277; t[2]=1311; t[3]=2621; t[4]=3932; t[5]=655; t[6]=2294; t[7]=2949 // small targets (~0.01-0.06) 48 let g: *i64 = sys_mmap(n*8) as *i64 49 50 // ---- Q16 AdamW (lib nfa_adamw): small gradients underflow g^2 ---- 51 let w16: *i64 = sys_mmap(n*8) as *i64; let m16: *i64 = sys_mmap(n*8) as *i64; let v16: *i64 = sys_mmap(n*8) as *i64 52 var z: i64=0; while z<n { w16[z]=0; m16[z]=0; v16[z]=0; z=z+1 } 53 var s1: i64=0 54 while s1<steps { fill_grad(w16,t,g,n); nfa_adamw(w16,g,m16,v16,n,655,58982,65470,66,0,s1+1); s1=s1+1 } 55 let l16: i64 = loss_q32(w16,t,n) 56 57 // ---- Q32 AdamW (the fix) ---- 58 let w32: *i64 = sys_mmap(n*8) as *i64; let m32: *i64 = sys_mmap(n*8) as *i64; let v32: *i64 = sys_mmap(n*8) as *i64 59 z=0; while z<n { w32[z]=0; m32[z]=0; v32[z]=0; z=z+1 } 60 var s2: i64=0 61 while s2<steps { fill_grad(w32,t,g,n); adamw32_one(w32,g,m32,v32,n,655,58982,65470,66,s2+1); s2=s2+1 } 62 let l32: i64 = loss_q32(w32,t,n) 63 64 g_puts(" [measure] final fit loss (sum (w-t)^2, Q32 raw; lower=better): Q16-AdamW=" as *u8); g_pn(l16); g_puts(" Q32-AdamW=" as *u8); g_pn(l32); g_puts("\n" as *u8) 65 66 var pass: i64=0; var total: i64=0 67 var t1: i64=0; if l32 < 30000000 { t1=1 } // Q32 converged: sum d^2 < 3e7 (Q32) ~= avg |w-t| < ~0.001 68 pass=pass+g_check("T1: Q32-moment AdamW CONVERGES on small-gradient fit (loss -> ~0)" as *u8, t1); total=total+1 69 var t2: i64=0; if l32*10 < l16 { t2=1 } // Q32 at least 10x better than Q16 = the underflow fix is real 70 pass=pass+g_check("T2: MEASURED EXCEED -- Q32 final loss << Q16 final loss (g^2-underflow fixed)" as *u8, t2); total=total+1 71 72 var okall: i64=0; if pass==total { okall=1 } 73 let logf: i64 = sys_openat_append("knowledge/status/nofloat_adamw32.log" as *u8, 420) 74 if logf >= 0 { let x0: i64=sys_write(logf,"NOFLOATADAMW32 q32-moments fix q16 adam underflow measured\n" as *u8,58); sys_close(logf) } 75 g_puts("---- adamw32 gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 76 if okall==1 { g_puts("verdict=GREEN (Q32-moment AdamW: a numerically STABLE fixed-point optimizer; fixes the Q16 underflow)\n" as *u8); sys_exit(0); return 0 } 77 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 78}