code wiki / _hdl_build / nx_nofloat_linattn_gate.nx
nx_nofloat_linattn_gate.nx source
↩ module page · 100 lines · 4035 B
1// nx_nofloat_linattn_gate.nx -- gate for the no-float linear-attention recurrent kernel (KDA foundation).
2// T1 recurrent output MATCHES the direct linear-attention formula o_t = sum_{s<=t}(q_t.k_s) v_s
3// T2 CAUSAL: o_t depends only on s<=t (truncating the sequence at t leaves o_t unchanged)
4// T3 O(1)-memory state accumulates correctly (S after T = sum k_s (x) v_s)
5// T4 DETERMINISTIC bit-identical (the exceed: float linear-attn drifts by accumulation order)
6// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
7import "nx_nofloat_linattn.nx"
8import "nx_gate_verdict.nx"
9import "nx_syscalls.nx"
10
11const LG_T: i64 = 4
12const LG_D: i64 = 4
13const LG_Q: i64 = 65536
14const LG_QBITS: i64 = 16
15
16// direct linear attention: o_t[j] = sum_{s<=t} (sum_i q_t[i] k_s[i]) v_s[j] (Q16)
17func lg_direct(qm: *i64, km: *i64, vm: *i64, om: *i64, t: i64, d: i64) -> i64 {
18 var ti: i64 = 0
19 while ti < t {
20 var j: i64 = 0
21 while j < d {
22 var acc: i64 = 0
23 var s: i64 = 0
24 while s <= ti {
25 // qk = q_t . k_s
26 var qk: i64 = 0
27 var i: i64 = 0
28 while i < d { qk = qk + qm[ti*d+i] * km[s*d+i]; i = i + 1 }
29 qk = qk >> LG_QBITS
30 acc = acc + ((qk * vm[s*d+j]) >> LG_QBITS)
31 s = s + 1
32 }
33 om[ti*d+j] = acc
34 j = j + 1
35 }
36 ti = ti + 1
37 }
38 return 0
39}
40
41func main() -> i64 {
42 let ctr: *i64 = gv_ctr()
43 gv_head("nx_nofloat_linattn gate -- linear-attention recurrent kernel (KDA foundation, no-float)" as *u8)
44 let t: i64 = LG_T
45 let d: i64 = LG_D
46 let qm: *i64 = sys_mmap(64*8) as *i64
47 let km: *i64 = sys_mmap(64*8) as *i64
48 let vm: *i64 = sys_mmap(64*8) as *i64
49 // deterministic pseudo-values in Q16 (small, distinct): fill Q,K,V
50 var i: i64 = 0
51 while i < t*d {
52 qm[i] = ((i * 7) % 5 + 1) * (LG_Q / 8) // 0.125..0.625
53 km[i] = ((i * 3) % 4 + 1) * (LG_Q / 8)
54 vm[i] = ((i * 5) % 6 + 1) * (LG_Q / 8)
55 i = i + 1
56 }
57 let orec: *i64 = sys_mmap(64*8) as *i64
58 let odir: *i64 = sys_mmap(64*8) as *i64
59 la_forward(qm, km, vm, orec, t, d)
60 lg_direct(qm, km, vm, odir, t, d)
61
62 // T1: recurrent == direct (within tiny Q16 rounding tolerance)
63 var t1: i64 = 1
64 i = 0
65 while i < t*d { var e: i64 = orec[i] - odir[i]; if e < 0 { e = 0 - e } if e > 4 { t1 = 0 } i = i + 1 }
66 gv_check("T1 recurrent output MATCHES direct linear-attention formula (<=4 ulp)" as *u8, t1, ctr)
67
68 // T2: causal -- o_1 (t=2 seq) unchanged when sequence truncated to length 2
69 let orec2: *i64 = sys_mmap(64*8) as *i64
70 la_forward(qm, km, vm, orec2, 2, d)
71 var t2: i64 = 1
72 i = 0
73 while i < 2*d { if orec2[i] != orec[i] { t2 = 0 } i = i + 1 }
74 gv_check("T2 CAUSAL (truncating seq to length 2 leaves o_0,o_1 identical)" as *u8, t2, ctr)
75
76 // T3: state after full pass = sum k_s (x) v_s
77 let s: *i64 = sys_mmap(LA_DMAX*LA_DMAX*8) as *i64
78 la_zero(s, d)
79 var ts: i64 = 0
80 while ts < t { la_accum(s, ((km as i64)+ts*d*8) as *i64, ((vm as i64)+ts*d*8) as *i64, d); ts = ts + 1 }
81 // reference S[0][0] = sum_s k_s[0]*v_s[0] >>16
82 var ref00: i64 = 0
83 ts = 0
84 while ts < t { ref00 = ref00 + ((km[ts*d+0] * vm[ts*d+0]) >> LG_QBITS); ts = ts + 1 }
85 var t3: i64 = 0
86 if s[0] == ref00 { t3 = 1 }
87 gv_check("T3 O(1)-memory state accumulates (S[0][0] = sum k_s[0] v_s[0])" as *u8, t3, ctr)
88
89 // T4: deterministic
90 let ob: *i64 = sys_mmap(64*8) as *i64
91 la_forward(qm, km, vm, ob, t, d)
92 var t4: i64 = 1
93 i = 0
94 while i < t*d { if ob[i] != orec[i] { t4 = 0 } i = i + 1 }
95 gv_check("T4 DETERMINISTIC bit-identical (the exceed vs order-dependent float linear-attn)" as *u8, t4, ctr)
96
97 let rc: i64 = gv_verdict("NOFLOAT-LINATTN-GATE" as *u8, ctr, "linear-attention recurrent kernel (KDA foundation): matches direct formula, causal, O(1)-state, bit-exact deterministic" as *u8)
98 sys_exit(rc)
99 return rc
100}