nx_gelu.nx
buildroot/runtime/nx_gelu.nx
about
nx_gelu.nx -- Gaussian Error Linear Unit activation.
The activation used by every non-Llama transformer family:
GPT-2 / GPT-3 / GPT-Neo / GPT-J
BERT / RoBERTa / DistilBERT / ALBERT
T5 / mT5 / ByT5
ViT / DeiT / Swin / CLIP / SigLIP
PaLM, Gemma (uses GeGLU variant = GELU * linear gate)
Llama / Mistral / Qwen use SiLU instead (closely related;
nx_silu.nx already ships). Substrate now ships both -- model
loader picks per-config.
===== Math =======================================================
Exact GELU (Hendrycks & Gimpel 2016 eq. 1):
GELU(x) = x * Phi(x)
= x * 0.5 * (1 + erf(x / sqrt(2)))
Exact requires erf, which we don't ship. Two practical
approximations:
1. Tanh form (most common):
GELU(x) ~= 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715*x^3)))
2. Sigmoid form (Hendrycks 2016 alt):
GELU(x) ~= x * sigmoid(1.702 * x)
We use form #2 because nx_sigmoid_q10 already ships in nx_silu.nx
and it's a one-multiply + sigmoid composition. Max relative error
vs exact GELU is ~0.5% across [-5, 5] -- well within substrate
quantization budget.
Tanh-form upgrade queued -- needs nx_tanh.nx primitive. For
q4_K-quant transformer inference the sigmoid form is more than
sufficient.
Q-format: x in Q10, output in Q10. Constant 1.702 in Q10 = 1743.
dependencies 5 imports · 0 importers
imports: nx_syscalls.nxnx_tier.nxnx_loop.nxnx_tensor.nxnx_silu.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
| 61 | const NX_MAGIC_1024: i64 = 1024 |
| 62 | const NX_MAGIC_10240: i64 = 10240 |
| 63 | const NX_MAGIC_2048: i64 = 2048 |
| 65 | const NX_GELU_Q10: nx_int = 1024 |
| 66 | const NX_GELU_K_1_702_Q10: nx_int = 1743 // 1.702 in Q10 = 1.702 * 1024 = 1742.85 |
| 70 | const NX_GELU_OK: nx_int = 0 |
| 71 | const NX_GELU_ERR_BAD_DTYPE: nx_int = 1 |
| 72 | const NX_GELU_ERR_SHAPE_MISMATCH: nx_int = 2 |
| 73 | const NX_GELU_ERR_NOT_CONTIGUOUS: nx_int = 3 |
| 74 | const NX_GELU_N_VERDICTS: nx_int = 4 |
functions
| 76 | func nx_gelu_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 89 | func nx_gelu_q10(x_q10: nx_int) -> nx_int |
| 99 | func nx_gelu_forward(x: *NxTensor, out: *NxTensor) -> nx_int |
| 147 | func main() -> i64 |