Parallel Scan
A recurrence looks inherently sequential. If the update is associative it is not — the same result is reachable in log n dependent steps, which is what lets a recurrent model train on a GPU.
Standing
Current standardPart of the default recipe. Present in most frontier models trained today, and the thing a new design departs from rather than argues for.
The algorithm that makes every modern recurrent model trainable. Not new, not from machine learning, and the reason Mamba exists at all.
judged as of 2026-09 · what the labels mean
Theory
An RNN trains slowly because needs . That dependency looks like a law of nature. For a linear recurrence it is not.
with and computed from the input alone — not from . Every element of the state evolves independently, and the update is affine.
Composition
Two consecutive steps compose into a single step of the same shape.
Substituting confirms it: applying then gives , which is the composed pair applied once. And the operator is associative, because function composition always is.
That is the whole trick. Associativity means the operations can be grouped however you like, and a different grouping is a different schedule — a balanced binary tree instead of a chain.
Blelloch’s two sweeps
Up-sweep. Pair adjacent elements and compose, giving pairs each representing two steps. Repeat. After rounds the root holds the composition of the entire sequence. This is a reduction tree.
Down-sweep. Walk back down, pushing each node’s accumulated prefix into its children, so every leaf ends up holding the composition of everything to its left. Another rounds.
For : 24 dependent steps rather than 4096. The total arithmetic is about twice the sequential version, which is an excellent trade on hardware with thousands of idle cores.
Why every recurrent model since 2021 is linear
The scan requires eq. 1’s form. A inside the recurrence destroys associativity — does not collapse into one affine step — and no amount of engineering recovers it.
So the design question inverted. Rather than asking what recurrence is most expressive and accepting slow training, the field asked what can be trained in parallel and found expressiveness elsewhere: nonlinearities between layers, and input-dependent within them. Selective SSMs, RWKV and gated linear attention are all eq. 1 with different parameterisations of and , and they are all trained by this algorithm.
Implementation
import torch
from torch import Tensor
def scan(a: Tensor, b: Tensor) -> Tensor:
"""h_t = a_t * h_{t-1} + b_t, over dim 1. a, b: [B, T, D]."""
a, b = a.clone(), b.clone()
t, shift = a.size(1), 1
while shift < t:
# Compose each element with the one `shift` places behind it. After
# round k every element holds the composition of 2^k steps.
a_prev = torch.nn.functional.pad(a[:, :-shift], (0, 0, shift, 0), value=1.0)
b_prev = torch.nn.functional.pad(b[:, :-shift], (0, 0, shift, 0), value=0.0)
b = a * b_prev + b
a = a * a_prev
shift *= 2
return b
def scan_reference(a: Tensor, b: Tensor) -> Tensor:
"""The loop it replaces. Same answer, 4096 launches instead of 12."""
h = torch.zeros_like(b[:, 0])
return torch.stack([(h := a[:, t] * h + b[:, t]) for t in range(b.size(1))], 1)Hillis–Steele is work against Blelloch’s , and is nonetheless what most implementations use: it has half the depth, no down-sweep, and a memory access pattern the hardware likes. Asymptotically worse and reliably faster at the sizes that occur.
The part the papers do not put in the equations
The real kernels do not run the scan on the full sequence in one go. They tile it: load a chunk into SRAM, scan it there, carry one composed pair across the chunk boundary, and never write intermediate states to HBM. That is the same IO-aware argument as FlashAttention, and it is why Mamba’s paper spends as much space on the memory hierarchy as on the model — the algorithm was settled in 1980, and what was new was making it bandwidth-efficient.