code wiki / (root) / nx_f32_adaln.nx

nx_f32_adaln.nx source

↩ module page · 108 lines · 4583 B

1// nx_f32_adaln.nx -- software-f32 adaptive LayerNorm (adaLN / adaLN-Zero) modulation, the DiT-specific brick. 2// 3// sd-server -> Nishi migration: this is what turns a plain transformer block into a DIFFUSION transformer 4// (DiT) block (Peebles & Xie 2023 "Scalable Diffusion Models with Transformers"; Z-Image uses it). A small 5// MLP maps the conditioning vector (timestep + pooled text) to per-block modulation params 6// {shift, scale, gate} for each sublayer; the block then applies: 7// 8// modulate(norm(x), shift, scale) = norm(x) * (1 + scale) + shift (broadcast over tokens) 9// x = x + gate * sublayer(modulate(...)) (gated residual; adaLN-Zero starts gate=0) 10// 11// This file owns the two elementwise ops (modulate + gated-residual). The conditioning MLP that produces 12// shift/scale/gate is a plain linear (nx_f32_matmul) the caller supplies. Composes nx_f32_add/mul + cvt. 13// x, out, res, h: flat *i64 f32 bits [n_tokens, D]. scale, shift, gate: [D] f32 (per-feature, token-broadcast). 14// license_tier: ORIGINAL 15import "nx_syscalls.nx" 16import "nx_f32.nx" 17import "nx_f32_cvt.nx" 18 19const NX_F32ADALN_OK: i64 = 0 20const NX_F32ADALN_ERR: i64 = 1 21 22// modulate: out[t][d] = x[t][d] * (1 + scale[d]) + shift[d] 23func nx_f32_adaln_modulate(x: *i64, n_tokens: i64, D: i64, scale: *i64, shift: *i64, out: *i64) -> i64 { 24 if n_tokens <= 0 { return NX_F32ADALN_ERR } 25 if D <= 0 { return NX_F32ADALN_ERR } 26 let one: i64 = nx_i32_to_f32(1) 27 var t: i64 = 0 28 while t < n_tokens { 29 let base: i64 = t * D 30 var d: i64 = 0 31 while d < D { 32 let sp1: i64 = nx_f32_add(one, scale[d]) // 1 + scale[d] 33 out[base + d] = nx_f32_add(nx_f32_mul(x[base + d], sp1), shift[d]) 34 d = d + 1 35 } 36 t = t + 1 37 } 38 return NX_F32ADALN_OK 39} 40 41// gated residual: out[t][d] = res[t][d] + gate[d] * h[t][d] (adaLN-Zero: gate starts at 0 -> identity init) 42func nx_f32_adaln_gate(res: *i64, h: *i64, n_tokens: i64, D: i64, gate: *i64, out: *i64) -> i64 { 43 if n_tokens <= 0 { return NX_F32ADALN_ERR } 44 if D <= 0 { return NX_F32ADALN_ERR } 45 var t: i64 = 0 46 while t < n_tokens { 47 let base: i64 = t * D 48 var d: i64 = 0 49 while d < D { 50 out[base + d] = nx_f32_add(res[base + d], nx_f32_mul(gate[d], h[base + d])) 51 d = d + 1 52 } 53 t = t + 1 54 } 55 return NX_F32ADALN_OK 56} 57 58// ===== Self-test (inline gate) -- all invariants are bit-exact f32 ===== 59// (a) modulate identity: scale=0, shift=0 -> out == x 60// (b) modulate: scale=1 -> out == 2x ; shift=k -> out == x + k 61// (c) gate-Zero: gate=0 -> out == res ; gate=1 -> out == res + h 62func main() -> i64 { 63 let x: *i64 = sys_mmap(16 * 8) as *i64 64 let out: *i64 = sys_mmap(16 * 8) as *i64 65 let res: *i64 = sys_mmap(16 * 8) as *i64 66 let hh: *i64 = sys_mmap(16 * 8) as *i64 67 let sc: *i64 = sys_mmap(8 * 8) as *i64 68 let sh: *i64 = sys_mmap(8 * 8) as *i64 69 let gt: *i64 = sys_mmap(8 * 8) as *i64 70 let one: i64 = nx_i32_to_f32(1) 71 72 // n_tokens=2, D=2. x = [1,2,3,4] 73 var i: i64 = 0 74 while i < 4 { x[i] = nx_i32_to_f32(i + 1); i = i + 1 } 75 76 // (a) identity modulate 77 sc[0] = 0; sc[1] = 0; sh[0] = 0; sh[1] = 0 78 let va: i64 = nx_f32_adaln_modulate(x, 2, 2, sc, sh, out) 79 if va != NX_F32ADALN_OK { return 10 } 80 i = 0 81 while i < 4 { if out[i] != x[i] { return 20 } i = i + 1 } 82 83 // (b) scale=1 -> 2x ; then shift=5 (scale 0) -> x+5 84 sc[0] = one; sc[1] = one; sh[0] = 0; sh[1] = 0 85 nx_f32_adaln_modulate(x, 2, 2, sc, sh, out) 86 if out[0] != nx_i32_to_f32(2) { return 30 } // 1*2 87 if out[1] != nx_i32_to_f32(4) { return 31 } // 2*2 88 if out[3] != nx_i32_to_f32(8) { return 32 } // 4*2 89 sc[0] = 0; sc[1] = 0; sh[0] = nx_i32_to_f32(5); sh[1] = nx_i32_to_f32(5) 90 nx_f32_adaln_modulate(x, 2, 2, sc, sh, out) 91 if out[0] != nx_i32_to_f32(6) { return 33 } // 1+5 92 if out[3] != nx_i32_to_f32(9) { return 34 } // 4+5 93 94 // (c) gated residual 95 i = 0 96 while i < 4 { res[i] = nx_i32_to_f32(i + 1); hh[i] = nx_i32_to_f32((i + 1) * 10); i = i + 1 } 97 gt[0] = 0; gt[1] = 0 98 let vc: i64 = nx_f32_adaln_gate(res, hh, 2, 2, gt, out) 99 if vc != NX_F32ADALN_OK { return 40 } 100 i = 0 101 while i < 4 { if out[i] != res[i] { return 41 } i = i + 1 } // gate 0 -> unchanged 102 gt[0] = one; gt[1] = one 103 nx_f32_adaln_gate(res, hh, 2, 2, gt, out) 104 if out[0] != nx_i32_to_f32(11) { return 42 } // 1 + 10 105 if out[3] != nx_i32_to_f32(44) { return 43 } // 4 + 40 106 107 return 0 108}