nx_layernorm.nx
buildroot/runtime/nx_layernorm.nx
about
nx_layernorm.nx -- Layer Normalisation (Ba/Kiros/Hinton 2016).
Sibling of nx_rmsnorm.nx (shipped dfce1e32). RMSNorm drops the
mean-subtract centering for speed; LayerNorm keeps it. LayerNorm
is still the choice for many architectures:
GPT-2 / GPT-3 / GPT-Neo / GPT-J
BERT / RoBERTa / DistilBERT
T5 / mT5 / ByT5
ViT / DeiT / Swin / CLIP
Whisper, Wav2Vec
Modern decoder-only LLMs (Llama, Mistral, Qwen) moved to RMSNorm
for the ~50% speed win at equivalent quality; encoder-decoder and
older architectures use LayerNorm. The substrate ships both so
the model loader picks per-layer.
===== Math =======================================================
For each token i, hidden dim D:
mean = sum_d x[i, d] / D
var = sum_d (x[i, d] - mean)^2 / D
norm = (x[i, d] - mean) / sqrt(var + eps)
y[i, d] = norm * gamma[d] + beta[d]
gamma is the learned per-channel scale (init Q10=1024).
beta is the learned per-channel bias (init 0).
===== Q-format ===================================================
All tensors in Q10 (substrate convention). mean accumulates in
raw integer (sum of Q10 values). variance accumulates as Q20
(sum of Q10*Q10 differences), divided to Q10 before sqrt.
eps_q10 = 1 (tightest Q10 stabiliser, same as RMSNorm).
Per the bits-up cardinal: composes
NxTensor (L1 canonical container)
nx_isqrt_q10 (L2 canonical sqrt)
dependencies 5 imports · 0 importers
imports: nx_syscalls.nxnx_tier.nxnx_loop.nxnx_tensor.nxnx_isqrt.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 70 | const NX_LN_Q10: nx_int = 1024 |
| 71 | const NX_LN_EPS_Q10: nx_int = 1 |
| 75 | const NX_LN_OK: nx_int = 0 |
| 76 | const NX_LN_ERR_BAD_DTYPE: nx_int = 1 |
| 77 | const NX_LN_ERR_BAD_NDIM: nx_int = 2 |
| 78 | const NX_LN_ERR_SHAPE_MISMATCH: nx_int = 3 |
| 79 | const NX_LN_ERR_NOT_CONTIGUOUS: nx_int = 4 |
| 80 | const NX_LN_N_VERDICTS: nx_int = 5 |
functions
| 82 | func nx_ln_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 97 | func nx_layernorm_forward(x: *NxTensor, gamma: *i64, beta: *i64, out: *NxTensor) -> nx_int |
| 171 | func nx_layernorm_gamma_unit(d: nx_int) -> *i64 |
| 185 | func nx_layernorm_beta_zero(d: nx_int) -> *i64 |
| 218 | func main() -> i64 |