nx_groupnorm.nx
buildroot/runtime/nx_groupnorm.nx
about
nx_groupnorm.nx -- Group Normalisation (Wu & He 2018).
Closes the missing-dep gap for the UNet block. Modern image-gen
architectures use GroupNorm, not RMSNorm or LayerNorm:
Stable Diffusion 1.x / 2.x / 3 UNet: GroupNorm(num_groups=32)
Flux UNet: GroupNorm
Z-Image: GroupNorm
StyleGAN family: GroupNorm + AdaIN (queued)
Substrate had RMSNorm (decoder-only LLMs) + LayerNorm (encoder-
decoders / GPT-2 era). GroupNorm was the third missing norm
variant and the load-bearing one for diffusion.
===== Math (Wu & He 2018 _Group Normalization_) =================
Input: x [N, C, H, W] Q10
Groups: G (C must be divisible by G; typically G=32)
For each (n, g):
mean = mean of x over (C/G channels in group g, H pixels, W pixels)
(i.e., (C/G * H * W) elements per group per sample)
var = variance over the same elements
For each (c in group g, h, w):
y[n, c, h, w] = (x[n, c, h, w] - mean) / sqrt(var + eps) *
gamma[c] + beta[c]
gamma is the per-channel learned scale (length C, Q10).
beta is the per-channel learned bias (length C, Q10).
Group-count edge cases:
G = 1 -> LayerNorm-like (normalize over all C*H*W)
G = C -> InstanceNorm (normalize per-channel separately)
Standard GroupNorm uses G=32 (Wu & He's recommendation, picked
to be roughly invariant across batch size).
===== Q-format ===================================================
x, gamma, beta all in Q10 (substrate convention). Accumulators
dependencies 5 imports · 1 importers
imports: nx_syscalls.nxnx_tier.nxnx_loop.nxnx_tensor.nxnx_isqrt.nx
imported by: nx_unet_block.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 64 | const NX_GN_Q10: nx_int = 1024 |
| 65 | const NX_GN_EPS_Q10: nx_int = 1 |
| 69 | const NX_GN_OK: nx_int = 0 |
| 70 | const NX_GN_ERR_BAD_DTYPE: nx_int = 1 |
| 71 | const NX_GN_ERR_BAD_NDIM: nx_int = 2 |
| 72 | const NX_GN_ERR_SHAPE_MISMATCH: nx_int = 3 |
| 73 | const NX_GN_ERR_NOT_CONTIGUOUS: nx_int = 4 |
| 74 | const NX_GN_ERR_BAD_GROUPS: nx_int = 5 |
| 75 | const NX_GN_N_VERDICTS: nx_int = 6 |
functions
| 77 | func nx_gn_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 91 | func nx_groupnorm_forward(x: *NxTensor, n_groups: nx_int, |
| 213 | func nx_groupnorm_gamma_unit(c: nx_int) -> *i64 |
| 227 | func nx_groupnorm_beta_zero(c: nx_int) -> *i64 |
| 250 | func main() -> i64 |