nx_rope.nx
buildroot/runtime/nx_rope.nx
about
nx_rope.nx -- Rotary Position Embeddings (Su et al. 2021).
Position-encoding primitive for modern transformers. Replaces
sinusoidal-absolute (Vaswani 2017) and learned-absolute encoding
with a multiplicative rotation that injects RELATIVE position into
attention scores. RoPE has been adopted by every modern decoder-
only LLM:
Llama 1/2/3, Mistral, Mixtral, Qwen, Phi-3, Gemma
Z-Image / Flux / Stable Diffusion 3 attention
Mamba state-space hybrid layers
Per the no-skipping cardinal: composes the just-shipped
`nx_trig.nx` for cos/sin and `nx_root.nx` for inverse-frequency
precompute. No skipping, no inlining trig; full bits-up.
===== Math =======================================================
For a query/key vector q of dimension d (d must be even), pair
adjacent dimensions (2i, 2i+1). Each pair has its own frequency
theta_i = 1 / base^(2i/d) where base = 10000 (Su's published
recommendation).
For position m, the rotation angle for pair i is angle_{m,i} = m *
theta_i radians. The 2D rotation:
q'[2i] = q[2i] * cos(angle_{m,i}) - q[2i+1] * sin(angle_{m,i})
q'[2i+1] = q[2i] * sin(angle_{m,i}) + q[2i+1] * cos(angle_{m,i})
This is a 2x2 orthogonal rotation -- norm-preserving by
construction. When Q'K^T is computed downstream, the dot product
gains a position-dependent term that depends ONLY on the relative
position (m_q - m_k), not the absolute positions. That's the key
RoPE property.
===== Inverse-frequency precompute ==============================
theta_i = base^(-2i/d) for i in [0, d/2).
Computing base^(-2i/d) in i64 + Q10:
dependencies 5 imports · 3 importers
imports: nx_syscalls.nxnx_tier.nxnx_loop.nxnx_root.nxnx_trig.nx
imported by: nx_gguf_load_block.nxnx_gguf_load_block_test.nxnx_transformer_block.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 75 | const NX_ROPE_Q10: nx_int = 1024 |
| 76 | const NX_ROPE_Q14_ONE: nx_int = 16384 |
| 77 | const NX_ROPE_BASE: nx_int = 10000 |
| 81 | const NX_ROPE_OK: nx_int = 0 |
| 82 | const NX_ROPE_ERR_ODD_DIM: nx_int = 1 |
| 83 | const NX_ROPE_ERR_BAD_DIM: nx_int = 2 |
| 84 | const NX_ROPE_ERR_BAD_BASE: nx_int = 3 |
| 85 | const NX_ROPE_N_VERDICTS: nx_int = 4 |
functions
| 87 | func nx_rope_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 105 | func nx_rope_compute_inv_freq(d: nx_int, base: nx_int, out_inv_freq: *i64) -> nx_int |
| 139 | func nx_rope_apply_vector(x: *i64, d: nx_int, position: nx_int, |
| 172 | func nx_rope_apply_batch(x: *i64, n_tokens: nx_int, d: nx_int, |
| 203 | func main() -> i64 |