TAI BUI
Back to curriculum
BuildPython

Positional Encoding — Sinusoidal, RoPE, ALiBi

Attention is permutation-invariant: "The cat sat on the mat" and "mat the on sat cat the" give the same output without a positional signal. Three algorithms fix that, each with a different bet on what "position" means.

TIME: ~45 minutesPREREQUISITES: Phase 7 · 02 (Self-Attention), Phase 7 · 03 (Multi-Head Attention)

Learning Objectives

  • Explain why self-attention is permutation-invariant and why that makes a position signal necessary.
  • Compute sinusoidal encodings and RoPE rotations in pure Python, and verify numerically that RoPE scores depend only on the gap between positions.
  • Build ALiBi slopes and the distance-penalty bias matrix, and say how a score-level bias differs from embedding-based schemes.
  • Choose between sinusoidal, RoPE, and ALiBi for a given context length and training budget, and name the main RoPE scaling methods.

The Problem

Positional encoding is any signal added to the embeddings or to the attention scores that tells the model where each token sits in the sequence. Attention needs one because it has no sense of order on its own.

The attention matrix softmax(Q K^T / √d) V is built from pairwise similarities. Shuffle the rows of X and the rows of the output get shuffled the same way. Nothing inside attention looks at position.

For a bag-of-words model that's fine. For language, code, audio, and video, where order carries meaning, it breaks the model.

The fix is to inject position into the embeddings somehow. Three eras of answers:

  1. Absolute sinusoidal (Vaswani 2017). Add sin/cos of position to the embedding. Simple, no learned parameters, extrapolates poorly beyond trained lengths.
  2. RoPE (Rotary Position Embeddings) (Su 2021). Rotate Q and K vectors by an angle proportional to position. Encodes relative position directly in the dot product. Dominant in 2026.
  3. ALiBi (Attention with Linear Biases) (Press 2022). Skip embeddings and add a per-head linear penalty to attention scores based on distance. Excellent length extrapolation.

As of 2026, most frontier open models use RoPE: Llama 2/3/4, Qwen 2/3, Mistral, Mixtral, DeepSeek-V3, Kimi. A handful of long-context models use ALiBi or its modern variants. Absolute sinusoidal is mostly historical.

Warm-up0/3
1. What range of values can sin(x) and cos(x) take for any real number x?Warm-up
A small table with the inputs 0.5, 3, -7, 40 and 1000 across the top and two rows, sin(x) and cos(x), whose output cells are all question marks.
Inputs typed for illustration; outputs left as ?.
2. Which operation changes a vector's direction while leaving its length unchanged?Warm-up
A grid with an arrow v from the origin to (3, 4), with its length 5 and angle 53.1 degrees noted, beside a note that after some operation the new vector has a different direction and the same length of 5.
Typed example; the operation itself is not drawn.
3. In a decoder-only language model, what does the causal mask do to attention scores?Warm-up
A 4 by 4 grid of attention scores with rows and columns labelled The, cat, sat, down, followed by a chain: scores, then a box reading causal mask with a question mark, then softmax.
Scores are typed, illustrative numbers (random.Random(5)), not model output.
Check: The Problem0/3
1. What is a positional encoding?Define
Four tokens (The, cat, sat, down) at positions 0 to 3, each beside a row of four numbers taken from the sinusoidal_pe function of the lesson's main.py.
Row values printed by the lesson's main.py (first 4 dims); the words are typed.
2. Which positional scheme do most frontier open models (Llama, Qwen, Mistral, DeepSeek-V3) use as of 2026?Define
Four boxes naming Llama, Qwen, Mistral and DeepSeek-V3, each reading position scheme with a question mark.
Model families named in the stem.
3. Self-attention scores each token against every other token by pairwise similarity. If you reorder the input tokens and add no position signal, what happens to the output?Explain
Two rows through an attention layer with no position signal. Order 1 takes tokens A, B, C and gives out(A), out(B), out(C). Order 2 takes C, A, B and gives three question marks.
Tokens A, B, C are labels, not real words.

The Concept

Sinusoidal absolute vs RoPE rotations vs ALiBi distance bias

Absolute sinusoidal

Absolute sinusoidal encoding is a fixed matrix PE of shape (max_len, d_model), computed once, whose row pos gets added to the embedding at that position:

PE[pos, 2i]   = sin(pos / 10000^(2i / d_model))
PE[pos, 2i+1] = cos(pos / 10000^(2i / d_model))

You add it as X' = X + PE[:N] before attention. Each dimension is a sinusoid at a different frequency, and the model learns to read position from the phase pattern. It breaks past max_len: nothing told the model what happens at position 2048 when it only saw positions 0–2047.

RoPE

RoPE rotates the Q and K vectors instead of touching the embeddings. For each pair of dimensions (2i, 2i+1), the rotation angle is the position times a per-pair frequency:

[q'_2i    ]   [ cos(pos·θ_i)  -sin(pos·θ_i) ] [q_2i   ]
[q'_2i+1  ] = [ sin(pos·θ_i)   cos(pos·θ_i) ] [q_2i+1 ]

θ_i = base^(-2i / d_head),  base = 10000 by default

Apply the same rotation to keys at position pos_k. The dot product q'_m · k'_n then depends on (m - n) alone. The attention score reflects relative distance even though the rotation was keyed off absolute positions. Beautiful trick.

You can rescale base (NTK-aware, YaRN, LongRoPE) to stretch RoPE to longer contexts. Plain base rescaling often works with little or no fine-tuning, while YaRN and LongRoPE usually add a short fine-tuning stage for the best quality. Llama 3.1 went from 8K to 128K context with a larger base and continued long-context training. These scaling schemes are described in prose only: main.py has no NTK-aware, YaRN, or LongRoPE code.

ALiBi

ALiBi skips the embedding trick and biases the attention scores directly:

attn_score[i, j] = (q_i · k_j) / √d  -  m_h · |i - j|

m_h is a head-specific slope (e.g. 1 / 2^(8·h/H)). Closer tokens get boosted and far tokens get penalized. It adds no training-time cost. The paper reports that length extrapolation beats sinusoidal and matches RoPE at its original trained length.

What to pick in 2026

VariantExtrapolationTraining costUsed by
Absolute sinusoidalpoorfreeoriginal transformer, early BERT
Learned absolutenonetinyGPT-2, GPT-3
RoPEgood with scalingfreeLlama 2/3/4, Qwen 2/3, Mistral, DeepSeek-V3, Kimi
RoPE + YaRNexcellentfine-tune stageQwen2/2.5 long-context configs
ALiBiexcellentfreeBLOOM, MPT, Baichuan

RoPE won because it slots into attention without changing the architecture, encodes relative position, and its base hyperparameter gives a clean knob for long-context fine-tuning.

Check: The Concept0/12
1. Which pair of formulas is the sinusoidal encoding for dimension pairs (2i, 2i+1)?Define
An empty matrix grid with rows for positions 0 to 2 and columns 0 to 5 grouped into pairs i = 0, 1, 2, where every cell holds a question mark.
Layout only; the cell contents are what the question asks for.
2. What property does RoPE give the attention score q'_m · k'_n?Define
Two rows of boxes: q at position m and k at position n each pass through a rotation by an angle from their position, giving q'_m and k'_n, which feed a dot product box reading q'_m dot k'_n equals a question mark.
Schematic of the setup in the stem.
3. In the ALiBi formula `attn_score[i,j] = (q_i · k_j)/√d - m_h · |i-j|`, what does m_h represent?Define
The ALiBi score formula above a row of five keys j = 0 to 4 for query i = 4, each labelled with its distance |i - j| from 4 down to 0, and a box reading m_h equals a question mark.
Query index and key row typed for illustration.
4. In RoPE, what is the `base` in theta_i = base^(-2i / d_head)?Define
The RoPE angle formula theta_i equals base to the power minus 2i over d_head with base 10000, and a one-row table of theta for i = 0 to 3 with d_head = 8: 1, 0.1, 0.01, 0.001.
d_head = 8 is illustrative; values computed from the formula.
5. What does extrapolation mean for a positional scheme?Define
A card describing a sinusoidal scheme with max_len 2048 and three test prompts of 512, 1500 and 4096 tokens, each with a question mark for whether the output is correct.
Prompt lengths are illustrative.
6. What training-time cost does the lesson attribute to ALiBi?Define
A one-row table for ALiBi with the extrapolation and training cost cells set to question marks and the used-by cell reading BLOOM, MPT, Baichuan.
Row layout from the lesson's table; two cells hidden.
7. Why does absolute sinusoidal encoding break beyond max_len?Explain
A position bar from 0 to 4095 with positions 0 to 2047 filled and 2048 onward dashed, above a box reading token at position 2048, model output equals a question mark.
Bar scale is illustrative; the 2047/2048 boundary is the lesson's example.
8. Q at position m and K at position n are each rotated by their own absolute angle. Why does the score depend only on m - n?Explain
Two rows of boxes: a q pair at position m rotated by m times theta_i, and a k pair at position n rotated by n times theta_i, both feeding a dot product box q' dot k' equals a question mark.
Schematic of the setup in the stem.
9. Why does the lesson say RoPE won as the dominant 2026 choice?Explain
Six chips naming Llama 2/3/4, Qwen 2/3, Mistral, Mixtral, DeepSeek-V3 and Kimi above a box stating RoPE is the dominant 2026 choice and asking why it won.
Model list copied from the lesson.
10. How does ALiBi differ from sinusoidal encoding and RoPE in where it injects position?Explain
A pipeline of five boxes: token embeddings, Q K V projections, scores QK^T over root d, softmax, and weights times V, with a note that a positional scheme injects position somewhere along it.
Generic attention path; no scheme is placed on it.
11. Two models are trained on 2K-token sequences and tested at 8K. The sinusoidal model degrades sharply while the ALiBi model degrades gently. Which explanation fits?Analyze
A length bar showing training up to 2K and test lengths up to 8K, with two boxes: a sinusoidal model that degrades sharply and an ALiBi model that degrades gently.
Setup from the stem; no measurements are drawn.
12. You are training a new model from scratch. It must run well on inputs far longer than any training example, and you cannot afford a long-context fine-tuning stage. Which choice fits the lesson's table best?Design
Three requirement boxes: trained from scratch, inputs far longer than any training example, and no long-context fine-tuning stage, with a question about which scheme fits.
Requirements restated from the stem.

Build It

Step 1: sinusoidal encoding

The full version is in code/main.py, where it is called sinusoidal_pe. The core is a short double loop over positions and dimension pairs:

python
def sinusoidal_pe(n, d, base=10000.0):
    pe = [[0.0] * d for _ in range(n)]
    for pos in range(n):
        for i in range(d // 2):
            theta = pos / (base ** (2 * i / d))
            pe[pos][2 * i]     = math.sin(theta)
            pe[pos][2 * i + 1] = math.cos(theta)
    return pe

Add the result to the embedding matrix before the first attention layer.

Step 2: RoPE applied to Q, K

RoPE rotates Q and K. apply_rope copies its input and returns a new rotated list, leaving the original vector untouched. For each pair of dims:

python
def apply_rope(x, pos, base=10000.0):
    d = len(x)
    out = list(x)
    for i in range(d // 2):
        theta = pos / (base ** (2 * i / d))
        c, s = math.cos(theta), math.sin(theta)
        a, b = x[2 * i], x[2 * i + 1]
        out[2 * i]     = a * c - b * s
        out[2 * i + 1] = a * s + b * c
    return out

Apply the same function to Q at position m and K at position n. Each coordinate pair of Q is rotated by m·θ_i and of K by n·θ_i, so their dot product depends only on the gap m - n, and attention gets relative position for free.

Step 3: ALiBi slopes and bias

main.py splits this into two functions: one for the per-head slopes, one for the bias matrices.

python
def alibi_slopes(n_heads):
    # slope_h = 2 ** (-8 * h / n_heads) for h = 1..n_heads
    return [2 ** (-8 * (h + 1) / n_heads) for h in range(n_heads)]
 
def alibi_bias(n_heads, seq_len, causal=True):
    out = []
    for m in alibi_slopes(n_heads):
        head_bias = []
        for i in range(seq_len):
            row = []
            for j in range(seq_len):
                if causal and j > i:
                    row.append(float("-inf"))
                else:
                    row.append(-m * abs(i - j))
            head_bias.append(row)
        out.append(head_bias)
    return out  # add to attention scores before softmax

Add out[h] to the (seq_len, seq_len) attention score matrix of head h, then softmax. With causal=True (the default), future positions j > i get -inf so softmax gives them zero weight. The demo in main.py passes causal=False so you can see the full symmetric penalty matrix.

Step 4: verify the relative-distance property of RoPE

Pick two random vectors a, b. Rotate them by (pos_a, pos_b), then by (pos_a + k, pos_b + k). The two dot products must match within floating-point error. That property is what RoPE is built around: the score ignores the absolute offset, and only the relative gap matters.

Check: Build It0/7
1. What is row 0 of `sinusoidal_pe(3, 4)`?Apply
A 3 by 4 grid of question marks for the output of sinusoidal_pe(3, 4), with columns marked sin, cos, sin, cos for indices 0 to 3 and row 0 highlighted.
Layout only; the cell values are what the question asks for.
2. `alibi_slopes(4)` uses `2 ** (-8 * (h + 1) / n_heads)`. What does it return?Apply
The ALiBi slope formula 2 to the power of -8 times h plus 1 over n_heads, above four boxes for heads 0 to 3 each reading slope equals a question mark.
Layout only; the slopes are what the question asks for.
3. With default arguments, what is `alibi_bias(8, 3)[0][0][2]`, the bias for query i = 0 and key j = 2 in head 0?Apply
The call alibi_bias(8, 3) above a 3 by 3 grid of question marks for head 0, rows i and columns j, with the cell at i = 0, j = 2 highlighted.
Layout only; the entries are what the question asks for.
4. What is `alibi_bias(8, 5, causal=False)[1][4][1]`, the entry for head 1, query i = 4, key j = 1?Apply
A note that head 1 of 8 is used, above a 5 by 5 grid of rows i and columns j in which only the cell at i = 4, j = 1 is highlighted with a question mark.
Layout only; the entry is what the question asks for.
5. With `apply_rope` applied to Q at position m and K at position n, which pair gives the same score as (m, n) = (12, 15)?Apply
A position axis from 0 to 45 with a query dot at 12 and a key dot at 15, and the line score(m, n) equals the dot product of apply_rope(q, m) and apply_rope(k, n).
Positions from the stem.
6. You port RoPE code and the score for positions (3, 5) differs from the score for (1024, 1026), though both gaps are 2. What is the most likely cause?Analyze
A terminal-style table with two rows, positions (3, 5) and (1024, 1026), both gap 2, showing two clearly different scores.
Scores come from an altered rotation run for this picture, not the lesson's main.py (q, k from random.Random(0), d = 16).
7. A decoder trains with ALiBi but calls `alibi_bias(n_heads, seq_len, causal=False)`. What goes wrong?Analyze
The call alibi_bias(n_heads, seq_len, causal=False) above four tokens t0 to t3 in a row, with a line asking what the bias entries for keys t0 to t3 become for query t1.
Layout only; the entries are what the question asks for.

Use It

Core PyTorch has no RoPE function. Libraries such as torchtune and Hugging Face transformers ship their own implementations, and flash_attn includes a fused rotary kernel, so production code rarely hand-rolls the rotation. The scaling schemes below are described in prose only; main.py implements none of them.

python
from transformers import AutoModel
model = AutoModel.from_pretrained("meta-llama/Llama-3.2-3B")
# model.config.rope_scaling → {"rope_type": "llama3", "factor": 32.0, "low_freq_factor": 1.0, "high_freq_factor": 4.0, "original_max_position_embeddings": 8192}

Llama 3.2 uses Meta's own llama3 scaling variant, not YaRN, but rope_scaling is where you check which scheme any checkpoint uses.

Long-context tricks in 2026:

  • NTK-aware interpolation. Rescale base to base * (scale_factor)^(d/(d-2)) when extending from 4K to 16K+.
  • YaRN. A smarter interpolation that preserves attention entropy on long contexts. Qwen2's long-context configs use it.
  • LongRoPE. Microsoft's 2024 method, which uses evolutionary search to pick per-dimension scale factors. Phi-3-Long uses it.
  • Position interpolation + fine-tuning. Shrink positions by the extension factor and fine-tune for 1–5B tokens. Surprisingly effective.
Check: Use It0/6
1. How does NTK-aware interpolation extend RoPE's context length?Define
A position bar from 0 to 16K+ with the first 4K shaded as trained and the rest dashed as unseen, above a box with the RoPE angle formula and a question about what is changed.
The 4K to 16K+ extension is the lesson's example.
2. What does YaRN do differently from simple linear interpolation?Define
A position bar from 0 to 16K with the first 4K marked as the trained range and an arrow from 16K back toward 4K labelled every position p becomes p divided by 4, above a box asking what YaRN does differently.
Factor 4 follows the lesson's 4K to 16K example.
3. What method does LongRoPE use to pick per-dimension scale factors?Define
Four boxes for dimension pairs 0 to 3, each with its own scale factor s_i set to a question mark, and a note that LongRoPE is Microsoft's 2024 method used by Phi-3-Long.
Four pairs shown for illustration.
4. What does the lesson say about the position interpolation plus fine-tuning approach to extending context?Define
A flow of four boxes: pretrained RoPE model, step 1 with a question mark, step 2 with a question mark, and longer context.
Flow shape only; the steps are what the question asks for.
5. An engineer scores one position pair at bases 10000, 100000 and 1,000,000, sees the scores change, and concludes that raising base makes a RoPE model handle long contexts. What is wrong with this conclusion?Analyze
A terminal-style listing of the RoPE score for positions 4096 and 4098 at bases 10000, 100000 and 1000000 (1.331200, 1.368584, 1.388781), above the engineer's conclusion.
Scores printed by demo_rope_base_scaling in the lesson's main.py (seed 1, d = 8).
6. A RoPE model trained at 4K context must serve 32K, and you have almost no fine-tuning budget. What is the sensible first step?Design
A position bar from 0 to 32K with the first 4K shaded as trained, beside two boxes: fine-tuning budget almost none, and first step with a question mark.
Setup from the stem.

Ship It

The skill in outputs/skill-positional-encoding-picker.md picks an encoding strategy for a new model given target context length, extrapolation needs, and training budget.

Check: Ship It0/1
1. What does this lesson's Ship It skill, positional-encoding-picker, do?Define
A box for the positional-encoding-picker skill file, outputs/skill-positional-encoding-picker.md, with an arrow in from a question-marked inputs box and an arrow out to a question-marked output box.
File name from the lesson's Ship It section.
Author's Note

I'd start with RoPE for a new model and want a concrete reason before picking anything else. ALiBi is the one I'd weigh against it when extrapolation past the trained length matters most, since the comparison table rates it excellent there at no training cost.

My bet is that the habit worth building is running the relative-distance check from Step 4 on RoPE code you write or port. main.py prints the score for four position pairs that all have a gap of 2, from (3, 5) out to (1024, 1026), and they should agree to floating-point error. If those rows disagree, something is off in the rotation.

I'd also read the base-scaling demo in main.py as a sketch, not a recipe. It scores one position pair, (4096, 4098), at three bases (10000, 100000, 1,000,000), and its comment says a larger base means slower rotation. That isn't the NTK-aware formula from Use It, and it isn't a long-context test. For real extension I'd measure perplexity before and after, as Exercise 2 does.

Exercises

  1. Easy. Plot the sinusoidal PE matrix as a heatmap for max_len=512, d=128. Confirm the "stripes get wider as dimension index grows" pattern.
  2. Medium. Implement NTK-aware RoPE scaling. Train a tiny LM on sequences of length 256, then test on length 1024 with and without scaling. Measure perplexity.
  3. Hard. Implement ALiBi and RoPE in the same attention module. Train a 4-layer transformer on a copy task with sequences of length 512, extrapolate to 2048 at test time, and compare degradation.

Key Terms

TermWhat people sayWhat it actually means
Positional encoding"Tells attention about order"Any signal added to embeddings or attention that encodes position.
Sinusoidal"The original one"sin/cos at geometric frequencies added to embeddings; extrapolates poorly.
RoPE"Rotary embeddings"Rotate Q, K by position-dependent angle; dot product encodes relative distance.
ALiBi"Linear bias trick"Add -m·|i-j| to attention scores; no embedding needed, extrapolates well.
base"RoPE's knob"The frequency scaler in RoPE; increase to extend context at inference.
NTK-aware"A RoPE scaling trick"Rescale base so high-frequency dims aren't squeezed when context expands.
YaRN"The fancy one"Per-dimension interpolation+extrapolation that preserves attention entropy.
Extrapolation"Works beyond trained length"Can the position scheme serve correct output past max_len seen in training?

Further Reading

Code

Ships With