code wiki / _hdl_build / nx_fnet_mix_gate.nx

nx_fnet_mix_gate.nx source

↩ module page · 289 lines · 12080 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 26import "nx_gate_verdict.nx" 27 28const FN_LOG: *u8 = "knowledge/status/fnet_mix.log" 29 30func 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 } 31func fn_wn(fd: i64, v: i64) -> i64 { 32 let bb: *u8 = sys_mmap(28); var m: i64 = v 33 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 34 let t: *u8 = sys_mmap(28); var k: i64 = 0 35 if m == 0 { t[0] = 48; k = 1 } 36 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 37 var i: i64 = 0 38 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 39 sys_write(fd, bb, k); return 0 40} 41func fn_abs(x: i64) -> i64 { if x < 0 { return 0 - x } return x } 42 43// ===== naive O(n^2) DFT reference (independent of the radix-2 butterfly) ============================= 44// out[k] = sum_j in[j] * exp(-2*pi*i*j*k/n), Q14 twiddles synthesized by the same sovereign CORDIC. 45func fn_dft(in_re: *i64, in_im: *i64, n: i64, out_re: *i64, out_im: *i64) -> i64 { 46 var k: i64 = 0 47 while k < n { 48 var sr: i64 = 0 49 var si: i64 = 0 50 var j: i64 = 0 51 while j < n { 52 let idx: i64 = (j * k) % n 53 let ang: i64 = FX_TWO_PI * idx / n 54 let wr: i64 = fnet_q16_to_q14(fx_cos(ang)) 55 let wi: i64 = fnet_q16_to_q14(0 - fx_sin(ang)) 56 sr = sr + (in_re[j] * wr - in_im[j] * wi) / NX_FFT_Q 57 si = si + (in_re[j] * wi + in_im[j] * wr) / NX_FFT_Q 58 j = j + 1 59 } 60 out_re[k] = sr 61 out_im[k] = si 62 k = k + 1 63 } 64 return 0 65} 66 67// C1: max abs diff between the generalized FFT and the naive DFT on a deterministic input of length n. 68func fn_c1_fft_vs_dft(n: i64) -> i64 { 69 let ar: *i64 = (sys_mmap(n * 8)) as *i64 70 let ai: *i64 = (sys_mmap(n * 8)) as *i64 71 let br: *i64 = (sys_mmap(n * 8)) as *i64 72 let bi: *i64 = (sys_mmap(n * 8)) as *i64 73 var i: i64 = 0 74 while i < n { 75 let v: i64 = ((i * 3 + 1) % 9) - 4 // deterministic input in [-4,4] 76 ar[i] = v; ai[i] = 0 77 br[i] = v; bi[i] = 0 78 i = i + 1 79 } 80 let twr: *i64 = (sys_mmap(n * 8)) as *i64 81 let twi: *i64 = (sys_mmap(n * 8)) as *i64 82 fnet_build_twiddles(n, twr, twi) 83 let opc: *i64 = (sys_mmap(8)) as *i64 84 *opc = 0 85 fnet_fft_fwd(ar, ai, n, twr, twi, opc) // FFT in place over (ar,ai) 86 let or2: *i64 = (sys_mmap(n * 8)) as *i64 87 let oi2: *i64 = (sys_mmap(n * 8)) as *i64 88 fn_dft(br, bi, n, or2, oi2) // DFT over (br,bi) -> (or2,oi2) 89 var maxd: i64 = 0 90 i = 0 91 while i < n { 92 let d1: i64 = fn_abs(ar[i] - or2[i]) 93 let d2: i64 = fn_abs(ai[i] - oi2[i]) 94 if d1 > maxd { maxd = d1 } 95 if d2 > maxd { maxd = d2 } 96 i = i + 1 97 } 98 return maxd 99} 100 101// C2: max abs diff between synthesized n=32 twiddles and the SHIPPED nx_fft hardcoded W_32 table. 102func fn_c2_twiddle_match() -> i64 { 103 let hr: *i64 = (sys_mmap(16 * 8)) as *i64 104 let hi: *i64 = (sys_mmap(16 * 8)) as *i64 105 hr[0]=16384; hr[1]=16069; hr[2]=15137; hr[3]=13623; hr[4]=11585; hr[5]=9102; hr[6]=6270; hr[7]=3196 106 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 107 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 108 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 109 let gr: *i64 = (sys_mmap(16 * 8)) as *i64 110 let gi: *i64 = (sys_mmap(16 * 8)) as *i64 111 fnet_build_twiddles(32, gr, gi) 112 var maxd: i64 = 0 113 var k: i64 = 0 114 while k < 16 { 115 let d1: i64 = fn_abs(gr[k] - hr[k]) 116 let d2: i64 = fn_abs(gi[k] - hi[k]) 117 if d1 > maxd { maxd = d1 } 118 if d2 > maxd { maxd = d2 } 119 k = k + 1 120 } 121 return maxd 122} 123 124// ===== attention baseline: faithful scaled-dot-product attention MAC count + real checksum =========== 125// Counts integer multiplies: scores (n*n*d) + softmax-normalize (n*n) + apply (n*n*d) = 2*n^2*d + n^2. 126// Streamed one query row at a time so memory stays O(n). Writes checksum via chk; returns the multiply count. 127func fn_attn_measure(n: i64, d: i64, chk: *i64) -> i64 { 128 let q: *i64 = (sys_mmap(n * d * 8)) as *i64 129 let kk: *i64 = (sys_mmap(n * d * 8)) as *i64 130 let vv: *i64 = (sys_mmap(n * d * 8)) as *i64 131 var f: i64 = 0 132 while f < n * d { 133 q[f] = ((f * 7 + 3) % 11) - 5 134 kk[f] = ((f * 5 + 1) % 9) - 4 135 vv[f] = ((f * 3 + 2) % 7) - 3 136 f = f + 1 137 } 138 let row: *i64 = (sys_mmap(n * 8)) as *i64 139 var mul: i64 = 0 140 var checksum: i64 = 0 141 var i: i64 = 0 142 while i < n { 143 // scores[j] = dot(Q[i,:], K[j,:]) -- n*d multiplies 144 var j: i64 = 0 145 while j < n { 146 var acc: i64 = 0 147 var t: i64 = 0 148 while t < d { 149 acc = acc + q[i * d + t] * kk[j * d + t] 150 mul = mul + 1 151 t = t + 1 152 } 153 row[j] = acc 154 j = j + 1 155 } 156 // softmax: max-subtract + bounded positive surrogate (the exp is a non-MAC, excluded by FLOP 157 // convention from both kernels); then normalize -- n multiplies. 158 var mx: i64 = row[0] 159 j = 1 160 while j < n { if row[j] > mx { mx = row[j] } j = j + 1 } 161 var sum: i64 = 0 162 j = 0 163 while j < n { 164 var w: i64 = 8192 + (row[j] - mx) 165 if w < 1 { w = 1 } 166 row[j] = w 167 sum = sum + w 168 j = j + 1 169 } 170 if sum <= 0 { sum = 1 } 171 j = 0 172 while j < n { row[j] = (row[j] * 1024) / sum; mul = mul + 1; j = j + 1 } 173 // apply: out[i,c] = sum_j weights[j] * V[j,c] -- n*d multiplies 174 var c: i64 = 0 175 while c < d { 176 var oacc: i64 = 0 177 var j2: i64 = 0 178 while j2 < n { 179 oacc = oacc + row[j2] * vv[j2 * d + c] 180 mul = mul + 1 181 j2 = j2 + 1 182 } 183 checksum = checksum + (oacc / 1024) 184 c = c + 1 185 } 186 i = i + 1 187 } 188 *chk = checksum 189 return mul 190} 191 192// ===== fnet baseline: run the real mixer, read its executed-multiply counter + real checksum ========== 193func fn_fnet_measure(n: i64, d: i64, chk: *i64) -> i64 { 194 let xr: *i64 = (sys_mmap(n * d * 8)) as *i64 195 let xi: *i64 = (sys_mmap(n * d * 8)) as *i64 196 var f: i64 = 0 197 while f < n * d { xr[f] = ((f * 7 + 3) % 11) - 5; xi[f] = 0; f = f + 1 } 198 let opc: *i64 = (sys_mmap(8)) as *i64 199 *opc = 0 200 fnet_mix(xr, xi, n, d, opc) 201 var checksum: i64 = 0 202 f = 0 203 while f < n * d { checksum = checksum + xr[f]; f = f + 1 } 204 *chk = checksum 205 return *opc 206} 207 208func fn_emit(fd: i64, c1a: i64, c1b: i64, c2: i64, ns: *i64, am: *i64, fm: *i64, rt: *i64, chk: i64, ok: i64) -> i64 { 209 fn_w(fd, "FNETGATE authored=organ kernel=fnet-fourier-mix metric=int-multiplies d=16" as *u8) 210 fn_w(fd, " c1_fft_vs_dft_n8=" as *u8); fn_wn(fd, c1a) 211 fn_w(fd, " c1_fft_vs_dft_n16=" as *u8); fn_wn(fd, c1b) 212 fn_w(fd, " c2_twiddle_maxdiff_vs_nxfft=" as *u8); fn_wn(fd, c2) 213 var i: i64 = 0 214 while i < 4 { 215 fn_w(fd, " | n=" as *u8); fn_wn(fd, ns[i]) 216 fn_w(fd, " attn_mul=" as *u8); fn_wn(fd, am[i]) 217 fn_w(fd, " fnet_mul=" as *u8); fn_wn(fd, fm[i]) 218 fn_w(fd, " ratio=" as *u8); fn_wn(fd, rt[i]) 219 i = i + 1 220 } 221 fn_w(fd, " work_chk=" as *u8); fn_wn(fd, chk) 222 if ok == 1 { fn_w(fd, " verdict=GREEN\n" as *u8) } else { fn_w(fd, " verdict=RED\n" as *u8) } 223 return 0 224} 225 226func main() -> i64 { 227 var ok: i64 = 1 228 229 // --- correctness --- 230 let c1a: i64 = fn_c1_fft_vs_dft(8) 231 let c1b: i64 = fn_c1_fft_vs_dft(16) 232 let c2: i64 = fn_c2_twiddle_match() 233 if c1a > 32 { ok = 0 } // bound ~ 4*n fixed-point accumulation; actuals reported 234 if c1b > 64 { ok = 0 } 235 if c2 > 1 { ok = 0 } // synthesized twiddles within 1 Q14 unit of the shipped table 236 237 // --- measured sub-quadratic crossover --- 238 let d: i64 = 16 239 let ns: *i64 = (sys_mmap(4 * 8)) as *i64 240 ns[0] = 64; ns[1] = 256; ns[2] = 1024; ns[3] = 4096 241 let am: *i64 = (sys_mmap(4 * 8)) as *i64 242 let fm: *i64 = (sys_mmap(4 * 8)) as *i64 243 let rt: *i64 = (sys_mmap(4 * 8)) as *i64 244 let chkbox: *i64 = (sys_mmap(8)) as *i64 245 var work_chk: i64 = 0 246 247 var i: i64 = 0 248 while i < 4 { 249 let n: i64 = ns[i] 250 let amul: i64 = fn_attn_measure(n, d, chkbox) 251 work_chk = work_chk + *chkbox 252 let fmul: i64 = fn_fnet_measure(n, d, chkbox) 253 work_chk = work_chk + *chkbox 254 am[i] = amul 255 fm[i] = fmul 256 rt[i] = amul / fmul 257 258 // counter-faithfulness: measured count == structural closed form (proves the counters are real). 259 let attn_expect: i64 = 2 * n * n * d + n * n 260 let ln: i64 = nx_fft_log2(n) 261 let ld: i64 = nx_fft_log2(d) 262 let fnet_expect: i64 = 2 * n * d * (ld + ln) 263 if amul != attn_expect { ok = 0 } 264 if fmul != fnet_expect { ok = 0 } 265 266 // exceed: fnet strictly cheaper at every n. 267 if fmul >= amul { ok = 0 } 268 i = i + 1 269 } 270 271 // crossover: the attn/fnet advantage STRICTLY GROWS with sequence length (quadratic vs linearithmic). 272 if rt[1] <= rt[0] { ok = 0 } 273 if rt[2] <= rt[1] { ok = 0 } 274 if rt[3] <= rt[2] { ok = 0 } 275 276 fn_emit(1, c1a, c1b, c2, ns, am, fm, rt, work_chk, ok) 277 let lf: i64 = sys_openat_append(FN_LOG, 420) 278 if lf >= 0 { fn_emit(lf, c1a, c1b, c2, ns, am, fm, rt, work_chk, ok); sys_close(lf) } 279 280 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 281 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 282 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 283 let ctr__dry: *i64 = gv_ctr() 284 ctr__dry[0] = ok 285 ctr__dry[1] = 1 286 let rc__dry: i64 = gv_verdict("FNET-MIX-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 287 sys_exit(rc__dry) 288 return rc__dry 289}