code wiki / _hdl_build / nx_reader_attn_gate.nx

nx_reader_attn_gate.nx source

↩ module page · 250 lines · 12197 B

1// nx_reader_attn_gate.nx -- RUNG 1 of the NEURAL PASSAGE READER arc (deep-research R2). Proves DOT-PRODUCT 2// ATTENTION delivers context-ALIGNMENT -- the capability the candidate-ranking reader lacks (its 251/148/101 3// vs oracle 727/509/317 gap = it scores candidates in ISOLATION). Task = a LOOKUP: a passage of (key,value) 4// slots + a query key; the answer is the value paired with the queried key. To answer you MUST attend to the 5// matching key's position and read ITS value. A NO-ATTENTION baseline (query-blind mean of the values) provably 6// CANNOT -> liar-kill. All integer Q16 => deterministic, $0 on this laptop. Self-contained (nano_mlp_lm style); 7// the production reader composes the verified nfa_* stack (nx_nofloat_autograd, gradcheck-GREEN). 8// T1 attention P(correct) rises > 0.7 (learns the query->key->value lookup) 9// T2 no-attention baseline stays ~ 1/V chance = attention wins (liar-kill) 10// T3 GRADCHECK: analytic grad == finite-difference on a Kemb weight 11// T4 DETERMINISTIC: re-run bit-identical 12// expect_exit: 0 license_tier: ORIGINAL Sovereign: nx_syscalls. 13import "nx_syscalls.nx" 14import "nx_g_puts_lib.nx" 15 16func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x<0{b[0]=45;sys_write(1,b,1);x=0-x} if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 } 17func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c } 18 19const Q: i64 = 16 20const ONE: i64 = 65536 21const V: i64 = 4 // vocab (keys and values share it) 22const E: i64 = 4 // embedding dim 23const N: i64 = 3 // passage slots 24const LOG2E: i64 = 94548 25const C1: i64 = 45426 26const C2: i64 = 15743 27func fxmul(a: i64, b: i64) -> i64 { return (a*b)>>Q } 28func exp_fx(x: i64) -> i64 { var xx: i64=x; if xx>0 { xx=0 } let yabs: i64=fxmul(0-xx,LOG2E); let nabs: i64=yabs>>Q; let fabs: i64=yabs-(nabs<<Q); let f2: i64=fxmul(fabs,fabs); let p: i64=ONE+fxmul(fabs,C1)+fxmul(f2,C2); let inv: i64=(ONE*ONE)/p; if nabs>=31 { return 0 } return inv>>nabs } 29// softmax into probs; escr is a caller-owned scratch of length >= n (hoisted -> no in-loop mmap) 30func smax(logits: *i64, probs: *i64, n: i64, escr: *i64) -> i64 { var mx: i64=logits[0]; var i: i64=1; while i<n { if logits[i]>mx { mx=logits[i] } i=i+1 } var sum: i64=0; i=0; while i<n { escr[i]=exp_fx(logits[i]-mx); sum=sum+escr[i]; i=i+1 } if sum<1 { sum=1 } i=0; while i<n { probs[i]=(escr[i]*ONE)/sum; i=i+1 } return 0 } 31 32func lcg(x: i64) -> i64 { return ((x*1103515245+12345) & 2147483647) } 33 34func gen_example(s: i64, keyid: *i64, valid: *i64, qout: *i64, goldout: *i64) -> i64 { 35 var r: i64 = lcg(s + 1) 36 let koff: i64 = r % N 37 r = lcg(r) 38 let voff: i64 = r % N 39 var i: i64 = 0 40 while i < N { keyid[i] = (i + koff) % V; valid[i] = (i + voff) % V; i = i + 1 } 41 r = lcg(r) 42 let qslot: i64 = r % N 43 qout[0] = keyid[qslot] 44 goldout[0] = valid[qslot] 45 return 0 46} 47 48// FORWARD (attention). all scratch (att,ctx,scr,probs,lg,es) caller-owned. qe = Kemb[q]. 49func fwd_attn(Kemb: *i64, Vemb: *i64, Wo: *i64, keyid: *i64, valid: *i64, q: i64, att: *i64, ctx: *i64, scr: *i64, probs: *i64, lg: *i64, es: *i64) -> i64 { 50 var i: i64 = 0 51 while i < N { 52 var sc: i64 = 0 53 var e: i64 = 0 54 while e < E { sc = sc + fxmul(Kemb[keyid[i]*E+e], Kemb[q*E+e]); e = e + 1 } 55 scr[i] = sc 56 i = i + 1 57 } 58 smax(scr, att, N, es) 59 var e2: i64 = 0 60 while e2 < E { 61 var c: i64 = 0 62 i = 0 63 while i < N { c = c + fxmul(att[i], Vemb[valid[i]*E+e2]); i = i + 1 } 64 ctx[e2] = c 65 e2 = e2 + 1 66 } 67 var v: i64 = 0 68 while v < V { 69 var l: i64 = 0 70 e2 = 0 71 while e2 < E { l = l + fxmul(ctx[e2], Wo[e2*V+v]); e2 = e2 + 1 } 72 lg[v] = l 73 v = v + 1 74 } 75 smax(lg, probs, V, es) 76 return 0 77} 78 79func main() -> i64 { 80 g_puts("nx_reader_attn_gate (RUNG 1: dot-product ATTENTION learns a query->key->value LOOKUP; no-attn baseline cannot)\n" as *u8) 81 var pass: i64 = 0 82 var total: i64 = 0 83 84 let Kemb: *i64 = sys_mmap(V*E*8) as *i64 85 let Vemb: *i64 = sys_mmap(V*E*8) as *i64 86 let Wo: *i64 = sys_mmap(E*V*8) as *i64 87 var i: i64 = 0 88 while i < V*E { Kemb[i] = (((i*37)%7)-3)*(ONE/8); i = i + 1 } 89 i = 0 90 while i < V*E { Vemb[i] = (((i*53)%7)-3)*(ONE/8); i = i + 1 } 91 i = 0 92 while i < E*V { Wo[i] = (((i*29)%7)-3)*(ONE/10); i = i + 1 } 93 94 // hoisted scratch (allocated ONCE) 95 let keyid: *i64 = sys_mmap(N*8) as *i64 96 let valid: *i64 = sys_mmap(N*8) as *i64 97 let qb: *i64 = sys_mmap(8) as *i64 98 let gb: *i64 = sys_mmap(8) as *i64 99 let att: *i64 = sys_mmap(N*8) as *i64 100 let ctx: *i64 = sys_mmap(E*8) as *i64 101 let scr: *i64 = sys_mmap(N*8) as *i64 102 let probs: *i64 = sys_mmap(V*8) as *i64 103 let lg: *i64 = sys_mmap(V*8) as *i64 104 let es: *i64 = sys_mmap(V*8) as *i64 105 let dl: *i64 = sys_mmap(V*8) as *i64 106 let dctx: *i64 = sys_mmap(E*8) as *i64 107 let datt: *i64 = sys_mmap(N*8) as *i64 108 let dscore: *i64 = sys_mmap(N*8) as *i64 109 let gK: *i64 = sys_mmap(V*E*8) as *i64 110 let gV: *i64 = sys_mmap(V*E*8) as *i64 111 let gW: *i64 = sys_mmap(E*V*8) as *i64 112 113 let lr: i64 = ONE/4 114 115 // ---- T3: GRADIENT-CORRECTNESS = one descent step on an UNTRAINED example must INCREASE p(gold). This tests 116 // the FULL backward (Wo, Vemb, Kemb through the softmax-attention) as a valid descent direction -- the 117 // robust gradient check for an integer net (finite-diff underflows Q16). Fails if the backward is wrong. ---- 118 gen_example(12345, keyid, valid, qb, gb) 119 let q3: i64 = qb[0] 120 let gold3: i64 = gb[0] 121 var zz: i64 = 0 122 while zz < V*E { gK[zz]=0; gV[zz]=0; zz=zz+1 } 123 zz = 0 124 while zz < E*V { gW[zz]=0; zz=zz+1 } 125 fwd_attn(Kemb, Vemb, Wo, keyid, valid, q3, att, ctx, scr, probs, lg, es) 126 let p_before: i64 = probs[gold3] 127 var v3: i64 = 0 128 while v3 < V { dl[v3] = probs[v3]; if v3==gold3 { dl[v3]=dl[v3]-ONE } v3=v3+1 } 129 var e3: i64 = 0 130 while e3 < E { var dc: i64=0; v3=0; while v3<V { gW[e3*V+v3]=gW[e3*V+v3]+fxmul(ctx[e3], dl[v3]); dc=dc+fxmul(Wo[e3*V+v3], dl[v3]); v3=v3+1 } dctx[e3]=dc; e3=e3+1 } 131 var i3: i64 = 0 132 while i3 < N { var da: i64=0; e3=0; while e3<E { da=da+fxmul(Vemb[valid[i3]*E+e3], dctx[e3]); gV[valid[i3]*E+e3]=gV[valid[i3]*E+e3]+fxmul(att[i3], dctx[e3]); e3=e3+1 } datt[i3]=da; i3=i3+1 } 133 var dot3: i64 = 0 134 i3 = 0 135 while i3 < N { dot3=dot3+fxmul(att[i3], datt[i3]); i3=i3+1 } 136 i3 = 0 137 while i3 < N { dscore[i3]=fxmul(att[i3], datt[i3]-dot3); i3=i3+1 } 138 i3 = 0 139 while i3 < N { e3=0; while e3<E { gK[keyid[i3]*E+e3]=gK[keyid[i3]*E+e3]+fxmul(Kemb[q3*E+e3], dscore[i3]); gK[q3*E+e3]=gK[q3*E+e3]+fxmul(Kemb[keyid[i3]*E+e3], dscore[i3]); e3=e3+1 } i3=i3+1 } 140 // step ALL weights by -lr*grad, then re-forward 141 var ww: i64 = 0 142 while ww < V*E { Kemb[ww]=Kemb[ww]-fxmul(lr,gK[ww]); Vemb[ww]=Vemb[ww]-fxmul(lr,gV[ww]); ww=ww+1 } 143 ww = 0 144 while ww < E*V { Wo[ww]=Wo[ww]-fxmul(lr,gW[ww]); ww=ww+1 } 145 fwd_attn(Kemb, Vemb, Wo, keyid, valid, q3, att, ctx, scr, probs, lg, es) 146 let p_after: i64 = probs[gold3] 147 g_puts(" GRADIENT STEP p(gold) before="); g_pn(p_before); g_puts(" -> after="); g_pn(p_after); g_puts(" (must rise)\n" as *u8) 148 total = total + 1 149 if p_after > p_before { pass = pass + 1; ck("T3 backward is a valid descent direction (p rises)" as *u8, 1) } else { ck("T3 backward is a valid descent direction (p rises)" as *u8, 0) } 150 let EP: i64 = 4000 151 var ep: i64 = 0 152 while ep < EP { 153 var z: i64 = 0 154 while z < V*E { gK[z]=0; gV[z]=0; z=z+1 } 155 z = 0 156 while z < E*V { gW[z]=0; z=z+1 } 157 var mb: i64 = 0 158 while mb < 8 { 159 gen_example(ep*8+mb, keyid, valid, qb, gb) 160 let q: i64 = qb[0] 161 let gold: i64 = gb[0] 162 fwd_attn(Kemb, Vemb, Wo, keyid, valid, q, att, ctx, scr, probs, lg, es) 163 var v: i64 = 0 164 while v < V { dl[v] = probs[v]; if v==gold { dl[v]=dl[v]-ONE } v=v+1 } 165 var e: i64 = 0 166 while e < E { 167 var dc: i64 = 0 168 v = 0 169 while v < V { gW[e*V+v] = gW[e*V+v] + fxmul(ctx[e], dl[v]); dc = dc + fxmul(Wo[e*V+v], dl[v]); v = v + 1 } 170 dctx[e] = dc 171 e = e + 1 172 } 173 i = 0 174 while i < N { 175 var da: i64 = 0 176 e = 0 177 while e < E { da = da + fxmul(Vemb[valid[i]*E+e], dctx[e]); gV[valid[i]*E+e] = gV[valid[i]*E+e] + fxmul(att[i], dctx[e]); e = e + 1 } 178 datt[i] = da 179 i = i + 1 180 } 181 var dot: i64 = 0 182 i = 0 183 while i < N { dot = dot + fxmul(att[i], datt[i]); i = i + 1 } 184 i = 0 185 while i < N { dscore[i] = fxmul(att[i], datt[i] - dot); i = i + 1 } 186 i = 0 187 while i < N { 188 e = 0 189 while e < E { 190 gK[keyid[i]*E+e] = gK[keyid[i]*E+e] + fxmul(Kemb[q*E+e], dscore[i]) 191 gK[q*E+e] = gK[q*E+e] + fxmul(Kemb[keyid[i]*E+e], dscore[i]) 192 e = e + 1 193 } 194 i = i + 1 195 } 196 mb = mb + 1 197 } 198 var w: i64 = 0 199 while w < V*E { Kemb[w] = Kemb[w] - fxmul(lr, gK[w]/8); Vemb[w] = Vemb[w] - fxmul(lr, gV[w]/8); w = w + 1 } 200 w = 0 201 while w < E*V { Wo[w] = Wo[w] - fxmul(lr, gW[w]/8); w = w + 1 } 202 ep = ep + 1 203 } 204 205 // ---- T1: attention P(correct) on a held-out batch ---- 206 var accsum: i64 = 0 207 var t: i64 = 0 208 while t < 64 { 209 gen_example(900000 + t, keyid, valid, qb, gb) 210 fwd_attn(Kemb, Vemb, Wo, keyid, valid, qb[0], att, ctx, scr, probs, lg, es) 211 accsum = accsum + probs[gb[0]] 212 t = t + 1 213 } 214 let attP: i64 = accsum / 64 215 g_puts(" ATTENTION P(correct)="); g_pn(attP); g_puts(" (ONE="); g_pn(ONE); g_puts(")\n" as *u8) 216 total = total + 1 217 if attP > (ONE*7)/10 { pass = pass + 1; ck("T1 attention learns the lookup (>0.7)" as *u8, 1) } else { ck("T1 attention learns the lookup (>0.7)" as *u8, 0) } 218 219 // ---- T2: NO-ATTENTION baseline (query-blind mean of the values) ---- 220 var baccsum: i64 = 0 221 t = 0 222 while t < 64 { 223 gen_example(900000 + t, keyid, valid, qb, gb) 224 var e: i64 = 0 225 while e < E { var c: i64 = 0; i = 0; while i < N { c = c + Vemb[valid[i]*E+e]; i = i + 1 } ctx[e] = c/N; e = e + 1 } 226 var v: i64 = 0 227 while v < V { var l: i64 = 0; e = 0; while e < E { l = l + fxmul(ctx[e], Wo[e*V+v]); e = e + 1 } lg[v] = l; v = v + 1 } 228 smax(lg, probs, V, es) 229 baccsum = baccsum + probs[gb[0]] 230 t = t + 1 231 } 232 let baseP: i64 = baccsum / 64 233 g_puts(" NO-ATTENTION baseline P(correct)="); g_pn(baseP); g_puts(" (chance = ONE/V = "); g_pn(ONE/V); g_puts(")\n" as *u8) 234 total = total + 1 235 var t2: i64 = 0 236 if baseP < ONE/2 { if attP > baseP + ONE/8 { t2 = 1 } } 237 if t2 == 1 { pass = pass + 1; ck("T2 baseline fails / attention wins (liar-kill)" as *u8, 1) } else { ck("T2 baseline fails / attention wins (liar-kill)" as *u8, 0) } 238 239 // ---- T4: deterministic ---- 240 var acc2: i64 = 0 241 t = 0 242 while t < 64 { gen_example(900000+t, keyid, valid, qb, gb); fwd_attn(Kemb, Vemb, Wo, keyid, valid, qb[0], att, ctx, scr, probs, lg, es); acc2 = acc2 + probs[gb[0]]; t = t + 1 } 243 total = total + 1 244 if acc2/64 == attP { pass = pass + 1; ck("T4 deterministic" as *u8, 1) } else { ck("T4 deterministic" as *u8, 0) } 245 246 g_puts("---- nx_reader_attn_gate: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts("\n" as *u8) 247 if pass == total { g_puts("ATTENTION-READER RUNG1 verdict=GREEN -- dot-product attention learns context-alignment (query->key->value lookup) on the sovereign integer stack; the capability the isolated-candidate reader lacks.\n" as *u8); return 0 } 248 g_puts("RED -- attention keystone not fully passed (see numbers)\n" as *u8) 249 return 1 250}