code wiki / _hdl_build / nx_nofloat_muon_ns.nx
nx_nofloat_muon_ns.nx source
↩ module page · 108 lines · 4567 B
1// nx_nofloat_muon_ns.nx -- SOVEREIGN no-float Newton-Schulz orthogonalization = the core of the Muon
2// optimizer (K3 F405 gap; operator 2026-07-19 "beyond SOTA, novel"). Muon = MomentUm Orthogonalized by
3// Newton-Schulz (Keller Jordan). NS quintic iteration drives a matrix toward its orthogonal polar factor:
4// X0 = G / ||G||_F (Frobenius-normalize so spectral norm <= 1)
5// X_{k+1} = a*X + b*(A*X) + c*(A*A*X), A = X*X^T, (a,b,c) = (3.4445, -4.7750, 2.0315)
6// After ~5 iters X*X^T -> I. Done in PURE INTEGER Q16 (matmul accumulate-then-shift + integer isqrt) =
7// BIT-EXACT DETERMINISTIC orthogonalization -- a float training stack CANNOT reproduce this bit-for-bit
8// (non-associative accumulation); that is the novel exceed. Grounded: kellerjordan.github.io/posts/muon.
9// license_tier: ORIGINAL No hw writes (Rule 26).
10import "nx_syscalls.nx"
11import "nx_vecmath.nx"
12
13const MN_Q: i64 = 65536 // Q16 one
14const MN_QBITS: i64 = 16
15const MN_NMAX: i64 = 8 // max matrix dim supported
16// NS quintic coeffs in Q16 (Muon): a=3.4445, b=-4.7750, c=2.0315
17const MN_A: i64 = 225755
18const MN_B: i64 = 0 - 312934
19const MN_C: i64 = 133136
20const MN_ITERS: i64 = 5
21
22// integer sqrt of a non-negative i64 (floor). Newton iteration.
23func mn_isqrt(v: i64) -> i64 { return vm_isqrt(v) }
24// C[n x n] = A[n x n] * B[n x n] in Q16 (accumulate i64, >>16). A,B,C distinct buffers.
25func mn_mm(a: *i64, b: *i64, c: *i64, n: i64) -> i64 {
26 var i: i64 = 0
27 while i < n {
28 var j: i64 = 0
29 while j < n {
30 var s: i64 = 0
31 var k: i64 = 0
32 while k < n { s = s + a[i*n+k] * b[k*n+j]; k = k + 1 }
33 c[i*n+j] = s >> MN_QBITS
34 j = j + 1
35 }
36 i = i + 1
37 }
38 return 0
39}
40// C = A^T (n x n)
41func mn_transpose(a: *i64, c: *i64, n: i64) -> i64 {
42 var i: i64 = 0
43 while i < n { var j: i64 = 0; while j < n { c[j*n+i] = a[i*n+j]; j = j + 1 } i = i + 1 }
44 return 0
45}
46// out = pa*A + pb*B + pc*C (scalars in Q16, matrices in Q16) elementwise, n x n
47func mn_comb(a: *i64, b: *i64, c: *i64, out: *i64, pa: i64, pb: i64, pc: i64, n: i64) -> i64 {
48 var i: i64 = 0
49 let nn: i64 = n * n
50 while i < nn {
51 out[i] = ((pa * a[i]) >> MN_QBITS) + ((pb * b[i]) >> MN_QBITS) + ((pc * c[i]) >> MN_QBITS)
52 i = i + 1
53 }
54 return 0
55}
56// Newton-Schulz orthogonalize G (n x n, Q16) -> X (n x n, Q16), `iters` NS steps.
57// Muon NS = APPROXIMATE orthogonalization: singular values -> ~1 (fixed points 0.87/1.26), not exactly 1.
58func mn_orthogonalize_it(g: *i64, x: *i64, n: i64, iters: i64) -> i64 {
59 let nn: i64 = n * n
60 // Frobenius norm ||G||_F = sqrt(sum g^2). g in Q16 -> g^2 in Q32; sum in i64; sqrt -> Q16 norm.
61 var ss: i64 = 0
62 var i: i64 = 0
63 while i < nn { let v: i64 = g[i] >> 8; ss = ss + v * v; i = i + 1 } // (g>>8)^2 = g^2 >> 16 = Q16-scaled sumsq
64 let fro: i64 = mn_isqrt(ss) // ~ ||G||_F in Q8-ish; normalize by ratio
65 if fro <= 0 { return 0 }
66 // X0 = G / ||G||_F : x = g * Q / (fro<<8) (fro was sqrt of Q16-scaled -> scale back)
67 let denom: i64 = fro << 8
68 i = 0
69 while i < nn { x[i] = (g[i] << MN_QBITS) / denom; i = i + 1 }
70 // iteration buffers
71 let xt: *i64 = sys_mmap(MN_NMAX*MN_NMAX*8) as *i64
72 let aM: *i64 = sys_mmap(MN_NMAX*MN_NMAX*8) as *i64
73 let ax: *i64 = sys_mmap(MN_NMAX*MN_NMAX*8) as *i64
74 let aax: *i64 = sys_mmap(MN_NMAX*MN_NMAX*8) as *i64
75 var it: i64 = 0
76 while it < iters {
77 mn_transpose(x, xt, n) // X^T
78 mn_mm(x, xt, aM, n) // A = X X^T
79 mn_mm(aM, x, ax, n) // A X
80 mn_mm(aM, ax, aax, n) // A A X
81 mn_comb(x, ax, aax, x, MN_A, MN_B, MN_C, n) // X = aX + b(AX) + c(AAX)
82 it = it + 1
83 }
84 return 0
85}
86// default 5-iteration Muon NS
87func mn_orthogonalize(g: *i64, x: *i64, n: i64) -> i64 { return mn_orthogonalize_it(g, x, n, MN_ITERS) }
88// orthogonality error metric: sum |(X X^T)[i][j] - I[i][j]| in Q16 (0 = perfectly orthogonal)
89func mn_ortho_err(x: *i64, n: i64) -> i64 {
90 let xt: *i64 = sys_mmap(MN_NMAX*MN_NMAX*8) as *i64
91 let g: *i64 = sys_mmap(MN_NMAX*MN_NMAX*8) as *i64
92 mn_transpose(x, xt, n)
93 mn_mm(x, xt, g, n)
94 var e: i64 = 0
95 var i: i64 = 0
96 while i < n {
97 var j: i64 = 0
98 while j < n {
99 var t: i64 = g[i*n+j]
100 if i == j { t = t - MN_Q }
101 if t < 0 { t = 0 - t }
102 e = e + t
103 j = j + 1
104 }
105 i = i + 1
106 }
107 return e
108}