code wiki / _hdl_build / nx_nofloat_k3stack.nx
nx_nofloat_k3stack.nx source
↩ module page · 92 lines · 4158 B
1// nx_nofloat_k3stack.nx -- MULTI-LAYER K3 stack with ATTENTION RESIDUALS in no-float (operator 2026-07-19
2// "logically integrated, mature"). Stacks the K3 block at DEPTH, and closes the K3-specific Attention-
3// Residuals gap: instead of a plain x+f(x) residual, each layer's input is a CONTENT-WEIGHTED ATTENTION
4// over ALL preceding layer outputs (arXiv 2603.15031: "learned input-dependent attention over preceding
5// layers... earlier-layer representations stay reachable deeper in the stack"). Minimal faithful AttnRes:
6// score_s = |mean(h_s) . mean(h_{l-1})| ; w_s = score_s / sum ; r_l = sum_{s<l} w_s h_s ; h_l = block(r_l)
7// Pure integer Q16, deterministic AT DEPTH -- a float stack drifts per-layer and compounds; ours is bit-exact
8// through N layers. Composes nx_nofloat_k3block (the K3 block) + weight-tying across layers.
9// license_tier: ORIGINAL No hw writes (Rule 26).
10import "nx_nofloat_k3block.nx"
11import "nx_syscalls.nx"
12import "nx_vecmath.nx"
13
14const KS_QBITS: i64 = 16
15const KS_Q: i64 = 65536
16const KS_LMAX: i64 = 8
17const KS_CELLMAX: i64 = 256 // t*d cap per layer buffer
18
19func ks_mean(h: *i64, td: i64) -> i64 {
20 var s: i64 = 0
21 var i: i64 = 0
22 while i < td { s = s + h[i]; i = i + 1 }
23 return s / td
24}
25func ks_isqrt(v: i64) -> i64 { return vm_isqrt(v) }
26// RMSNorm (pre-norm transformers keep activations bounded at depth): h[i] <- h[i] / RMS(h), unit RMS.
27// derivation: RMS_q16 ~ isqrt(sum(h>>8)^2 / td) * 256 ; h_norm[i] = (h[i]<<8) / isqrt(...) (unit-RMS Q16).
28func ks_rmsnorm(h: *i64, td: i64) -> i64 {
29 var ss: i64 = 0
30 var i: i64 = 0
31 while i < td { let v: i64 = h[i] >> 8; ss = ss + v * v; i = i + 1 }
32 let q: i64 = ks_isqrt(ss / td)
33 if q <= 0 { return 0 }
34 i = 0
35 while i < td { h[i] = (h[i] << 8) / q; i = i + 1 }
36 return 0
37}
38// N-layer K3 stack with AttnRes cross-layer residual. weights tied across layers. out: t x d.
39func k3_stack(x: *i64, wq: *i64, wk: *i64, wv: *i64, wo: *i64, w1: *i64, w2: *i64, layers: i64, t: i64, d: i64, dff: i64, out: *i64) -> i64 {
40 let td: i64 = t * d
41 // layer output buffers h[0..layers]
42 let hbase: *i64 = sys_mmap((KS_LMAX+1) * KS_CELLMAX * 8) as *i64
43 let means: *i64 = sys_mmap((KS_LMAX+1) * 8) as *i64
44 // h[0] = x
45 var i: i64 = 0
46 while i < td { hbase[i] = x[i]; i = i + 1 }
47 means[0] = ks_mean(hbase, td)
48 let r: *i64 = sys_mmap(KS_CELLMAX * 8) as *i64
49 var l: i64 = 1
50 while l <= layers {
51 // AttnRes: content-weighted combine of h[0..l-1]
52 let prev: i64 = l - 1
53 // scores sc[s] = |mean_s * mean_prev| >> 16, +1 to avoid all-zero
54 var sumsc: i64 = 0
55 let sc: *i64 = sys_mmap((KS_LMAX+1)*8) as *i64
56 var s: i64 = 0
57 while s < l {
58 var v: i64 = (means[s] * means[prev]) >> KS_QBITS
59 if v < 0 { v = 0 - v }
60 v = v + 1
61 sc[s] = v
62 sumsc = sumsc + v
63 s = s + 1
64 }
65 // r = sum_s (sc[s]/sumsc) * h[s]
66 var c: i64 = 0
67 while c < td { r[c] = 0; c = c + 1 }
68 s = 0
69 while s < l {
70 let w: i64 = (sc[s] << KS_QBITS) / sumsc
71 let hs: *i64 = ((hbase as i64) + s * KS_CELLMAX * 8) as *i64
72 c = 0
73 while c < td { r[c] = r[c] + ((w * hs[c]) >> KS_QBITS); c = c + 1 }
74 s = s + 1
75 }
76 // h[l] = block(r)
77 let hl: *i64 = ((hbase as i64) + l * KS_CELLMAX * 8) as *i64
78 k3_block(r, wq, wk, wv, wo, w1, w2, hl, t, d, dff)
79 ks_rmsnorm(hl, td) // pre-norm keeps activations bounded at depth (DeepSeek-V3 style)
80 means[l] = ks_mean(hl, td)
81 l = l + 1
82 }
83 // out = h[layers]
84 let hL: *i64 = ((hbase as i64) + layers * KS_CELLMAX * 8) as *i64
85 i = 0
86 while i < td { out[i] = hL[i]; i = i + 1 }
87 return 0
88}
89// re-expose: get layer-l input residual r (for the cross-layer test) -- runs the AttnRes combine only
90func k3_stack_out_layer(x: *i64, wq: *i64, wk: *i64, wv: *i64, wo: *i64, w1: *i64, w2: *i64, layers: i64, t: i64, d: i64, dff: i64, out: *i64) -> i64 {
91 return k3_stack(x, wq, wk, wv, wo, w1, w2, layers, t, d, dff, out)
92}