Complex positional encodings#
The native complextorch.nn.Transformer and
complextorch.nn.MultiheadAttention (see
Complex transformer & attention masking) apply no
positional encoding on their own — attention is permutation-equivariant, so
position has to be injected explicitly. complextorch.nn ships three
complex-valued schemes.
Module |
Type |
Position |
Mechanism |
|---|---|---|---|
|
relative |
rotary (RoPE) |
multiply Q/K by \(e^{j\omega_k n}\) |
|
absolute |
additive |
add \(e^{j\omega_k n}\) |
|
absolute |
learnable |
multiply by \(e^{j(\omega_k n + \phi_k)}\) |
Rotary embeddings (RoPE) are complex by construction#
RoPE encodes position by rotating each feature channel by an angle proportional to its position. Since this library’s tensors are already complex, a rotation is literally a multiplication by a unit phasor:
The attention score uses the Hermitian inner product \(Q\,K^H\), so the rotation applied at query position \(m\) and key position \(n\) leaves a residual phase that depends only on the relative offset \(m-n\):
Apply it inside attention via the rotary argument (it is applied to the
per-head query/key tensors after projection, so build it with dim=d_k):
import torch
import complextorch as ctorch
d_model, n_heads, d_head = 32, 4, 8
rope = ctorch.nn.RotaryEmbedding(dim=d_head)
mha = ctorch.nn.MultiheadAttention(n_heads, d_model, d_head, d_head, rotary=rope)
x = torch.randn(2, 16, d_model, dtype=torch.cfloat) # (batch, length, d_model)
y = mha(x, x, x)
print(y.shape, y.dtype)
Absolute encodings#
complextorch.nn.SinusoidalPositionalEncoding adds a fixed complex
sinusoidal phasor bank to the embeddings, and complextorch.nn.CoPE is a
lightweight learnable variant (per-channel learnable frequency and phase,
2·dim parameters). The complextorch.models.ViT exposes all three via
its pos_encoding= argument ("learned", "sinusoidal", "rotary").