Position Encoding Comparison › Implementation Complexity

Michael BrenndoerferUpdated June 2, 20254 min read
Language AI HandbookAll chapters
Part 14 of 67Positional Encoding
Chapter 102Position Encoding Comparison › Implementation Complexity
Reading section 5 of 12 · Implementation Complexity · 4 minSection 5 of 12 · 4 min

Book contents

Section 5 of 12 · 4 min read

Position Encoding Comparison › Implementation ComplexityOne section · Sections in this chapter5 / 12 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Practical engineering considerations often drive architecture choices as much as theoretical properties. Let's examine the implementation complexity of each method.

Code Complexity Comparison

We'll implement the core logic of each method and compare their complexity.

In[16]:
Code
# Sinusoidal: Standalone function, applied once to input
def apply_sinusoidal(embeddings, pos_encodings):
    """Add pre-computed sinusoidal encodings to token embeddings."""
    seq_len = embeddings.shape[0]
    return embeddings + pos_encodings[:seq_len]


# Learned: Simple lookup and add
def apply_learned(embeddings, pos_embeddings):
    """Add learned position embeddings to token embeddings."""
    seq_len = embeddings.shape[0]
    return embeddings + pos_embeddings[:seq_len]


# RoPE: Requires modifying Q and K within attention
def apply_rope(q, k, cos, sin):
    """
    Apply rotary position embedding to queries and keys.
    Rotates pairs of dimensions by position-dependent angles.
    """
    # Split into pairs for rotation
    q_even, q_odd = q[..., 0::2], q[..., 1::2]
    k_even, k_odd = k[..., 0::2], k[..., 1::2]

    # Apply 2D rotation to each pair
    q_rotated = np.concatenate(
        [q_even * cos - q_odd * sin, q_even * sin + q_odd * cos], axis=-1
    )

    k_rotated = np.concatenate(
        [k_even * cos - k_odd * sin, k_even * sin + k_odd * cos], axis=-1
    )

    return q_rotated, k_rotated


# ALiBi: Requires modifying attention scores
def apply_alibi(attention_scores, alibi_bias):
    """Add ALiBi bias to attention scores before softmax."""
    return attention_scores + alibi_bias

Let's see these in the context of a complete attention forward pass.

In[17]:
Code
def attention_with_sinusoidal(X, W_q, W_k, W_v, pos_encodings):
    """Standard attention with sinusoidal position encoding."""
    # Position info added at input
    X_pos = X + pos_encodings[: X.shape[0]]

    Q = X_pos @ W_q
    K = X_pos @ W_k
    V = X_pos @ W_v

    scores = Q @ K.T / np.sqrt(K.shape[-1])
    weights = np.exp(scores - scores.max(axis=-1, keepdims=True))
    weights = weights / weights.sum(axis=-1, keepdims=True)

    return weights @ V


def attention_with_rope(X, W_q, W_k, W_v, cos, sin):
    """Attention with RoPE applied to Q and K."""
    Q = X @ W_q
    K = X @ W_k
    V = X @ W_v

    # RoPE modifies Q and K before score computation
    Q_rot, K_rot = apply_rope(Q, K, cos, sin)

    scores = Q_rot @ K_rot.T / np.sqrt(K.shape[-1])
    weights = np.exp(scores - scores.max(axis=-1, keepdims=True))
    weights = weights / weights.sum(axis=-1, keepdims=True)

    return weights @ V


def attention_with_alibi(X, W_q, W_k, W_v, alibi_bias):
    """Attention with ALiBi bias on scores."""
    Q = X @ W_q
    K = X @ W_k
    V = X @ W_v

    scores = Q @ K.T / np.sqrt(K.shape[-1])
    # ALiBi modifies scores before softmax
    scores = scores + alibi_bias

    weights = np.exp(scores - scores.max(axis=-1, keepdims=True))
    weights = weights / weights.sum(axis=-1, keepdims=True)

    return weights @ V
Implementation complexity comparison showing where each method injects positional information and what code modifications are required.
MethodWhere AppliedModifications NeededComplexity
SinusoidalInput embeddingsAdd to embeddings before attentionSimple
LearnedInput embeddingsAdd to embeddings before attentionSimple
Relative (Shaw)Attention scoresNew terms in QK computationComplex
RoPEQ and K vectorsRotate Q, K before score computationModerate
ALiBiAttention scoresAdd bias after score computationSimple

The key insight is where each method injects positional information:

  • Sinusoidal and Learned: Modify only the input, leaving the attention mechanism untouched
  • ALiBi: Adds a bias after scores are computed but before softmax
  • RoPE: Transforms Q and K before they interact
  • Relative (Shaw): Requires additional terms in the attention score computation

For engineering teams, simpler integration points make maintenance and debugging easier and simplify optimization. Methods that modify input embeddings work with any attention implementation. Methods that modify attention internals require careful coordination with optimizations like flash attention.