code wiki / _hdl_build / nx_nofloat_rope_gate.nx

nx_nofloat_rope_gate.nx source

↩ module page · 194 lines · 10737 B

1// nx_nofloat_rope_gate.nx -- HARD-EVIDENCE gate for RoPE (rotary position embedding) backward + RoPE composed 2// into the attention path. Advances CAP-NF-ATTN-FULL: with RoPE the attention is positionally-aware AND still 3// fully differentiable in pure integer Q16. RoPE is parameter-free (orthogonal rotation), so its backward is 4// the inverse rotation -- here proven correct by finite differences AND inside the full attention block. 5// 6// A1 RoPE gradcheck : loss=mse(RoPE(x), t); tape grad dL/dx == central finite difference. 7// A2 attention+RoPE gradcheck: X->{Q,K,V}; RoPE(Q),RoPE(K); S=QK^T; scale; causal softmax; O=A.V; mse; 8// gradcheck dL/dWq -- the gradient flowing through RoPE + softmax + both matmuls (the real RoPE-attn backward). 9// D neg-control teeth : a wrong RoPE grad is rejected. 10// C bit-exact : the attention+RoPE gradient is identical across two runs (determinism). 11// 12// Evidence -> knowledge/status/nofloat_rope.log. Sovereign: imports nx_nofloat_autograd (pure integer) + nx_syscalls. 13// HONEST scope: single-head; output-projection + multi-head/GQA remain (bookkeeping over matmul+vadd). expect_exit: 0 14import "nx_nofloat_autograd.nx" 15import "nx_syscalls.nx" 16import "nx_gate_emit_lib.nx" 17import "nx_gate_verdict.nx" 18 19const RLOG: *u8 = "knowledge/status/nofloat_rope.log" 20const Q16: i64 = 65536 21 22 23func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 24func r_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 } 25func r_wn(fd: i64, v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(fd,"-" as *u8,1);m=0-m} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;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{b[i]=t[k-1-i];i=i+1} sys_write(fd,b,k); return 0 } 26 27// ---- A1: RoPE gradcheck (loss = mse(RoPE(x), target)) ---- 28func rope_loss(tape: *i64, vals: *i64, st: *i64, Xs: *i64, T: i64, hd: i64, Ts: *i64, leaves: *i64) -> i64 { 29 st[0]=0; st[1]=0 30 let nX: i64 = nfa_leaf(tape,vals,st,T,hd,Xs,0) 31 let nR: i64 = nfa_rope(tape,vals,st,nX) 32 let nt: i64 = nfa_leaf(tape,vals,st,T,hd,Ts,0) 33 let loss: i64 = nfa_mse(tape,vals,st,nR,nt) 34 leaves[0]=nX 35 return loss 36} 37func rope_lossval(tape: *i64, vals: *i64, st: *i64, Xs: *i64, T: i64, hd: i64, Ts: *i64) -> i64 { 38 let lv: *i64 = sys_mmap(8) as *i64 39 let loss: i64 = rope_loss(tape,vals,st,Xs,T,hd,Ts,lv) 40 return nfa_val(tape,vals,loss,0) 41} 42 43// ---- A2: attention + RoPE forward (leaves[0..3]=nWq,nWk,nWv,nX) -> O node ---- 44func ar_fwd(tape: *i64, vals: *i64, st: *i64, Xs: *i64, Wqs: *i64, Wks: *i64, Wvs: *i64, T: i64, d: i64, scale: i64, leaves: *i64) -> i64 { 45 st[0]=0; st[1]=0 46 let nX: i64 = nfa_leaf(tape,vals,st,T,d,Xs,0) 47 let nWq: i64 = nfa_leaf(tape,vals,st,d,d,Wqs,0) 48 let nWk: i64 = nfa_leaf(tape,vals,st,d,d,Wks,0) 49 let nWv: i64 = nfa_leaf(tape,vals,st,d,d,Wvs,0) 50 let nQ: i64 = nfa_matmul(tape,vals,st,nX,nWq) 51 let nK: i64 = nfa_matmul(tape,vals,st,nX,nWk) 52 let nV: i64 = nfa_matmul(tape,vals,st,nX,nWv) 53 let nQr: i64 = nfa_rope(tape,vals,st,nQ) 54 let nKr: i64 = nfa_rope(tape,vals,st,nK) 55 let nS: i64 = nfa_matmul_nt(tape,vals,st,nQr,nKr) 56 let nSs: i64 = nfa_cmul(tape,vals,st,nS,scale) 57 let nA: i64 = nfa_softmax_rows(tape,vals,st,nSs,1) 58 let nO: i64 = nfa_matmul(tape,vals,st,nA,nV) 59 leaves[0]=nWq; leaves[1]=nWk; leaves[2]=nWv; leaves[3]=nX 60 return nO 61} 62func ar_loss(tape: *i64, vals: *i64, st: *i64, Xs: *i64, Wqs: *i64, Wks: *i64, Wvs: *i64, Ts: *i64, T: i64, d: i64, scale: i64, leaves: *i64) -> i64 { 63 let nO: i64 = ar_fwd(tape,vals,st,Xs,Wqs,Wks,Wvs,T,d,scale,leaves) 64 let nt: i64 = nfa_leaf(tape,vals,st,T,d,Ts,0) 65 return nfa_mse(tape,vals,st,nO,nt) 66} 67func ar_lossval(tape: *i64, vals: *i64, st: *i64, Xs: *i64, Wqs: *i64, Wks: *i64, Wvs: *i64, Ts: *i64, T: i64, d: i64, scale: i64) -> i64 { 68 let lv: *i64 = sys_mmap(4*8) as *i64 69 let loss: i64 = ar_loss(tape,vals,st,Xs,Wqs,Wks,Wvs,Ts,T,d,scale,lv) 70 return nfa_val(tape,vals,loss,0) 71} 72 73func main() -> i64 { 74 g_puts("nx_nofloat_rope gate (RoPE backward + RoPE-in-attention, PURE INTEGER Q16 -- MEASURED)\n" as *u8) 75 var pass: i64=0; var total: i64=0 76 let tape: *i64 = sys_mmap(512*7*8) as *i64 77 let vals: *i64 = sys_mmap(8192*8) as *i64 78 let grads: *i64 = sys_mmap(8192*8) as *i64 79 let st: *i64 = sys_mmap(2*8) as *i64 80 let h: i64 = 512 81 let floor_q: i64 = 4096 82 83 // ---- A1: RoPE gradcheck ---- 84 let T: i64 = 3; let hd: i64 = 2 85 let Xs: *i64 = sys_mmap(T*hd*8) as *i64; Xs[0]=32768; Xs[1]=49152; Xs[2]=0-16384; Xs[3]=65536; Xs[4]=24576; Xs[5]=0-40960 86 let Ts: *i64 = sys_mmap(T*hd*8) as *i64; Ts[0]=13107; Ts[1]=0-6554; Ts[2]=19661; Ts[3]=6554; Ts[4]=0-26214; Ts[5]=32768 87 let lv: *i64 = sys_mmap(8) as *i64 88 let loss1: i64 = rope_loss(tape,vals,st,Xs,T,hd,Ts,lv) 89 nfa_backward(tape,vals,grads,st[0],loss1) 90 let nX: i64 = lv[0] 91 var r1_ok: i64 = 1; var r1_worst: i64 = 0 92 var pi: i64 = 0 93 while pi < T*hd { 94 let ana: i64 = nfa_grad(tape,grads,nX,pi) 95 let xp: *i64 = sys_mmap(T*hd*8) as *i64 96 let xm: *i64 = sys_mmap(T*hd*8) as *i64 97 var z: i64=0 98 while z<T*hd { xp[z]=Xs[z]; xm[z]=Xs[z]; z=z+1 } 99 xp[pi]=Xs[pi]+h; xm[pi]=Xs[pi]-h 100 let lp: i64 = rope_lossval(tape,vals,st,xp,T,hd,Ts) 101 let lm: i64 = rope_lossval(tape,vals,st,xm,T,hd,Ts) 102 let fd: i64 = ((lp-lm)*Q16)/(2*h) 103 let num: i64 = g_abs(fd-ana) 104 var den: i64 = g_abs(ana); if den<floor_q { den=floor_q } 105 if num >= ((4096*den)>>16) { r1_ok=0 } // tol 1/16 106 let rel: i64 = (num*1000)/den 107 if rel>r1_worst { r1_worst=rel } 108 pi=pi+1 109 } 110 g_puts(" [measure] RoPE worst rel grad err = " as *u8); g_pn(r1_worst); g_puts(" /1000 (tol=62)\n" as *u8) 111 pass=pass+g_check("A1: RoPE gradcheck -- rotation backward (rotate by -angle) == finite differences" as *u8, r1_ok); total=total+1 112 113 // ---- A2: attention + RoPE gradcheck wrt Wq ---- 114 let d: i64 = 2; let scale: i64 = 46341 115 let aX: *i64 = sys_mmap(T*d*8) as *i64; aX[0]=32768; aX[1]=16384; aX[2]=0-16384; aX[3]=49152; aX[4]=65536; aX[5]=0-32768 116 let Wq: *i64 = sys_mmap(d*d*8) as *i64; Wq[0]=49152; Wq[1]=0-16384; Wq[2]=32768; Wq[3]=65536 117 let Wk: *i64 = sys_mmap(d*d*8) as *i64; Wk[0]=16384; Wk[1]=32768; Wk[2]=0-32768; Wk[3]=49152 118 let Wv: *i64 = sys_mmap(d*d*8) as *i64; Wv[0]=65536; Wv[1]=0-32768; Wv[2]=16384; Wv[3]=49152 119 let aT: *i64 = sys_mmap(T*d*8) as *i64; aT[0]=13107; aT[1]=0-6554; aT[2]=19661; aT[3]=6554; aT[4]=0-13107; aT[5]=26214 120 let lv2: *i64 = sys_mmap(4*8) as *i64 121 let loss2: i64 = ar_loss(tape,vals,st,aX,Wq,Wk,Wv,aT,T,d,scale,lv2) 122 nfa_backward(tape,vals,grads,st[0],loss2) 123 let nWq: i64 = lv2[0] 124 var r2_ok: i64 = 1; var r2_worst: i64 = 0 125 var qi: i64 = 0 126 while qi < d*d { 127 let ana: i64 = nfa_grad(tape,grads,nWq,qi) 128 let wqp: *i64 = sys_mmap(d*d*8) as *i64 129 let wqm: *i64 = sys_mmap(d*d*8) as *i64 130 var z: i64=0 131 while z<d*d { wqp[z]=Wq[z]; wqm[z]=Wq[z]; z=z+1 } 132 wqp[qi]=Wq[qi]+h; wqm[qi]=Wq[qi]-h 133 let lp: i64 = ar_lossval(tape,vals,st,aX,wqp,Wk,Wv,aT,T,d,scale) 134 let lm: i64 = ar_lossval(tape,vals,st,aX,wqm,Wk,Wv,aT,T,d,scale) 135 let fd: i64 = ((lp-lm)*Q16)/(2*h) 136 let num: i64 = g_abs(fd-ana) 137 var den: i64 = g_abs(ana); if den<floor_q { den=floor_q } 138 if num >= ((8192*den)>>16) { r2_ok=0 } // tol 1/8 (long chain through RoPE+softmax) 139 let rel: i64 = (num*1000)/den 140 if rel>r2_worst { r2_worst=rel } 141 qi=qi+1 142 } 143 g_puts(" [measure] attention+RoPE dL/dWq worst rel grad err = " as *u8); g_pn(r2_worst); g_puts(" /1000 (tol=125)\n" as *u8) 144 pass=pass+g_check("A2: attention+RoPE gradcheck -- dL/dWq through RoPE + causal-softmax + QK^T + A.V == finite diff" as *u8, r2_ok); total=total+1 145 146 // ---- D: neg-control teeth (RoPE) ---- 147 let lossd: i64 = rope_loss(tape,vals,st,Xs,T,hd,Ts,lv) 148 nfa_backward(tape,vals,grads,st[0],lossd) 149 let rana0: i64 = nfa_grad(tape,grads,lv[0],0) 150 let xp0: *i64 = sys_mmap(T*hd*8) as *i64 151 let xm0: *i64 = sys_mmap(T*hd*8) as *i64 152 var z0: i64=0 153 while z0<T*hd { xp0[z0]=Xs[z0]; xm0[z0]=Xs[z0]; z0=z0+1 } 154 xp0[0]=Xs[0]+h; xm0[0]=Xs[0]-h 155 let fdr0: i64 = ((rope_lossval(tape,vals,st,xp0,T,hd,Ts) - rope_lossval(tape,vals,st,xm0,T,hd,Ts))*Q16)/(2*h) 156 let bad: i64 = 0 - rana0 157 var rden0: i64 = g_abs(rana0); if rden0<floor_q { rden0=floor_q } 158 var caught: i64 = 1 159 if g_abs(fdr0-bad) < ((4096*rden0)>>16) { caught=0 } 160 pass=pass+g_check("D: neg-control -- a deliberately WRONG RoPE grad is rejected (teeth)" as *u8, caught); total=total+1 161 162 // ---- C: bit-exact (attention+RoPE gradient identical across two runs) ---- 163 let lvc: *i64 = sys_mmap(4*8) as *i64 164 let lossc: i64 = ar_loss(tape,vals,st,aX,Wq,Wk,Wv,aT,T,d,scale,lvc) 165 nfa_backward(tape,vals,grads,st[0],lossc) 166 let gA: i64 = nfa_grad(tape,grads,lvc[0],0); let gB: i64 = nfa_grad(tape,grads,lvc[0],1) 167 let lossc2: i64 = ar_loss(tape,vals,st,aX,Wq,Wk,Wv,aT,T,d,scale,lvc) 168 nfa_backward(tape,vals,grads,st[0],lossc2) 169 let gA2: i64 = nfa_grad(tape,grads,lvc[0],0); let gB2: i64 = nfa_grad(tape,grads,lvc[0],1) 170 var bitexact: i64 = 1 171 if gA2 != gA { bitexact=0 } 172 if gB2 != gB { bitexact=0 } 173 pass=pass+g_check("C: bit-exact -- attention+RoPE gradient identical across two runs (determinism)" as *u8, bitexact); total=total+1 174 175 // ---- emit ---- 176 var okall: i64=0; if pass==total { okall=1 } 177 let logf: i64 = sys_openat_append(RLOG, 420) 178 if logf >= 0 { 179 r_ws(logf,"NOFLOATROPE A1_rope=" as *u8); r_wn(logf,r1_ok); r_ws(logf," A2_attn_rope=" as *u8); r_wn(logf,r2_ok) 180 r_ws(logf," D=" as *u8); r_wn(logf,caught); r_ws(logf," C=" as *u8); r_wn(logf,bitexact) 181 if okall==1 { r_ws(logf," verdict=GREEN\n" as *u8) } else { r_ws(logf," verdict=RED\n" as *u8) } 182 sys_close(logf) 183 } 184 g_puts("---- nofloat_rope gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 185 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 186 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 187 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 188 let ctr__dry: *i64 = gv_ctr() 189 ctr__dry[0] = pass 190 ctr__dry[1] = total 191 let rc__dry: i64 = gv_verdict("NOFLOAT-ROPE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 192 sys_exit(rc__dry) 193 return rc__dry 194}