RoPE - Rotary Position Embeddings #
Implements Rotary Position Embeddings (RoPE) as used in LLaMA, GPT-NeoX, and BitNet.
Mathematical Definition #
RoPE encodes position information by rotating query/key vectors in complex space:
NeoX split-half style (used by BitNet b1.58):
For position m and dimension i (0 ≤ i < headDim/2):
θᵢ = m × base^(-2i/headDim) where base = 500000.0 for BitNet
Dimension pairs: (x[i], x[i + headDim/2])
Applied as:
x'[i] = x[i] × cos(θ) - x[i + headDim/2] × sin(θ)
x'[i + headDim/2] = x[i] × sin(θ) + x[i + headDim/2] × cos(θ)
Why RoPE? #
Traditional positional encoding (sinusoidal):
- Absolute positions → fixed vectors added to embeddings
- No relative position information in attention scores
RoPE advantages:
- Relative position encoding: Attention scores depend on relative positions
- Efficient: No learned parameters, just rotation
- Extrapolation: Can handle longer sequences than training length
- Mathematically elegant: Complex number rotation in frequency space
Attention Score Property #
For queries Q and keys K at positions m and n:
(RoPE(Q, m) · RoPE(K, n)) = f(Q, K, m-n)
The dot product depends only on relative position (m-n), not absolute positions!
Implementation Strategy #
Precomputed approach (llama.cpp):
1. Precompute cos(θ) and sin(θ) for all positions and dimensions
2. Store in lookup tables: cos_cache[pos][dim], sin_cache[pos][dim]
3. During forward pass: read from cache and apply rotation
On-the-fly computation (this implementation):
1. Compute θ = pos × base^(-2i/d) in shader
2. Compute cos(θ) and sin(θ) using GPU intrinsics
3. Apply rotation to adjacent dimension pairs
References #
- RoFormer paper: "RoFormer: Enhanced Transformer with Rotary Position Embedding" (Su et al., 2021)
- LLaMA: https://github.com/facebookresearch/llama (rope_forward)
- llama.cpp: ggml/src/ggml.c (ggml_rope_impl)
Layer Configuration #
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
Helper Functions #
Compute rotation angle θ for position and dimension pair θᵢ = pos × base^(-2i/d)
Equations
- One or more equations did not get rendered due to their size.
Instances For
GPU Kernel Implementation #
RoPE kernel: Apply rotary embeddings to query or key tensor
Input shape: [batch, seq_len, num_heads, head_dim] Output shape: [batch, seq_len, num_heads, head_dim]
Algorithm:
for each position pos in sequence:
for each dimension pair (2i, 2i+1):
θ = pos × base^(-2i/d)
x'[2i] = x[2i] × cos(θ) - x[2i+1] × sin(θ)
x'[2i+1] = x[2i] × sin(θ) + x[2i+1] × cos(θ)
Workgroup strategy: Each thread handles one dimension pair for one token
@param config RoPE configuration @param batchSize Batch size @param seqLen Current sequence length @param numHeads Number of attention heads
Equations
- One or more equations did not get rendered due to their size.
Instances For
RoPE kernel with dynamic posOffset from params buffer. Produces identical WGSL regardless of position, enabling pipeline caching. Params buffer layout: [posOffset: u32] (4 bytes minimum, but shares attention params buffer) For single-token inference: batchSize=1, seqLen=1, reads posOffset from params[0].
Equations
- One or more equations did not get rendered due to their size.
Instances For
Optimized: Cached RoPE (Future) #
Precomputed RoPE: Use cached cos/sin values
This is more efficient for long sequences where we repeatedly apply RoPE to the same positions. Common in inference with KV caching.
Approach:
- Precompute cos(θ) and sin(θ) for all positions and dimensions
- Store in buffers: cos_cache[max_seq_len][head_dim/2], sin_cache[...]
- During forward: lookup instead of compute
@param config RoPE configuration @param batchSize Batch size @param seqLen Current sequence length @param numHeads Number of attention heads
Equations
- One or more equations did not get rendered due to their size.
Instances For
High-Level API #
Create RoPE layer (no learned parameters)
@param config RoPE configuration
Equations
- One or more equations did not get rendered due to their size.
Instances For
Apply RoPE to query or key tensor
@param device WebGPU device @param layer RoPE layer @param inputBuf GPU buffer [batch, seq_len, num_heads, head_dim] @param outputBuf GPU buffer for output (same shape) @param batchSize Batch size @param seqLen Current sequence length @param numHeads Number of attention heads
Equations
- One or more equations did not get rendered due to their size.
Instances For
Apply RoPE with dynamic posOffset from a params buffer. The params buffer must contain [posOffset: u32, ...] (posOffset at index 0). Produces identical WGSL across tokens → enables pipeline + bind group caching.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Apply cached RoPE (requires precomputed cos/sin buffers)
@param device WebGPU device @param layer RoPE layer @param inputBuf GPU buffer [batch, seq_len, num_heads, head_dim] @param cosCacheBuf Precomputed cos values [max_seq_len, head_dim/2] @param sinCacheBuf Precomputed sin values [max_seq_len, head_dim/2] @param outputBuf GPU buffer for output @param batchSize Batch size @param seqLen Current sequence length @param numHeads Number of attention heads
Equations
- One or more equations did not get rendered due to their size.
Instances For
Cache Generation Utilities #
Generate cos/sin cache on CPU for later GPU upload
This is typically done once during model initialization.
@param config RoPE configuration @param numHeads Number of attention heads @return (cos_cache, sin_cache) as ByteArrays
Equations
- One or more equations did not get rendered due to their size.