code wiki / _hdl_build / nx_fnet_train_gate.nx

nx_fnet_train_gate.nx source

↩ module page · 189 lines · 8171 B

1// nx_fnet_train_gate.nx -- GATE for FNET-003: the trainable FNet block (arc convergence). Proves, by RUNNING: 2// G1 f32 FFT correctness: the f32 radix-2 FFT equals the naive O(n^2) f32 DFT (same CORDIC->f32 twiddles) 3// at n=8 -- the f32 transform is a real Fourier transform. 4// G2 FNet-op GRADCHECK through the autograd: x[4,4] -> ta_fnet -> mse(.,target); analytic gradient (the 5// self-adjoint backward, dx = fnet_mix(dL/dy)) vs central finite difference per input cell (h=1/128, 6// rel<1/32 floor 1/64). Proves the sub-quadratic mixer is now a correctly-differentiable tape op. 7// G3 TRAIN through the op: gradient descent on the input to minimize mse(fnet(x), target) -- the loss 8// strictly decreases, so gradients flow end-to-end through the FNet block. 9// 10// Evidence -> knowledge/status/fnet_train.log (FNETTRAINGATE authored=organ ... verdict=GREEN). license_tier: ORIGINAL 11import "nx_autograd_tensor.nx" // ta_fnet + ta_* + transitively nx_fft_f32 (fftf_*, fnet_mix_f32) + fx + f32 tower 12import "nx_syscalls.nx" 13 14const FT_LOG: *u8 = "knowledge/status/fnet_train.log" 15 16func ft_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 } 17func ft_wn(fd: i64, v: i64) -> i64 { 18 let bb: *u8 = sys_mmap(28); var m: i64 = v 19 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 20 let t: *u8 = sys_mmap(28); var k: i64 = 0 21 if m == 0 { t[0] = 48; k = 1 } 22 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 23 var i: i64 = 0 24 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 25 sys_write(fd, bb, k); return 0 26} 27 28// naive O(n^2) f32 DFT using the same CORDIC->f32 twiddles as the FFT. 29func ft_dft(in_re: *i64, in_im: *i64, n: i64, out_re: *i64, out_im: *i64) -> i64 { 30 var k: i64 = 0 31 while k < n { 32 var sr: i64 = TA_F32_ZERO 33 var si: i64 = TA_F32_ZERO 34 var j: i64 = 0 35 while j < n { 36 let ang: i64 = FX_TWO_PI * ((j * k) % n) / n 37 let wr: i64 = fftf_q16_to_f32(fx_cos(ang)) 38 let wi: i64 = fftf_q16_to_f32(0 - fx_sin(ang)) 39 sr = nx_f32_add(sr, nx_f32_sub(nx_f32_mul(in_re[j], wr), nx_f32_mul(in_im[j], wi))) 40 si = nx_f32_add(si, nx_f32_add(nx_f32_mul(in_re[j], wi), nx_f32_mul(in_im[j], wr))) 41 j = j + 1 42 } 43 out_re[k] = sr; out_im[k] = si 44 k = k + 1 45 } 46 return 0 47} 48 49// ===== Gate 2/3: FNet-op loss graph: loss = mse(ta_fnet(x), target), x and target both [4,4] ===== 50func g_fnet_build(tape: *i64, vals: *i64, st: *i64, x: *i64, target: *i64, xnout: *i64) -> i64 { 51 st[0] = 0; st[1] = 0 52 let xn: i64 = ta_leaf(tape, vals, st, 4, 4, x, 0) 53 let fn: i64 = ta_fnet(tape, vals, st, xn) 54 let tn: i64 = ta_leaf(tape, vals, st, 4, 4, target, 0) 55 let loss: i64 = ta_mse(tape, vals, st, fn, tn) 56 xnout[0] = xn 57 return loss 58} 59func g_fnet_loss(tape: *i64, vals: *i64, st: *i64, x: *i64, target: *i64) -> i64 { 60 let xn: *i64 = (sys_mmap(8)) as *i64 61 let loss: i64 = g_fnet_build(tape, vals, st, x, target, xn) 62 return ta_val(tape, vals, loss, 0) 63} 64func g_fnet_grads(tape: *i64, vals: *i64, grads: *i64, st: *i64, x: *i64, target: *i64, dx: *i64) -> i64 { 65 let xn: *i64 = (sys_mmap(8)) as *i64 66 let loss: i64 = g_fnet_build(tape, vals, st, x, target, xn) 67 ta_backward(tape, vals, grads, st[0], loss) 68 var i: i64 = 0 69 while i < 16 { dx[i] = ta_grad(tape, grads, xn[0], i); i = i + 1 } 70 return 0 71} 72 73func main() -> i64 { 74 var ok: i64 = 1 75 let tape: *i64 = (sys_mmap(256 * 7 * 8)) as *i64 76 let vals: *i64 = (sys_mmap(4096 * 8)) as *i64 77 let grads: *i64 = (sys_mmap(4096 * 8)) as *i64 78 let st: *i64 = (sys_mmap(2 * 8)) as *i64 79 80 // ---------- G1: f32 FFT == naive f32 DFT at n=8 ---------- 81 let n1: i64 = 8 82 let ar: *i64 = (sys_mmap(n1 * 8)) as *i64 83 let ai: *i64 = (sys_mmap(n1 * 8)) as *i64 84 let br: *i64 = (sys_mmap(n1 * 8)) as *i64 85 let bi: *i64 = (sys_mmap(n1 * 8)) as *i64 86 var i: i64 = 0 87 while i < n1 { 88 let v: i64 = ta_constf((i % 5) - 2, 1) 89 ar[i] = v; ai[i] = TA_F32_ZERO; br[i] = v; bi[i] = TA_F32_ZERO 90 i = i + 1 91 } 92 let twr: *i64 = (sys_mmap(n1 * 8)) as *i64 93 let twi: *i64 = (sys_mmap(n1 * 8)) as *i64 94 fftf_twiddles(n1, twr, twi) 95 fftf_fwd(ar, ai, n1, twr, twi) 96 let dor: *i64 = (sys_mmap(n1 * 8)) as *i64 97 let doi: *i64 = (sys_mmap(n1 * 8)) as *i64 98 ft_dft(br, bi, n1, dor, doi) 99 var g1max: i64 = TA_F32_ZERO 100 i = 0 101 while i < n1 { 102 let d1: i64 = nx_f32_abs(nx_f32_sub(ar[i], dor[i])) 103 let d2: i64 = nx_f32_abs(nx_f32_sub(ai[i], doi[i])) 104 if nx_f32_gt(d1, g1max) == 1 { g1max = d1 } 105 if nx_f32_gt(d2, g1max) == 1 { g1max = d2 } 106 i = i + 1 107 } 108 let g1milli: i64 = ta_f32_to_milli(g1max) 109 if g1milli > 50 { ok = 0 } // FFT vs DFT agree to < 0.05 (f32 rounding-order only) 110 111 // ---------- G2: FNet-op gradcheck through the autograd ---------- 112 let x: *i64 = (sys_mmap(16 * 8)) as *i64 113 let target: *i64 = (sys_mmap(16 * 8)) as *i64 114 i = 0 115 while i < 16 { 116 x[i] = ta_constf((i % 5) - 2, 2) // -1 .. 1 117 target[i] = ta_constf((i % 7) - 3, 4) // -0.75 .. 0.75 118 i = i + 1 119 } 120 let ana: *i64 = (sys_mmap(16 * 8)) as *i64 121 g_fnet_grads(tape, vals, grads, st, x, target, ana) 122 let h: i64 = ta_constf(1, 128) 123 let flo: i64 = ta_constf(1, 64) 124 let tol: i64 = ta_constf(1, 32) 125 let xp: *i64 = (sys_mmap(16 * 8)) as *i64 126 let xm: *i64 = (sys_mmap(16 * 8)) as *i64 127 var g2pass: i64 = 1 128 var g2worst: i64 = 0 129 var c: i64 = 0 130 while c < 16 { 131 var j: i64 = 0 132 while j < 16 { xp[j] = x[j]; xm[j] = x[j]; j = j + 1 } 133 xp[c] = nx_f32_add(x[c], h) 134 xm[c] = nx_f32_sub(x[c], h) 135 let lp: i64 = g_fnet_loss(tape, vals, st, xp, target) 136 let lm: i64 = g_fnet_loss(tape, vals, st, xm, target) 137 let fd: i64 = nx_f32_div(nx_f32_sub(lp, lm), nx_f32_add(h, h)) 138 let num: i64 = nx_f32_abs(nx_f32_sub(fd, ana[c])) 139 var den: i64 = nx_f32_abs(ana[c]) 140 if nx_f32_lt(den, flo) == 1 { den = flo } 141 if nx_f32_lt(num, nx_f32_mul(tol, den)) != 1 { g2pass = 0 } 142 let nm: i64 = ta_f32_to_milli(num) 143 if nm > g2worst { g2worst = nm } 144 c = c + 1 145 } 146 if g2pass != 1 { ok = 0 } 147 148 // ---------- G3: train the input through the FNet op (loss must strictly decrease) ---------- 149 let xt: *i64 = (sys_mmap(16 * 8)) as *i64 150 i = 0 151 while i < 16 { xt[i] = TA_F32_ZERO; i = i + 1 } 152 let lr: i64 = ta_constf(1, 64) 153 let dxt: *i64 = (sys_mmap(16 * 8)) as *i64 154 var lfirst: i64 = TA_F32_ZERO 155 var llast: i64 = TA_F32_ZERO 156 var ep: i64 = 0 157 while ep < 1000 { 158 if ep == 0 { lfirst = g_fnet_loss(tape, vals, st, xt, target) } 159 g_fnet_grads(tape, vals, grads, st, xt, target, dxt) 160 i = 0 161 while i < 16 { xt[i] = nx_f32_sub(xt[i], nx_f32_mul(lr, dxt[i])); i = i + 1 } 162 ep = ep + 1 163 } 164 llast = g_fnet_loss(tape, vals, st, xt, target) 165 var g3pass: i64 = 1 166 if nx_f32_lt(llast, lfirst) != 1 { g3pass = 0 } // loss strictly decreased 167 if g3pass != 1 { ok = 0 } 168 169 // ---------- emit ---------- 170 var fdi: i64 = 1 171 while fdi >= 0 { 172 var out: i64 = 1 173 if fdi == 0 { out = sys_openat_append(FT_LOG, 420) } 174 if out >= 0 { 175 ft_w(out, "FNETTRAINGATE authored=organ block=trainable-fnet-f32" as *u8) 176 ft_w(out, " | G1_fft_vs_dft_milli=" as *u8); ft_wn(out, g1milli) 177 ft_w(out, " | G2_gradcheck_pass=" as *u8); ft_wn(out, g2pass); ft_w(out, " worst_|fd-analytic|_milli=" as *u8); ft_wn(out, g2worst) 178 ft_w(out, " | G3_train_pass=" as *u8); ft_wn(out, g3pass) 179 ft_w(out, " loss_first_milli=" as *u8); ft_wn(out, ta_f32_to_milli(lfirst)) 180 ft_w(out, " loss_last_milli=" as *u8); ft_wn(out, ta_f32_to_milli(llast)) 181 if ok == 1 { ft_w(out, " verdict=GREEN\n" as *u8) } else { ft_w(out, " verdict=RED\n" as *u8) } 182 if fdi == 0 { sys_close(out) } 183 } 184 fdi = fdi - 1 185 } 186 187 if ok == 1 { return 0 } 188 return 1 189}