code wiki / (root) / nx_reader_mlp_gate.nx

nx_reader_mlp_gate.nx source

↩ module page · 230 lines · 10776 B

1// nx_reader_mlp_gate.nx -- NEURAL-READER RUNG 1: prove the sovereign autograd (nx_autograd) can TRAIN a 2// nonlinear model, on the exact capability the deep-research reader's linear plateau lacks. The oracle 3// diagnostic proved SCORING is the deep-research wall and that a LINEAR perceptron over hand-features plateaus 4// (~250 vs oracle 727; two features net-negative/inert). The fix is LEARNED NONLINEAR representations. This 5// gate is the minimal liar-killed proof of that mechanism: a 2-layer MLP (2->4->1, ReLU) trained by SGD on the 6// autograd tape LEARNS XOR (100%), which a LINEAR model provably CANNOT (<=75%). Same loop scales to the reader 7// (features->hidden->score) and to any trained model; train in f32, quantize to integer for the no-float 8// inference path. TEETH: T1 MLP fits XOR 4/4 T2 MLP loss collapses T3 LINEAR baseline FAILS (<4/4, the 9// liar-kill: if a linear model 'passed' XOR the test would be meaningless) T4 gradcheck (autograd d/dx x^2=2x). 10// expect_exit: 0 license_tier: ORIGINAL 11import "nx_autograd.nx" 12 13func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14func gn(v: i64) -> i64 { 15 if v==0 { sys_write(1,"0" as *u8,1); return 0 } 16 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } 17 let t: *u8=sys_mmap(24); var k: i64=0 18 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 19 let b: *u8=sys_mmap(24); var i: i64=0; while i<k { b[i]=t[k-1-i]; i=i+1 } sys_write(1,b,k); return 0 20} 21// f32 (IEEE-754 binary32 bits) -> integer, truncated toward zero. small magnitudes (no overflow guard needed 22// for our loss/grad displays). No nx_f32_to_i32 in the cvt module, so decode exp/mantissa directly. 23func nx_f32_trunc(raw: i64) -> i64 { 24 if raw == 0 { return 0 } 25 let sign: i64 = (raw / 2147483648) % 2 // bit31 (avoid >31 immediate shift) 26 let exp: i64 = (raw / 8388608) % 256 // bits 23..30 27 let mant: i64 = raw % 8388608 // bits 0..22 28 if exp == 0 { return 0 } // subnormal ~ 0 29 let m: i64 = mant + 8388608 // implicit leading 1 -> 1.mant * 2^23 30 let e: i64 = exp - 127 - 23 // value = m * 2^e 31 var val: i64 = 0 32 if e >= 0 { val = m << e } else { let sh: i64 = 0 - e; val = m >> sh } 33 if sign == 1 { val = 0 - val } 34 return val 35} 36// f32 -> integer milli (x1000), truncated. for display. 37func f32_milli(v: i64) -> i64 { return nx_f32_trunc(nx_f32_mul(v, nx_i32_to_f32(1000))) } 38 39const NIN: i64 = 2 40const NHID: i64 = 8 41const B1_OFF: i64 = 16 // NHID*NIN 42const W2_OFF: i64 = 24 // B1_OFF + NHID 43const B2_OFF: i64 = 32 // W2_OFF + NHID 44const NP_MLP: i64 = 33 // W1[8x2]=16 + b1[8]=8 + W2[8]=8 + b2=1 45const NP_LIN: i64 = 3 // w0,w1,b 46 47// deterministic symmetry-breaking init in [-0.6,0.6] 48func init_weights(W: *i64, n: i64) -> i64 { 49 var k: i64 = 0 50 while k < n { W[k] = ag_constf(((k*37) % 13) - 6, 10); k = k + 1 } 51 return 0 52} 53 54// MLP init: W spread; b1 (hidden biases) a MIXED-SIGN spread [-1 .. +0.75] so hidden units have varied ReLU 55// thresholds -- the ReLU XOR solution needs a unit with a NEGATIVE bias (relu(x0+x1-1)); positive-only biases 56// stalled it. b2 = 0. 57func init_mlp(W: *i64) -> i64 { 58 var k: i64 = 0 59 while k < NP_MLP { W[k] = ag_constf(((k*37) % 13) - 6, 10); k = k + 1 } 60 var j: i64 = 0 61 while j < NHID { W[B1_OFF + j] = ag_constf(j - 4, 4); j = j + 1 } // b1 in [-1.0 .. +0.75] 62 W[B2_OFF] = AG_F32_ZERO // b2 = 0 63 return 0 64} 65 66// build the MLP forward on a FRESH tape (weights = leafs 0..16, x=17,18, target=19); return the loss node. 67// out node index is returned via ob[0]. Layout: W1[j*2+i], b1[8+j], W2[12+j], b2=16. 68func mlp_step(tape: *i64, np: *i64, W: *i64, x0: i64, x1: i64, tgt: i64, ob: *i64) -> i64 { 69 *np = 0 70 var k: i64 = 0 71 while k < NP_MLP { ag_leaf(tape, np, W[k]); k = k + 1 } 72 let xa: i64 = ag_leaf(tape, np, x0) // 17 73 let xb: i64 = ag_leaf(tape, np, x1) // 18 74 let tg: i64 = ag_leaf(tape, np, tgt) // NP_MLP+2 75 var out: i64 = B2_OFF // start accumulator at b2 leaf 76 var j: i64 = 0 77 while j < NHID { 78 let m0: i64 = ag_mul(tape, np, j*2+0, xa) 79 let m1: i64 = ag_mul(tape, np, j*2+1, xb) 80 let s: i64 = ag_add(tape, np, m0, m1) 81 let s2: i64 = ag_add(tape, np, s, B1_OFF+j) 82 let hj: i64 = ag_relu(tape, np, s2) 83 let om: i64 = ag_mul(tape, np, W2_OFF+j, hj) 84 out = ag_add(tape, np, out, om) 85 j = j + 1 86 } 87 ob[0] = out 88 let diff: i64 = ag_sub(tape, np, out, tg) 89 let loss: i64 = ag_mul(tape, np, diff, diff) 90 return loss 91} 92 93// linear forward (weights leafs 0,1,2; x=3,4; target=5); loss node; out via ob[0]. 94func lin_step(tape: *i64, np: *i64, W: *i64, x0: i64, x1: i64, tgt: i64, ob: *i64) -> i64 { 95 *np = 0 96 ag_leaf(tape, np, W[0]); ag_leaf(tape, np, W[1]); ag_leaf(tape, np, W[2]) 97 let xa: i64 = ag_leaf(tape, np, x0) // 3 98 let xb: i64 = ag_leaf(tape, np, x1) // 4 99 let tg: i64 = ag_leaf(tape, np, tgt) // 5 100 let m0: i64 = ag_mul(tape, np, 0, xa) 101 let m1: i64 = ag_mul(tape, np, 1, xb) 102 let s: i64 = ag_add(tape, np, m0, m1) 103 let out: i64 = ag_add(tape, np, s, 2) 104 ob[0] = out 105 let diff: i64 = ag_sub(tape, np, out, tg) 106 let loss: i64 = ag_mul(tape, np, diff, diff) 107 return loss 108} 109 110func main() -> i64 { 111 gw("=== nx_reader_mlp_gate -- neural-reader rung 1: autograd MLP learns XOR, linear cannot ===\n" as *u8) 112 let tape: *i64 = sys_mmap(2048 * 5 * 8) as *i64 113 let np: *i64 = sys_mmap(8) as *i64 114 let ob: *i64 = sys_mmap(8) as *i64 115 116 // XOR dataset: 4 points. inputs 0.0/1.0, targets 0.0/1.0. 117 let F0: i64 = AG_F32_ZERO 118 let F1: i64 = AG_F32_ONE 119 let X0: *i64 = sys_mmap(64) as *i64 120 let X1: *i64 = sys_mmap(64) as *i64 121 let TY: *i64 = sys_mmap(64) as *i64 122 X0[0]=F0; X1[0]=F0; TY[0]=F0 123 X0[1]=F0; X1[1]=F1; TY[1]=F1 124 X0[2]=F1; X1[2]=F0; TY[2]=F1 125 X0[3]=F1; X1[3]=F1; TY[3]=F0 126 127 let lr: i64 = ag_constf(1, 10) // 0.1 (on SUMMED batch gradient) 128 let half: i64 = ag_constf(1, 2) // 0.5 decision threshold 129 let EPOCHS: i64 = 20000 130 131 // ---------- train MLP (BATCH gradient descent: accumulate grads over all 4 points, one update/epoch -- 132 // far more stable than per-example SGD on a 4-point set) ---------- 133 let Wm: *i64 = sys_mmap(NP_MLP * 8) as *i64 134 let Gacc: *i64 = sys_mmap(NP_MLP * 8) as *i64 135 init_mlp(Wm) 136 var ep: i64 = 0 137 var lastloss_m: i64 = 0 138 while ep < EPOCHS { 139 var k0: i64 = 0 140 while k0 < NP_MLP { Gacc[k0] = AG_F32_ZERO; k0 = k0 + 1 } 141 var d: i64 = 0 142 var lsum: i64 = AG_F32_ZERO 143 while d < 4 { 144 let loss: i64 = mlp_step(tape, np, Wm, X0[d], X1[d], TY[d], ob) 145 ag_backward(tape, *np, loss) 146 lsum = nx_f32_add(lsum, ag_val(tape, loss)) 147 var k: i64 = 0 148 while k < NP_MLP { Gacc[k] = nx_f32_add(Gacc[k], ag_grad(tape, k)); k = k + 1 } 149 d = d + 1 150 } 151 var ku: i64 = 0 152 while ku < NP_MLP { Wm[ku] = nx_f32_sub(Wm[ku], nx_f32_mul(lr, Gacc[ku])); ku = ku + 1 } 153 lastloss_m = lsum 154 if ep % 5000 == 0 { gw(" epoch "); gn(ep); gw(" loss_milli="); gn(f32_milli(lsum)); gw("\n" as *u8) } 155 ep = ep + 1 156 } 157 // eval MLP 158 var mlp_correct: i64 = 0 159 var d2: i64 = 0 160 gw("MLP predictions: " as *u8) 161 while d2 < 4 { 162 mlp_step(tape, np, Wm, X0[d2], X1[d2], TY[d2], ob) 163 let ov: i64 = ag_val(tape, ob[0]) 164 var pred: i64 = 0 165 if nx_f32_gt(ov, half) == 1 { pred = 1 } 166 var tgt: i64 = 0 167 if nx_f32_gt(TY[d2], half) == 1 { tgt = 1 } 168 gw("(" as *u8); gn(f32_milli(X0[d2])/1000); gw("," as *u8); gn(f32_milli(X1[d2])/1000) 169 gw(")->" as *u8); gn(pred); gw("[t=" as *u8); gn(tgt); gw("] " as *u8) 170 if pred == tgt { mlp_correct = mlp_correct + 1 } 171 d2 = d2 + 1 172 } 173 gw(" = "); gn(mlp_correct); gw("/4 loss_milli="); gn(f32_milli(lastloss_m)); gw("\n" as *u8) 174 175 // ---------- train LINEAR baseline (the liar-kill: it MUST fail XOR) ---------- 176 let Wl: *i64 = sys_mmap(NP_LIN * 8) as *i64 177 init_weights(Wl, NP_LIN) 178 ep = 0 179 while ep < EPOCHS { 180 var d: i64 = 0 181 while d < 4 { 182 let loss: i64 = lin_step(tape, np, Wl, X0[d], X1[d], TY[d], ob) 183 ag_backward(tape, *np, loss) 184 var k: i64 = 0 185 while k < NP_LIN { Wl[k] = nx_f32_sub(Wl[k], nx_f32_mul(lr, ag_grad(tape, k))); k = k + 1 } 186 d = d + 1 187 } 188 ep = ep + 1 189 } 190 var lin_correct: i64 = 0 191 var d3: i64 = 0 192 gw("LIN predictions: " as *u8) 193 while d3 < 4 { 194 lin_step(tape, np, Wl, X0[d3], X1[d3], TY[d3], ob) 195 let ov: i64 = ag_val(tape, ob[0]) 196 var pred: i64 = 0 197 if nx_f32_gt(ov, half) == 1 { pred = 1 } 198 var tgt: i64 = 0 199 if nx_f32_gt(TY[d3], half) == 1 { tgt = 1 } 200 gw("(" as *u8); gn(f32_milli(X0[d3])/1000); gw("," as *u8); gn(f32_milli(X1[d3])/1000) 201 gw(")->" as *u8); gn(pred); gw("[t=" as *u8); gn(tgt); gw("] " as *u8) 202 if pred == tgt { lin_correct = lin_correct + 1 } 203 d3 = d3 + 1 204 } 205 gw(" = "); gn(lin_correct); gw("/4 (linear CANNOT separate XOR)\n" as *u8) 206 207 // ---------- gradcheck: d/dx (x*x) at x=3 == 6 ---------- 208 *np = 0 209 let gx: i64 = ag_leaf(tape, np, nx_i32_to_f32(3)) 210 let gy: i64 = ag_mul(tape, np, gx, gx) 211 ag_backward(tape, *np, gy) 212 let gxg: i64 = f32_milli(ag_grad(tape, gx)) // expect 6000 213 gw("gradcheck d/dx(x^2)@3 = "); gn(gxg); gw(" milli (expect 6000)\n" as *u8) 214 215 // ---------- teeth ---------- 216 var pass: i64 = 0 217 if mlp_correct == 4 { pass = pass + 1 } // T1 MLP fits XOR 218 if f32_milli(lastloss_m) < 100 { pass = pass + 1 } // T2 loss collapsed (<0.1) 219 if lin_correct < 4 { pass = pass + 1 } // T3 LIAR-KILL: linear FAILS XOR 220 if gxg > 5900 { if gxg < 6100 { pass = pass + 1 } } // T4 gradcheck 221 gw("TEETH T1(mlp4/4)+T2(loss<0.1)+T3(lin-fails)+T4(gradcheck) = "); gn(pass); gw("/4\n" as *u8) 222 if pass == 4 { 223 gw("GREEN -- the sovereign autograd TRAINS a nonlinear model that beats the linear ceiling. This is the\n" as *u8) 224 gw("mechanism the deep-research reader needs (scoring plateau = linear); next = MLP over the 14 reader\n" as *u8) 225 gw("features -> quantize to integer -> wire into MODE-R; target: beat 251/148/101 toward oracle 727.\n" as *u8) 226 return 0 227 } 228 gw("RED -- neural-reader mechanism not proven\n" as *u8) 229 return 1 230}