code wiki / _hdl_build / nx_fnet_mix_gate.nx

nx_fnet_mix_gate.nx source

↩ module page · 281 lines · 11546 B

1// nx_fnet_mix_gate.nx -- GATE for FNET-001 sub-quadratic Fourier token-mixer. Proves, by RUNNING (never 2// asserting): 3// 4// CORRECTNESS 5// C1 the generalized radix-2 FFT equals the naive O(n^2) DFT (same Q14 twiddles) at n=8 and n=16 6// -- the butterfly decomposition is algebraically a real Fourier transform, not loop-counting. 7// C2 the synthesized n=32 twiddle table matches the SHIPPED, gated nx_fft hardcoded W_32 table within 8// 1 Q14 unit -- so it is the SAME transform as the proven kernel, just generalized past N=32. 9// 10// MEASURED SUB-QUADRATIC EXCEED (the thesis: math kills the VRAM/compute exponential) 11// For hidden dim d=16 and sequence lengths n in {64,256,1024,4096}, both kernels are RUN on real 12// deterministic data and their executed integer-MULTIPLY counts are measured: 13// attention = scores(Q@K^T, n*n*d) + softmax-normalize(n*n) + apply(@V, n*n*d) = 2*n^2*d + n^2 (O(n^2)) 14// fnet-mix = 2*n*d*(log2 d + log2 n) (O(n log n)) 15// (softmax's transcendental exp is excluded for BOTH as a non-MAC, the standard FLOP convention; the 16// two O(n^2*d) matmuls are the attention cost present in every attention variant.) 17// Each measured count is asserted EQUAL to its structural closed form (the counter is faithful), and the 18// gate proves: fnet_mul < attn_mul at every n, AND the attn/fnet ratio STRICTLY GROWS with n -- the 19// quadratic-vs-linearithmic crossover, compounding as context lengthens. A real output checksum from each 20// kernel is printed so the work cannot be dead-code-eliminated. 21// 22// Evidence -> knowledge/status/fnet_mix.log (FNETGATE authored=organ ... verdict=GREEN). 23// license_tier: ORIGINAL 24import "nx_fnet_mix.nx" // the kernel under test (brings nx_fft + fx + nx_syscalls transitively) 25import "nx_syscalls.nx" // sys_write / sys_openat_append / sys_close / sys_mmap 26 27const FN_LOG: *u8 = "knowledge/status/fnet_mix.log" 28 29func fn_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 } 30func fn_wn(fd: i64, v: i64) -> i64 { 31 let bb: *u8 = sys_mmap(28); var m: i64 = v 32 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 33 let t: *u8 = sys_mmap(28); var k: i64 = 0 34 if m == 0 { t[0] = 48; k = 1 } 35 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 36 var i: i64 = 0 37 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 38 sys_write(fd, bb, k); return 0 39} 40func fn_abs(x: i64) -> i64 { if x < 0 { return 0 - x } return x } 41 42// ===== naive O(n^2) DFT reference (independent of the radix-2 butterfly) ============================= 43// out[k] = sum_j in[j] * exp(-2*pi*i*j*k/n), Q14 twiddles synthesized by the same sovereign CORDIC. 44func fn_dft(in_re: *i64, in_im: *i64, n: i64, out_re: *i64, out_im: *i64) -> i64 { 45 var k: i64 = 0 46 while k < n { 47 var sr: i64 = 0 48 var si: i64 = 0 49 var j: i64 = 0 50 while j < n { 51 let idx: i64 = (j * k) % n 52 let ang: i64 = FX_TWO_PI * idx / n 53 let wr: i64 = fnet_q16_to_q14(fx_cos(ang)) 54 let wi: i64 = fnet_q16_to_q14(0 - fx_sin(ang)) 55 sr = sr + (in_re[j] * wr - in_im[j] * wi) / NX_FFT_Q 56 si = si + (in_re[j] * wi + in_im[j] * wr) / NX_FFT_Q 57 j = j + 1 58 } 59 out_re[k] = sr 60 out_im[k] = si 61 k = k + 1 62 } 63 return 0 64} 65 66// C1: max abs diff between the generalized FFT and the naive DFT on a deterministic input of length n. 67func fn_c1_fft_vs_dft(n: i64) -> i64 { 68 let ar: *i64 = (sys_mmap(n * 8)) as *i64 69 let ai: *i64 = (sys_mmap(n * 8)) as *i64 70 let br: *i64 = (sys_mmap(n * 8)) as *i64 71 let bi: *i64 = (sys_mmap(n * 8)) as *i64 72 var i: i64 = 0 73 while i < n { 74 let v: i64 = ((i * 3 + 1) % 9) - 4 // deterministic input in [-4,4] 75 ar[i] = v; ai[i] = 0 76 br[i] = v; bi[i] = 0 77 i = i + 1 78 } 79 let twr: *i64 = (sys_mmap(n * 8)) as *i64 80 let twi: *i64 = (sys_mmap(n * 8)) as *i64 81 fnet_build_twiddles(n, twr, twi) 82 let opc: *i64 = (sys_mmap(8)) as *i64 83 *opc = 0 84 fnet_fft_fwd(ar, ai, n, twr, twi, opc) // FFT in place over (ar,ai) 85 let or2: *i64 = (sys_mmap(n * 8)) as *i64 86 let oi2: *i64 = (sys_mmap(n * 8)) as *i64 87 fn_dft(br, bi, n, or2, oi2) // DFT over (br,bi) -> (or2,oi2) 88 var maxd: i64 = 0 89 i = 0 90 while i < n { 91 let d1: i64 = fn_abs(ar[i] - or2[i]) 92 let d2: i64 = fn_abs(ai[i] - oi2[i]) 93 if d1 > maxd { maxd = d1 } 94 if d2 > maxd { maxd = d2 } 95 i = i + 1 96 } 97 return maxd 98} 99 100// C2: max abs diff between synthesized n=32 twiddles and the SHIPPED nx_fft hardcoded W_32 table. 101func fn_c2_twiddle_match() -> i64 { 102 let hr: *i64 = (sys_mmap(16 * 8)) as *i64 103 let hi: *i64 = (sys_mmap(16 * 8)) as *i64 104 hr[0]=16384; hr[1]=16069; hr[2]=15137; hr[3]=13623; hr[4]=11585; hr[5]=9102; hr[6]=6270; hr[7]=3196 105 hr[8]=0; hr[9]=0-3196; hr[10]=0-6270; hr[11]=0-9102; hr[12]=0-11585; hr[13]=0-13623; hr[14]=0-15137; hr[15]=0-16069 106 hi[0]=0; hi[1]=0-3196; hi[2]=0-6270; hi[3]=0-9102; hi[4]=0-11585; hi[5]=0-13623; hi[6]=0-15137; hi[7]=0-16069 107 hi[8]=0-16384; hi[9]=0-16069; hi[10]=0-15137; hi[11]=0-13623; hi[12]=0-11585; hi[13]=0-9102; hi[14]=0-6270; hi[15]=0-3196 108 let gr: *i64 = (sys_mmap(16 * 8)) as *i64 109 let gi: *i64 = (sys_mmap(16 * 8)) as *i64 110 fnet_build_twiddles(32, gr, gi) 111 var maxd: i64 = 0 112 var k: i64 = 0 113 while k < 16 { 114 let d1: i64 = fn_abs(gr[k] - hr[k]) 115 let d2: i64 = fn_abs(gi[k] - hi[k]) 116 if d1 > maxd { maxd = d1 } 117 if d2 > maxd { maxd = d2 } 118 k = k + 1 119 } 120 return maxd 121} 122 123// ===== attention baseline: faithful scaled-dot-product attention MAC count + real checksum =========== 124// Counts integer multiplies: scores (n*n*d) + softmax-normalize (n*n) + apply (n*n*d) = 2*n^2*d + n^2. 125// Streamed one query row at a time so memory stays O(n). Writes checksum via chk; returns the multiply count. 126func fn_attn_measure(n: i64, d: i64, chk: *i64) -> i64 { 127 let q: *i64 = (sys_mmap(n * d * 8)) as *i64 128 let kk: *i64 = (sys_mmap(n * d * 8)) as *i64 129 let vv: *i64 = (sys_mmap(n * d * 8)) as *i64 130 var f: i64 = 0 131 while f < n * d { 132 q[f] = ((f * 7 + 3) % 11) - 5 133 kk[f] = ((f * 5 + 1) % 9) - 4 134 vv[f] = ((f * 3 + 2) % 7) - 3 135 f = f + 1 136 } 137 let row: *i64 = (sys_mmap(n * 8)) as *i64 138 var mul: i64 = 0 139 var checksum: i64 = 0 140 var i: i64 = 0 141 while i < n { 142 // scores[j] = dot(Q[i,:], K[j,:]) -- n*d multiplies 143 var j: i64 = 0 144 while j < n { 145 var acc: i64 = 0 146 var t: i64 = 0 147 while t < d { 148 acc = acc + q[i * d + t] * kk[j * d + t] 149 mul = mul + 1 150 t = t + 1 151 } 152 row[j] = acc 153 j = j + 1 154 } 155 // softmax: max-subtract + bounded positive surrogate (the exp is a non-MAC, excluded by FLOP 156 // convention from both kernels); then normalize -- n multiplies. 157 var mx: i64 = row[0] 158 j = 1 159 while j < n { if row[j] > mx { mx = row[j] } j = j + 1 } 160 var sum: i64 = 0 161 j = 0 162 while j < n { 163 var w: i64 = 8192 + (row[j] - mx) 164 if w < 1 { w = 1 } 165 row[j] = w 166 sum = sum + w 167 j = j + 1 168 } 169 if sum <= 0 { sum = 1 } 170 j = 0 171 while j < n { row[j] = (row[j] * 1024) / sum; mul = mul + 1; j = j + 1 } 172 // apply: out[i,c] = sum_j weights[j] * V[j,c] -- n*d multiplies 173 var c: i64 = 0 174 while c < d { 175 var oacc: i64 = 0 176 var j2: i64 = 0 177 while j2 < n { 178 oacc = oacc + row[j2] * vv[j2 * d + c] 179 mul = mul + 1 180 j2 = j2 + 1 181 } 182 checksum = checksum + (oacc / 1024) 183 c = c + 1 184 } 185 i = i + 1 186 } 187 *chk = checksum 188 return mul 189} 190 191// ===== fnet baseline: run the real mixer, read its executed-multiply counter + real checksum ========== 192func fn_fnet_measure(n: i64, d: i64, chk: *i64) -> i64 { 193 let xr: *i64 = (sys_mmap(n * d * 8)) as *i64 194 let xi: *i64 = (sys_mmap(n * d * 8)) as *i64 195 var f: i64 = 0 196 while f < n * d { xr[f] = ((f * 7 + 3) % 11) - 5; xi[f] = 0; f = f + 1 } 197 let opc: *i64 = (sys_mmap(8)) as *i64 198 *opc = 0 199 fnet_mix(xr, xi, n, d, opc) 200 var checksum: i64 = 0 201 f = 0 202 while f < n * d { checksum = checksum + xr[f]; f = f + 1 } 203 *chk = checksum 204 return *opc 205} 206 207func fn_emit(fd: i64, c1a: i64, c1b: i64, c2: i64, ns: *i64, am: *i64, fm: *i64, rt: *i64, chk: i64, ok: i64) -> i64 { 208 fn_w(fd, "FNETGATE authored=organ kernel=fnet-fourier-mix metric=int-multiplies d=16" as *u8) 209 fn_w(fd, " c1_fft_vs_dft_n8=" as *u8); fn_wn(fd, c1a) 210 fn_w(fd, " c1_fft_vs_dft_n16=" as *u8); fn_wn(fd, c1b) 211 fn_w(fd, " c2_twiddle_maxdiff_vs_nxfft=" as *u8); fn_wn(fd, c2) 212 var i: i64 = 0 213 while i < 4 { 214 fn_w(fd, " | n=" as *u8); fn_wn(fd, ns[i]) 215 fn_w(fd, " attn_mul=" as *u8); fn_wn(fd, am[i]) 216 fn_w(fd, " fnet_mul=" as *u8); fn_wn(fd, fm[i]) 217 fn_w(fd, " ratio=" as *u8); fn_wn(fd, rt[i]) 218 i = i + 1 219 } 220 fn_w(fd, " work_chk=" as *u8); fn_wn(fd, chk) 221 if ok == 1 { fn_w(fd, " verdict=GREEN\n" as *u8) } else { fn_w(fd, " verdict=RED\n" as *u8) } 222 return 0 223} 224 225func main() -> i64 { 226 var ok: i64 = 1 227 228 // --- correctness --- 229 let c1a: i64 = fn_c1_fft_vs_dft(8) 230 let c1b: i64 = fn_c1_fft_vs_dft(16) 231 let c2: i64 = fn_c2_twiddle_match() 232 if c1a > 32 { ok = 0 } // bound ~ 4*n fixed-point accumulation; actuals reported 233 if c1b > 64 { ok = 0 } 234 if c2 > 1 { ok = 0 } // synthesized twiddles within 1 Q14 unit of the shipped table 235 236 // --- measured sub-quadratic crossover --- 237 let d: i64 = 16 238 let ns: *i64 = (sys_mmap(4 * 8)) as *i64 239 ns[0] = 64; ns[1] = 256; ns[2] = 1024; ns[3] = 4096 240 let am: *i64 = (sys_mmap(4 * 8)) as *i64 241 let fm: *i64 = (sys_mmap(4 * 8)) as *i64 242 let rt: *i64 = (sys_mmap(4 * 8)) as *i64 243 let chkbox: *i64 = (sys_mmap(8)) as *i64 244 var work_chk: i64 = 0 245 246 var i: i64 = 0 247 while i < 4 { 248 let n: i64 = ns[i] 249 let amul: i64 = fn_attn_measure(n, d, chkbox) 250 work_chk = work_chk + *chkbox 251 let fmul: i64 = fn_fnet_measure(n, d, chkbox) 252 work_chk = work_chk + *chkbox 253 am[i] = amul 254 fm[i] = fmul 255 rt[i] = amul / fmul 256 257 // counter-faithfulness: measured count == structural closed form (proves the counters are real). 258 let attn_expect: i64 = 2 * n * n * d + n * n 259 let ln: i64 = nx_fft_log2(n) 260 let ld: i64 = nx_fft_log2(d) 261 let fnet_expect: i64 = 2 * n * d * (ld + ln) 262 if amul != attn_expect { ok = 0 } 263 if fmul != fnet_expect { ok = 0 } 264 265 // exceed: fnet strictly cheaper at every n. 266 if fmul >= amul { ok = 0 } 267 i = i + 1 268 } 269 270 // crossover: the attn/fnet advantage STRICTLY GROWS with sequence length (quadratic vs linearithmic). 271 if rt[1] <= rt[0] { ok = 0 } 272 if rt[2] <= rt[1] { ok = 0 } 273 if rt[3] <= rt[2] { ok = 0 } 274 275 fn_emit(1, c1a, c1b, c2, ns, am, fm, rt, work_chk, ok) 276 let lf: i64 = sys_openat_append(FN_LOG, 420) 277 if lf >= 0 { fn_emit(lf, c1a, c1b, c2, ns, am, fm, rt, work_chk, ok); sys_close(lf) } 278 279 if ok == 1 { return 0 } 280 return 1 281}