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}