code wiki / _hdl_build / nx_mt_r2_gate.nx

nx_mt_r2_gate.nx source

↩ module page · 290 lines · 12736 B

1// nx_mt_r2_gate.nx -- GATE for MT-R2: CROSS-TOKEN CONTEXT enables REORDERING. The first genuinely 2// "machine translation" phenomenon: an output token that depends on MORE than its own aligned 3// input token. Task = REVERSE+map: source [a,b] -> target [tmap[b], tmap[a]] (the canonical MT 4// reordering, e.g. EN adjective-noun "red house" -> ES noun-adjective "casa roja"). A per-token 5// aligned map (MT-R1 / nx_mt_core) provably CANNOT do this; a cross-token model can. This rung 6// proves the CAPABILITY + the measured necessity contrast; the parameter-free fnet_mix and 7// attention are the SCALABLE realizations of this same cross-token mixing, wired at R3+ when we 8// move to real arrays/vocab. 9// 10// MODEL (cross-token, tape-trainable -- linear, so pure add/mul through nx_autograd): two learned 11// heads W[h] (h=0,1), each [T x 2S], over the CONCATENATED one-hot input [onehot(a) ; onehot(b)]. 12// head h, output j logit = W[h,j,a] + W[h,j,S+b] (the two active concat dims) ; argmax_j. 13// head 0 target = onehot(tmap[b]) (reordered!), head 1 target = onehot(tmap[a]). Full-batch GD, 14// MSE, zero-init. The loss is convex; lr chosen for the bipartite Hessian (degree ~ pairs/token). 15// 16// Vocab S=T=4 (small -> clean convex convergence + fast): EN 0 hello 1 family 2 love 3 good ; 17// ES 0 familia 1 bueno 2 hola 3 amor ; tmap=[2,0,3,1]. TRAIN = 13 of the 16 (a,b) pairs; 18// HELD-OUT = 3 pairs {(0,3),(3,0),(1,2)} (combinations never trained). 19// 20// FOUR GATES: 21// A LEARNS reordering: per-token acc == 2*train_pairs, loss decreased to < half. 22// B GENERALIZES: per-token acc == 2*held_pairs on pairs NEVER trained. 23// C BIT-EXACT: retrain -> identical W bits. 24// D PER-TOKEN MODEL CANNOT (measured necessity contrast + liar-kill): train MT-R1's per-token 25// shared map (nx_mt_core) on the SAME reverse task -> its acc < 2*train_pairs. Proves the 26// cross-token capacity is NECESSARY, not decorative. 27// 28// genealogy_id: rumelhart_1986_backprop (realized_in nx_autograd) + lee_2021_fnet (the mixing motive) 29// lineage_id: sovereign_neural_mt_r2_crosstoken_reorder_v1 30// license_tier: ORIGINAL 31import "nx_mt_core.nx" // mt_train / mt_seq_acc -- the per-token model, for the Gate-D contrast 32import "nx_autograd.nx" // ag_* + nx_f32 compares + AG_F32_* + nx_syscalls 33import "nx_syscalls.nx" 34import "nx_gate_verdict.nx" 35 36const M2_LOG: *u8 = "knowledge/status/mt_r2.log" 37const M2_S: i64 = 4 38const M2_T: i64 = 4 39 40const EN0: *u8 = "hello" as *u8 41const EN1: *u8 = "family" as *u8 42const EN2: *u8 = "love" as *u8 43const EN3: *u8 = "good" as *u8 44const ES0: *u8 = "familia" as *u8 45const ES1: *u8 = "bueno" as *u8 46const ES2: *u8 = "hola" as *u8 47const ES3: *u8 = "amor" as *u8 48const ESQ: *u8 = "?" as *u8 49 50func m2_en(i: i64) -> *u8 { 51 if i == 0 { return EN0 } 52 if i == 1 { return EN1 } 53 if i == 2 { return EN2 } 54 if i == 3 { return EN3 } 55 return ESQ 56} 57func m2_es(j: i64) -> *u8 { 58 if j == 0 { return ES0 } 59 if j == 1 { return ES1 } 60 if j == 2 { return ES2 } 61 if j == 3 { return ES3 } 62 return ESQ 63} 64 65func m2_w(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 } 66func m2_wn(fd: i64, v: i64) -> i64 { 67 let bb: *u8 = sys_mmap(28); var m: i64 = v 68 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 69 let t: *u8 = sys_mmap(28); var k: i64 = 0 70 if m == 0 { t[0] = 48; k = 1 } 71 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 72 var i: i64 = 0 73 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 74 sys_write(fd, bb, k); return 0 75} 76func m2_f32_to_milli(v: i64) -> i64 { 77 var s: i64 = nx_f32_mul(v, nx_i32_to_f32(1000)) 78 var neg: i64 = 0 79 if nx_f32_lt(s, AG_F32_ZERO) == 1 { neg = 1; s = nx_f32_neg(s) } 80 let half: i64 = ag_constf(1, 2) 81 var m: i64 = 0 82 var go: i64 = 1 83 while go == 1 { 84 let mid: i64 = nx_f32_add(nx_i32_to_f32(m), half) 85 if nx_f32_lt(mid, s) == 1 { m = m + 1; if m >= 100000 { go = 0 } } else { go = 0 } 86 } 87 if neg == 1 { return 0 - m } 88 return m 89} 90 91// flat index of W[h, j, d] in the (2 x T x 2S) head tensor 92func m2_idx(h: i64, j: i64, d: i64) -> i64 { return h * M2_T * (2 * M2_S) + j * (2 * M2_S) + d } 93 94// argmax over outputs for head h on input pair (a,b): logit_j = W[h,j,a] + W[h,j,S+b]. 95func m2_pred(W: *i64, h: i64, a: i64, b: i64) -> i64 { 96 var bestj: i64 = 0 97 var best: i64 = nx_f32_add(W[m2_idx(h, 0, a)], W[m2_idx(h, 0, M2_S + b)]) 98 var j: i64 = 1 99 while j < M2_T { 100 let o: i64 = nx_f32_add(W[m2_idx(h, j, a)], W[m2_idx(h, j, M2_S + b)]) 101 if nx_f32_gt(o, best) == 1 { best = o; bestj = j } 102 j = j + 1 103 } 104 return bestj 105} 106 107// per-token accuracy over a pair set: head0 should yield tmap[b], head1 tmap[a]. 2 tokens/pair. 108func m2_acc(W: *i64, pa: *i64, pb: *i64, npair: i64, tmap: *i64) -> i64 { 109 var c: i64 = 0 110 var i: i64 = 0 111 while i < npair { 112 if m2_pred(W, 0, pa[i], pb[i]) == tmap[pb[i]] { c = c + 1 } 113 if m2_pred(W, 1, pa[i], pb[i]) == tmap[pa[i]] { c = c + 1 } 114 i = i + 1 115 } 116 return c 117} 118 119// full-batch GD of the two cross-token heads on the reverse+map task. zero-init; caller lr. 120func m2_train(W: *i64, pa: *i64, pb: *i64, npair: i64, tmap: *i64, epochs: i64, lr: i64, lfirst: *i64, llast: *i64) -> i64 { 121 let NW: i64 = 2 * M2_T * (2 * M2_S) 122 var z: i64 = 0 123 while z < NW { W[z] = AG_F32_ZERO; z = z + 1 } 124 let wn: *i64 = (sys_mmap(NW * 8)) as *i64 125 let tape: *i64 = (sys_mmap(16384 * 5 * 8)) as *i64 126 let np: *i64 = (sys_mmap(8)) as *i64 127 var ep: i64 = 0 128 while ep < epochs { 129 *np = 0 130 var p: i64 = 0 131 while p < NW { wn[p] = ag_leaf(tape, np, W[p]); p = p + 1 } 132 let one: i64 = ag_leaf(tape, np, AG_F32_ONE) 133 let zero: i64 = ag_leaf(tape, np, AG_F32_ZERO) 134 var nsum: i64 = ag_leaf(tape, np, AG_F32_ZERO) 135 var i: i64 = 0 136 while i < npair { 137 let a: i64 = pa[i] 138 let b: i64 = pb[i] 139 var h: i64 = 0 140 while h < 2 { 141 var tgt: i64 = tmap[a] 142 if h == 0 { tgt = tmap[b] } // head 0 = reordered (depends on b) 143 var j: i64 = 0 144 while j < M2_T { 145 let logit: i64 = ag_add(tape, np, wn[m2_idx(h, j, a)], wn[m2_idx(h, j, M2_S + b)]) 146 var tn: i64 = zero 147 if tgt == j { tn = one } 148 let res: i64 = ag_sub(tape, np, logit, tn) 149 let sq: i64 = ag_mul(tape, np, res, res) 150 nsum = ag_add(tape, np, nsum, sq) 151 j = j + 1 152 } 153 h = h + 1 154 } 155 i = i + 1 156 } 157 ag_backward(tape, *np, nsum) 158 if ep == 0 { *lfirst = ag_val(tape, nsum) } 159 *llast = ag_val(tape, nsum) 160 p = 0 161 while p < NW { 162 W[p] = nx_f32_sub(W[p], nx_f32_mul(lr, ag_grad(tape, wn[p]))) 163 p = p + 1 164 } 165 ep = ep + 1 166 } 167 return 0 168} 169 170// demo: a held-out pair, showing INPUT order vs REORDERED output (the MT phenomenon). 171func m2_demo(fd: i64, W: *i64, a: i64, b: i64) -> i64 { 172 m2_w(fd, "MT-R2 reorder [" as *u8); m2_w(fd, m2_en(a)); m2_w(fd, " " as *u8); m2_w(fd, m2_en(b)) 173 m2_w(fd, "] -> [" as *u8); m2_w(fd, m2_es(m2_pred(W, 0, a, b))); m2_w(fd, " " as *u8); m2_w(fd, m2_es(m2_pred(W, 1, a, b))) 174 m2_w(fd, "]\n" as *u8) 175 return 0 176} 177 178func m2_emit(fd: i64, r: *i64) -> i64 { 179 m2_w(fd, "MTR2GATE authored=organ engine=scalar-tape-autograd-f32 task=crosstoken-reorder" as *u8) 180 m2_w(fd, " | A_learns_pass=" as *u8); m2_wn(fd, r[0]) 181 m2_w(fd, " train_acc=" as *u8); m2_wn(fd, r[1]); m2_w(fd, "/" as *u8); m2_wn(fd, r[2]) 182 m2_w(fd, " loss_first_milli=" as *u8); m2_wn(fd, r[3]); m2_w(fd, " loss_last_milli=" as *u8); m2_wn(fd, r[4]) 183 m2_w(fd, " | B_generalizes_pass=" as *u8); m2_wn(fd, r[5]); m2_w(fd, " heldout_acc=" as *u8); m2_wn(fd, r[6]); m2_w(fd, "/" as *u8); m2_wn(fd, r[7]) 184 m2_w(fd, " | C_bitexact_pass=" as *u8); m2_wn(fd, r[8]) 185 m2_w(fd, " | D_pertoken_cannot_pass=" as *u8); m2_wn(fd, r[9]); m2_w(fd, " pertoken_acc=" as *u8); m2_wn(fd, r[10]); m2_w(fd, "/" as *u8); m2_wn(fd, r[2]) 186 if r[11] == 1 { m2_w(fd, " verdict=GREEN\n" as *u8) } else { m2_w(fd, " verdict=RED\n" as *u8) } 187 return 0 188} 189 190func main() -> i64 { 191 var ok: i64 = 1 192 let NW: i64 = 2 * M2_T * (2 * M2_S) 193 194 let tmap: *i64 = (sys_mmap(M2_S * 8)) as *i64 195 tmap[0] = 2; tmap[1] = 0; tmap[2] = 3; tmap[3] = 1 196 197 // build train (13) + held-out (3) pair lists from all 16 (a,b); held = {(0,3),(3,0),(1,2)} 198 let pa: *i64 = (sys_mmap(16 * 8)) as *i64 199 let pb: *i64 = (sys_mmap(16 * 8)) as *i64 200 let ha: *i64 = (sys_mmap(8 * 8)) as *i64 201 let hb: *i64 = (sys_mmap(8 * 8)) as *i64 202 var ntr: i64 = 0 203 var nhd: i64 = 0 204 var a: i64 = 0 205 while a < M2_S { 206 var b: i64 = 0 207 while b < M2_S { 208 var held: i64 = 0 209 if a == 0 { if b == 3 { held = 1 } } 210 if a == 3 { if b == 0 { held = 1 } } 211 if a == 1 { if b == 2 { held = 1 } } 212 if held == 1 { ha[nhd] = a; hb[nhd] = b; nhd = nhd + 1 } 213 else { pa[ntr] = a; pb[ntr] = b; ntr = ntr + 1 } 214 b = b + 1 215 } 216 a = a + 1 217 } 218 219 // ---------- Gate A: LEARNS reordering ---------- 220 let W: *i64 = (sys_mmap(NW * 8)) as *i64 221 let lf: *i64 = (sys_mmap(8)) as *i64 222 let ll: *i64 = (sys_mmap(8)) as *i64 223 m2_train(W, pa, pb, ntr, tmap, 1200, ag_constf(1, 16), lf, ll) 224 let tr_acc: i64 = m2_acc(W, pa, pb, ntr, tmap) 225 let tr_tokens: i64 = 2 * ntr 226 var learns_pass: i64 = 1 227 if tr_acc != tr_tokens { learns_pass = 0 } 228 if nx_f32_lt(*ll, *lf) != 1 { learns_pass = 0 } // decreased 229 if nx_f32_lt(nx_f32_add(*ll, *ll), *lf) != 1 { learns_pass = 0 } // to < half 230 if learns_pass != 1 { ok = 0 } 231 232 // ---------- Gate B: GENERALIZES to held-out pairs ---------- 233 let hd_acc: i64 = m2_acc(W, ha, hb, nhd, tmap) 234 let hd_tokens: i64 = 2 * nhd 235 var gen_pass: i64 = 1 236 if hd_acc != hd_tokens { gen_pass = 0 } 237 if gen_pass != 1 { ok = 0 } 238 239 // ---------- Gate C: bit-exact ---------- 240 let Wr: *i64 = (sys_mmap(NW * 8)) as *i64 241 let lfr: *i64 = (sys_mmap(8)) as *i64 242 let llr: *i64 = (sys_mmap(8)) as *i64 243 m2_train(Wr, pa, pb, ntr, tmap, 1200, ag_constf(1, 16), lfr, llr) 244 var bitexact_pass: i64 = 1 245 var c: i64 = 0 246 while c < NW { if Wr[c] != W[c] { bitexact_pass = 0 } c = c + 1 } 247 if bitexact_pass != 1 { ok = 0 } 248 249 // ---------- Gate D: per-token model CANNOT (measured necessity contrast) ---------- 250 // per-token view of the reverse task: position 0 input=a target=tmap[b]; position 1 input=b target=tmap[a]. 251 let pt_src: *i64 = (sys_mmap(2 * 16 * 8)) as *i64 252 let pt_tgt: *i64 = (sys_mmap(2 * 16 * 8)) as *i64 253 var i: i64 = 0 254 while i < ntr { 255 pt_src[2 * i] = pa[i]; pt_tgt[2 * i] = tmap[pb[i]] 256 pt_src[2 * i + 1] = pb[i]; pt_tgt[2 * i + 1] = tmap[pa[i]] 257 i = i + 1 258 } 259 let Wpt: *i64 = (sys_mmap(M2_T * M2_S * 8)) as *i64 260 let lfp: *i64 = (sys_mmap(8)) as *i64 261 let llp: *i64 = (sys_mmap(8)) as *i64 262 mt_train(Wpt, M2_S, M2_T, pt_src, pt_tgt, ntr, 2, 300, ag_constf(1, 32), lfp, llp) 263 let pt_acc: i64 = mt_seq_acc(Wpt, M2_S, M2_T, pt_src, pt_tgt, ntr, 2) 264 var pertoken_cannot_pass: i64 = 1 265 if pt_acc >= tr_tokens { pertoken_cannot_pass = 0 } // it must FAIL to fit the reordering 266 if pertoken_cannot_pass != 1 { ok = 0 } 267 268 // ---------- emit ---------- 269 let r: *i64 = (sys_mmap(12 * 8)) as *i64 270 r[0] = learns_pass; r[1] = tr_acc; r[2] = tr_tokens 271 r[3] = m2_f32_to_milli(*lf); r[4] = m2_f32_to_milli(*ll) 272 r[5] = gen_pass; r[6] = hd_acc; r[7] = hd_tokens 273 r[8] = bitexact_pass; r[9] = pertoken_cannot_pass; r[10] = pt_acc 274 r[11] = ok 275 m2_emit(1, r) 276 var d: i64 = 0 277 while d < nhd { m2_demo(1, W, ha[d], hb[d]); d = d + 1 } 278 let logf: i64 = sys_openat_append(M2_LOG, 420) 279 if logf >= 0 { m2_emit(logf, r); d = 0; while d < nhd { m2_demo(logf, W, ha[d], hb[d]); d = d + 1 } sys_close(logf) } 280 281 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 282 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 283 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 284 let ctr__dry: *i64 = gv_ctr() 285 ctr__dry[0] = ok 286 ctr__dry[1] = 1 287 let rc__dry: i64 = gv_verdict("MT-R2-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 288 sys_exit(rc__dry) 289 return rc__dry 290}