code wiki / (root) / nx_reader_span_gate.nx

nx_reader_span_gate.nx source

↩ module page · 291 lines · 14481 B

1// nx_reader_span_gate.nx -- RUNG 2 of the NEURAL PASSAGE READER arc (deep-research R2): the READER-SHAPED 2// MODEL trains END-TO-END on the verified sovereign no-float stack. Composes nx_nofloat_autograd's PROVEN 3// transformer block (the exact blk_fwd shape from the green blockfloat gates: embed -> rmsnorm -> Q/K/V -> 4// RoPE -> QK^T -> causal softmax -> A.V -> Wo -> residual -> rmsnorm -> SwiGLU FFN -> residual) + a SPAN HEAD 5// (matmul_nt of a trained probe against the contextual states -> [1,L] position logits) + fused softmax-CE. 6// 7// TASK (question-conditioned span pointing = the reader mechanism): passage = 3 (key,value) pairs in random 8// order; question = one key token; GOLD SPAN = the position where the queried key occurs in the passage. To 9// point there the model must ALIGN the question to the context (attention); a QUESTION-BLIND ablation (same 10// params/budget, attention removed -> h_i sees only its own token, exactly the isolated-candidate reader that 11// plateaued at 251/148/101) can at best fire on one fixed key = ~333 permille -> liar-kill. 12// T1 reader (attention block) span accuracy > 700 permille held-out 13// T2 question-blind ablation < 450 permille AND reader beats it by > 250 (liar-kill) 14// T3 descent: one nfa_backward+nfa_sgd step on fresh weights DECREASES that example's loss (real gradients) 15// T4 deterministic re-eval (integer stack -> bit-identical) 16// expect_exit: 0 license_tier: ORIGINAL Sovereign: nx_nofloat_autograd + nx_syscalls. 17import "nx_nofloat_autograd.nx" 18import "nx_syscalls.nx" 19 20func rs_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 21func rs_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 } 22func rs_ck(name: *u8, c: i64) -> i64 { if c==1 { rs_puts(" PASS " as *u8) } else { rs_puts(" FAIL " as *u8) } rs_puts(name); rs_puts("\n" as *u8); return c } 23 24const RS_V: i64 = 7 // tokens: keys 0..2, values 3..5, SEP 6 25const RS_E: i64 = 8 // model dim (even, for RoPE) 26const RS_F: i64 = 16 // FFN dim 27const RS_L: i64 = 8 // sequence: [q, SEP, k,v, k,v, k,v] 28const RS_SCALE: i64 = 23170 // 1/sqrt(E) in Q16 29// flat weight layout 30const OFF_EMB: i64 = 0 // [V,E] 56 31const OFF_WQ: i64 = 56 // [E,E] 64 32const OFF_WK: i64 = 120 33const OFF_WV: i64 = 184 34const OFF_WO: i64 = 248 35const OFF_WG: i64 = 312 // [E,F] 128 36const OFF_WU: i64 = 440 // [E,F] 128 37const OFF_WD: i64 = 568 // [F,E] 128 38const OFF_U: i64 = 696 // [1,E] 8 (span probe) 39const RS_NP: i64 = 704 40 41func rs_lcg(x: i64) -> i64 { return ((x*1103515245+12345) & 2147483647) } 42 43// permutations of (0,1,2), flat 6x3 44func rs_perms(pm: *i64) -> i64 { 45 pm[0]=0;pm[1]=1;pm[2]=2; pm[3]=0;pm[4]=2;pm[5]=1; pm[6]=1;pm[7]=0;pm[8]=2 46 pm[9]=1;pm[10]=2;pm[11]=0; pm[12]=2;pm[13]=0;pm[14]=1; pm[15]=2;pm[16]=1;pm[17]=0 47 return 0 48} 49 50// example #s -> ids[L], tgt[0]=gold span position (the passage position of the queried key) 51func rs_gen(s: i64, pm: *i64, ids: *i64, tgt: *i64) -> i64 { 52 var r: i64 = rs_lcg(s + 1) 53 let pk: i64 = r % 6 // key-order permutation 54 r = rs_lcg(r) 55 let pv: i64 = r % 6 // value-pairing permutation 56 r = rs_lcg(r) 57 let qs: i64 = r % 3 // queried pair slot 58 ids[0] = pm[pk*3+qs] // question = the queried KEY token 59 ids[1] = 6 // SEP 60 var j: i64 = 0 61 while j < 3 { 62 ids[2+2*j] = pm[pk*3+j] // key token of pair j 63 ids[3+2*j] = 3 + pm[pv*3+j] // value token of pair j 64 j = j + 1 65 } 66 tgt[0] = 2 + 2*qs // gold = position of the queried key in the passage 67 return 0 68} 69 70// deterministic small init (distinct rows; different prime pattern per matrix to break symmetry) 71func rs_init(W: *i64) -> i64 { 72 var i: i64 = 0 73 while i < 56 { W[OFF_EMB+i] = (((i*7+3)%11)-5)*(NFA_Q16/16); i = i + 1 } 74 i = 0 75 while i < 64 { W[OFF_WQ+i] = (((i*13)%9)-4)*(NFA_Q16/24); i = i + 1 } 76 i = 0 77 while i < 64 { W[OFF_WK+i] = (((i*17)%9)-4)*(NFA_Q16/24); i = i + 1 } 78 i = 0 79 while i < 64 { W[OFF_WV+i] = (((i*19)%9)-4)*(NFA_Q16/24); i = i + 1 } 80 i = 0 81 while i < 64 { W[OFF_WO+i] = (((i*23)%9)-4)*(NFA_Q16/24); i = i + 1 } 82 i = 0 83 while i < 128 { W[OFF_WG+i] = (((i*29)%9)-4)*(NFA_Q16/32); i = i + 1 } 84 i = 0 85 while i < 128 { W[OFF_WU+i] = (((i*31)%9)-4)*(NFA_Q16/32); i = i + 1 } 86 i = 0 87 while i < 128 { W[OFF_WD+i] = (((i*37)%9)-4)*(NFA_Q16/32); i = i + 1 } 88 i = 0 89 while i < 8 { W[OFF_U+i] = (((i*5+1)%7)-3)*(NFA_Q16/8); i = i + 1 } 90 return 0 91} 92 93// forward the READER on the tape. useattn=0 -> QUESTION-BLIND ablation (attention sublayer removed; h_i sees 94// only its own token = the isolated-candidate reader). Fills out[0]=loss node, out[1]=span node, out[2..10]= 95// leaf node ids (Emb,Wq,Wk,Wv,Wo,Wg,Wu,Wd,U) for gradient extraction. 96func rs_fwd(tape: *i64, vals: *i64, st: *i64, W: *i64, ids: *i64, tgt: *i64, useattn: i64, out: *i64) -> i64 { 97 st[0] = 0 98 st[1] = 0 99 let nEmb: i64 = nfa_leaf(tape, vals, st, RS_V, RS_E, W, OFF_EMB) 100 let nWq: i64 = nfa_leaf(tape, vals, st, RS_E, RS_E, W, OFF_WQ) 101 let nWk: i64 = nfa_leaf(tape, vals, st, RS_E, RS_E, W, OFF_WK) 102 let nWv: i64 = nfa_leaf(tape, vals, st, RS_E, RS_E, W, OFF_WV) 103 let nWo: i64 = nfa_leaf(tape, vals, st, RS_E, RS_E, W, OFF_WO) 104 let nWg: i64 = nfa_leaf(tape, vals, st, RS_E, RS_F, W, OFF_WG) 105 let nWu: i64 = nfa_leaf(tape, vals, st, RS_E, RS_F, W, OFF_WU) 106 let nWd: i64 = nfa_leaf(tape, vals, st, RS_F, RS_E, W, OFF_WD) 107 let nU: i64 = nfa_leaf(tape, vals, st, 1, RS_E, W, OFF_U) 108 let nX: i64 = nfa_embed(tape, vals, st, nEmb, ids, RS_L) 109 var nH: i64 = nX 110 if useattn == 1 { 111 let nXn: i64 = nfa_rmsnorm_rows(tape, vals, st, nX) 112 let nQ: i64 = nfa_matmul(tape, vals, st, nXn, nWq) 113 let nK: i64 = nfa_matmul(tape, vals, st, nXn, nWk) 114 let nV: i64 = nfa_matmul(tape, vals, st, nXn, nWv) 115 let nQr: i64 = nfa_rope(tape, vals, st, nQ) 116 let nKr: i64 = nfa_rope(tape, vals, st, nK) 117 let nS: i64 = nfa_matmul_nt(tape, vals, st, nQr, nKr) 118 let nSs: i64 = nfa_cmul(tape, vals, st, nS, RS_SCALE) 119 let nA: i64 = nfa_softmax_rows(tape, vals, st, nSs, 1) 120 let nO: i64 = nfa_matmul(tape, vals, st, nA, nV) 121 let nOp: i64 = nfa_matmul(tape, vals, st, nO, nWo) 122 nH = nfa_vadd(tape, vals, st, nX, nOp) 123 } 124 let nHn: i64 = nfa_rmsnorm_rows(tape, vals, st, nH) 125 let nG: i64 = nfa_matmul(tape, vals, st, nHn, nWg) 126 let nU2: i64 = nfa_matmul(tape, vals, st, nHn, nWu) 127 let nSg: i64 = nfa_silu(tape, vals, st, nG) 128 let nHs: i64 = nfa_hadamard(tape, vals, st, nSg, nU2) 129 let nD: i64 = nfa_matmul(tape, vals, st, nHs, nWd) 130 let nY: i64 = nfa_vadd(tape, vals, st, nH, nD) 131 let nYn: i64 = nfa_rmsnorm_rows(tape, vals, st, nY) 132 let nSpan: i64 = nfa_matmul_nt(tape, vals, st, nU, nYn) // [1,E].[L,E]^T -> [1,L] position logits 133 let nLoss: i64 = nfa_softce_rows(tape, vals, st, nSpan, tgt) 134 out[0] = nLoss 135 out[1] = nSpan 136 out[2] = nEmb 137 out[3] = nWq 138 out[4] = nWk 139 out[5] = nWv 140 out[6] = nWo 141 out[7] = nWg 142 out[8] = nWu 143 out[9] = nWd 144 out[10] = nU 145 return nLoss 146} 147 148// accumulate the 9 leaves' grads (tape arena) into the flat G accumulator (weight layout offsets). 149// NO mmap here -- runs inside the training loop (flat ifs per the nx_cc no-else-chain gotcha). 150func rs_grab(tape: *i64, grads: *i64, out: *i64, G: *i64) -> i64 { 151 var k: i64 = 0 152 while k < 9 { 153 var sz: i64 = 64 154 var of: i64 = 0 155 if k == 0 { sz = 56; of = OFF_EMB } 156 if k == 1 { of = OFF_WQ } 157 if k == 2 { of = OFF_WK } 158 if k == 3 { of = OFF_WV } 159 if k == 4 { of = OFF_WO } 160 if k == 5 { sz = 128; of = OFF_WG } 161 if k == 6 { sz = 128; of = OFF_WU } 162 if k == 7 { sz = 128; of = OFF_WD } 163 if k == 8 { sz = 8; of = OFF_U } 164 let nid: i64 = out[2+k] 165 let goff: i64 = tape[7*nid+6] 166 var i: i64 = 0 167 while i < sz { G[of+i] = G[of+i] + grads[goff+i]; i = i + 1 } 168 k = k + 1 169 } 170 return 0 171} 172 173// train a reader (attention or ablated) for EP epochs of B examples; returns nothing (W updated in place) 174func rs_train(tape: *i64, vals: *i64, grads: *i64, st: *i64, W: *i64, pm: *i64, useattn: i64, EP: i64, lr: i64, verbose: i64) -> i64 { 175 let ids: *i64 = sys_mmap(RS_L*8) as *i64 176 let tgt: *i64 = sys_mmap(8) as *i64 177 let out: *i64 = sys_mmap(12*8) as *i64 178 let G: *i64 = sys_mmap(RS_NP*8) as *i64 179 let B: i64 = 8 180 var ep: i64 = 0 181 while ep < EP { 182 var z: i64 = 0 183 while z < RS_NP { G[z] = 0; z = z + 1 } 184 var lsum: i64 = 0 185 var mb: i64 = 0 186 while mb < B { 187 rs_gen(ep*B+mb, pm, ids, tgt) 188 let nLoss: i64 = rs_fwd(tape, vals, st, W, ids, tgt, useattn, out) 189 lsum = lsum + nfa_val(tape, vals, nLoss, 0) 190 nfa_backward(tape, vals, grads, st[0], nLoss) 191 rs_grab(tape, grads, out, G) 192 mb = mb + 1 193 } 194 var w: i64 = 0 195 while w < RS_NP { G[w] = G[w] / B; w = w + 1 } 196 nfa_sgd(W, G, RS_NP, lr) 197 if verbose == 1 { if ep % 500 == 0 { rs_puts(" epoch "); rs_pn(ep); rs_puts(" loss_milli="); rs_pn((lsum/B)*1000/NFA_Q16); rs_puts("\n" as *u8) } } 198 ep = ep + 1 199 } 200 return 0 201} 202 203// held-out span accuracy (permille over 64 examples) of a trained reader 204func rs_eval(tape: *i64, vals: *i64, st: *i64, W: *i64, pm: *i64, useattn: i64) -> i64 { 205 let ids: *i64 = sys_mmap(RS_L*8) as *i64 206 let tgt: *i64 = sys_mmap(8) as *i64 207 let out: *i64 = sys_mmap(12*8) as *i64 208 var hits: i64 = 0 209 var t: i64 = 0 210 while t < 64 { 211 rs_gen(900000 + t, pm, ids, tgt) 212 rs_fwd(tape, vals, st, W, ids, tgt, useattn, out) 213 let nSpan: i64 = out[1] 214 var best: i64 = 0 215 var bv: i64 = nfa_val(tape, vals, nSpan, 0) 216 var j: i64 = 1 217 while j < RS_L { let v: i64 = nfa_val(tape, vals, nSpan, j); if v > bv { bv = v; best = j } j = j + 1 } 218 if best == tgt[0] { hits = hits + 1 } 219 t = t + 1 220 } 221 return hits * 1000 / 64 222} 223 224func main() -> i64 { 225 rs_puts("nx_reader_span_gate (RUNG 2: the READER MODEL -- transformer block + span head -- trains end-to-end on the verified nfa stack)\n" as *u8) 226 var pass: i64 = 0 227 var total: i64 = 0 228 let tape: *i64 = sys_mmap(4096*7*8) as *i64 229 let vals: *i64 = sys_mmap(131072*8) as *i64 230 let grads: *i64 = sys_mmap(131072*8) as *i64 231 let st: *i64 = sys_mmap(2*8) as *i64 232 let pm: *i64 = sys_mmap(18*8) as *i64 233 rs_perms(pm) 234 let EP: i64 = 2500 235 let lr: i64 = 4096 236 237 // ---- T3 FIRST (on fresh weights; untrained -> non-trivial): one backward+sgd step must cut the loss ---- 238 let W3: *i64 = sys_mmap(RS_NP*8) as *i64 239 rs_init(W3) 240 let ids3: *i64 = sys_mmap(RS_L*8) as *i64 241 let tgt3: *i64 = sys_mmap(8) as *i64 242 let out3: *i64 = sys_mmap(12*8) as *i64 243 let G3: *i64 = sys_mmap(RS_NP*8) as *i64 244 var z3: i64 = 0 245 while z3 < RS_NP { G3[z3] = 0; z3 = z3 + 1 } 246 rs_gen(777, pm, ids3, tgt3) 247 let nL0: i64 = rs_fwd(tape, vals, st, W3, ids3, tgt3, 1, out3) 248 let loss0: i64 = nfa_val(tape, vals, nL0, 0) 249 nfa_backward(tape, vals, grads, st[0], nL0) 250 rs_grab(tape, grads, out3, G3) 251 nfa_sgd(W3, G3, RS_NP, lr) 252 let nL1: i64 = rs_fwd(tape, vals, st, W3, ids3, tgt3, 1, out3) 253 let loss1: i64 = nfa_val(tape, vals, nL1, 0) 254 rs_puts(" DESCENT loss_milli "); rs_pn(loss0*1000/NFA_Q16); rs_puts(" -> "); rs_pn(loss1*1000/NFA_Q16); rs_puts(" (one nfa_backward+nfa_sgd step; must fall)\n" as *u8) 255 total = total + 1 256 var t3ok: i64 = 0 257 if loss1 < loss0 { if loss0 > NFA_Q16/16 { t3ok = 1 } } 258 if t3ok == 1 { pass = pass + 1; rs_ck("T3 real gradients: one step cuts the loss" as *u8, 1) } else { rs_ck("T3 real gradients: one step cuts the loss" as *u8, 0) } 259 260 // ---- train the READER (attention block) ---- 261 rs_puts(" training READER (attention block + span head, batch-GD)...\n" as *u8) 262 let W: *i64 = sys_mmap(RS_NP*8) as *i64 263 rs_init(W) 264 rs_train(tape, vals, grads, st, W, pm, 1, EP, lr, 1) 265 let accA: i64 = rs_eval(tape, vals, st, W, pm, 1) 266 rs_puts(" READER (attention) held-out span accuracy = "); rs_pn(accA); rs_puts(" permille\n" as *u8) 267 total = total + 1 268 if accA > 700 { pass = pass + 1; rs_ck("T1 reader points at the answer span (>700)" as *u8, 1) } else { rs_ck("T1 reader points at the answer span (>700)" as *u8, 0) } 269 270 // ---- train the QUESTION-BLIND ablation (identical budget/init; attention removed) ---- 271 rs_puts(" training ABLATION (question-blind: attention removed, same params/budget)...\n" as *u8) 272 let W2: *i64 = sys_mmap(RS_NP*8) as *i64 273 rs_init(W2) 274 rs_train(tape, vals, grads, st, W2, pm, 0, EP, lr, 0) 275 let accB: i64 = rs_eval(tape, vals, st, W2, pm, 0) 276 rs_puts(" ABLATION (question-blind) held-out span accuracy = "); rs_pn(accB); rs_puts(" permille (informed ceiling ~333)\n" as *u8) 277 total = total + 1 278 var t2ok: i64 = 0 279 if accB < 450 { if accA > accB + 250 { t2ok = 1 } } 280 if t2ok == 1 { pass = pass + 1; rs_ck("T2 question-blind ablation fails / reader wins (liar-kill)" as *u8, 1) } else { rs_ck("T2 question-blind ablation fails / reader wins (liar-kill)" as *u8, 0) } 281 282 // ---- T4 deterministic re-eval ---- 283 let accA2: i64 = rs_eval(tape, vals, st, W, pm, 1) 284 total = total + 1 285 if accA2 == accA { pass = pass + 1; rs_ck("T4 deterministic re-eval (bit-identical)" as *u8, 1) } else { rs_ck("T4 deterministic re-eval (bit-identical)" as *u8, 0) } 286 287 rs_puts("---- nx_reader_span_gate: passed "); rs_pn(pass); rs_puts(" / "); rs_pn(total); rs_puts("\n" as *u8) 288 if pass == total { rs_puts("READER RUNG2 GREEN -- the reader-shaped model (transformer block + span head + softmax-CE) trains END-TO-END on the sovereign no-float autograd and does QUESTION-CONDITIONED span pointing; the question-blind (isolated-candidate) reader provably cannot.\n" as *u8); return 0 } 289 rs_puts("RED -- reader rung 2 not fully passed (see numbers)\n" as *u8) 290 return 1 291}