A small state has to choose what survives
Suppose a sequence contains a few useful signals separated by distractions. Keeping every earlier token makes later retrieval straightforward, but the stored history grows with the sequence. Keeping only a small array makes storage predictable. The harder question is what that array should retain when the next input arrives.
Mamba makes the update depend on the input. A token can change how much old state survives, what gets written and how the state is read. The paper motivates this with selective copying, where useful symbols appear at variable distances. The mechanism must respond to content as well as position. Paper v2, §3.1
Start with one input channel and three state coordinates. At position t, the update has two contributions: retained memory from the previous position and a write from the current input. A readout turns the updated array into an output.
The explorer below supplies incoming memory [0.2, −0.1, 0.3] before position zero. Step through the six inputs and inspect the retained and written terms coordinate by coordinate. Switch to fixed coefficients to keep the same inputs while changing the update rule.
Update a three-coordinate state
Select a position to inspect what survives, what is written and what is read.
Memory takes two paths into the next state
Input x[0] = 1.000
Complete updated memory at t0
STATE COORDINATE 0
0.188
0.147
Teal carries memory; amber writes the current input. The separate D × x branch bypasses memory. Bars share a fixed −0.75…+0.75 scale; violet is negative and teal is positive. Retention bars use a separate 0…1 scale.
Position 0: retained state plus new input produces the three updated coordinates.
Inspect exact values and equations
y[t] = Σ C[t,n] h[t,n] + D x[t]
t = 0; x = 1.000; Δ = 0.150; token-independent A = [-0.4, -0.9, -1.4] (learned in a model, authored here).
Incoming initial memory h[−1] (nonzero)
| coordinate | n = 0 | n = 1 | n = 2 |
|---|---|---|---|
| h[−1] | 0.200 | -0.100 | 0.300 |
Retain + write → updated state [N = 3]
| coordinate | previous | retention | retained | write | state |
|---|---|---|---|---|---|
| n = 0 | 0.200 | 0.942 | 0.188 | 0.105 | 0.293 |
| n = 1 | -0.100 | 0.874 | -0.087 | -0.030 | -0.117 |
| n = 2 | 0.300 | 0.811 | 0.243 | 0.045 | 0.288 |
Input and output maps
| coordinate | B[t,n] | C[t,n] | C × state |
|---|---|---|---|
| n = 0 | 0.700 | 0.500 | 0.147 |
| n = 1 | -0.200 | 0.300 | -0.035 |
| n = 2 | 0.300 | -0.200 | -0.058 |
Same-input fixed-coefficient y = 0.162
The two paths in the figure give the state update and readout equations:
These are calculated values from authored coefficient presets. The changing coefficients illustrate selection; they are not learned saliency, pretrained activations or a successful trained copy task. In particular, a small retention factor does not automatically mean “ignore this token”: it suppresses earlier memory, while the separate write can still add new information. Understanding that interaction requires the parameterization behind a and w.
The input changes the coefficients, while A stays learned
For the selected real-valued diagonal implementation, Mamba uses a learned negative continuous parameter A and an input-dependent positive time scale delta. The transition to the next discrete state is:
Here c indexes expanded channels and n indexes state coordinates. With batch b, sequence length T, expanded width E and state size N, the current state is [b, E, N]. The scan input and output are [b, T, E]. A has shape [E, N] and does not change with the token. Delta has shape [b, T, E]. In the pinned mamba_simple block, B and C have shape [b, T, N] and are shared across the expanded channels. The projection weights are learned and shared over positions; their outputs depend on the current locally mixed input. Algorithm 2 and §3.6, pinned implementation
Because A is negative and delta is positive, each retention lies between zero and one. A larger delta advances further along these decaying dynamics. It also scales the write. Delta is a time scale, not a probability gate, and B and C can contain signed values. The update is therefore not generally a convex average of old state and current input.
There is a discretization detail worth keeping explicit. The paper introduces exact zero-order hold for the continuous system, whose input map includes an integral:
The official readable selective_scan_ref instead uses delta × B × x for input injection while retaining exp(delta × A) for the transition. The runnable reference and every numeric figure on this page follow that code. Calling this write term the exact zero-order-hold integral would describe a different computation. Paper Equation 4, official reference recurrence
With fixed discrete coefficients, unrolling a state-space model gives a fixed global convolution kernel. Once the coefficients change with the token, the influence of an earlier input depends on the intervening inputs too. The same stationary kernel no longer describes every position. The recurrence still works, but computing it efficiently now needs a different organization.
Compose the updates instead of waiting for every state
If every update waits for the preceding state, training appears sequential across T positions. But once the inputs and their coefficients are available, each update is a known affine map. Combining two consecutive maps gives another affine map:
Composition is associative, so neighboring intervals can combine in parallel while preserving chronological order. It is not commutative: swapping updates generally changes which information is retained. Select a coordinate and a prefix in the figure to inspect the composed pair and compare its state with sequential evaluation, including the incoming memory.
Compose affine updates
Each token supplies h ↦ a × h + b. Every round reads one snapshot of the preceding round.
Six leaves → parallel prefix network
Selected interval 5…5, coordinate 0
a = a₂a₁; b = b₂ + a₂b₁. Both operands come from the preceding snapshot.
Complete prefix at t5
Sequential recurrence at the same position
Cell values are exported float64 values, including last-bit differences. Teal marks the composed pair and its two parents; amber marks covered token leaves. Network rounds explain algebra, not GPU latency.
Inspect exact values and equations
Round 0: original token pairs · n = 0
| coordinate | a | b |
|---|---|---|
| ends at t = 0 | 0.942 | 0.105 |
| ends at t = 1 | 0.726 | -0.040 |
| ends at t = 2 | 0.923 | 0.360 |
| ends at t = 3 | 0.787 | -0.045 |
| ends at t = 4 | 0.961 | -0.020 |
| ends at t = 5 | 0.819 | 0.450 |
This pair is the original single-token update.
Selected pair covers positions 5…5. Only after the last round does every pair represent its complete prefix.
Complete-prefix state versus sequential state
| coordinate | prefix | sequential | difference |
|---|---|---|---|
| n = 0 | 0.720 | 0.720 | 0 |
| n = 1 | 0.062 | 0.062 | 0 |
| n = 2 | -0.088 | -0.088 | 0 |
Complete prefix applies a × h[−1] + b, including the nonzero initial state h[−1] = [0.2, -0.1, 0.3].
Round 0, stride 0: inspect the pair ending at 5.
The browser and CPU prefix example expose the algebra. Their snapshot-based Hillis–Steele rounds have logarithmic dependency depth and O(T log T) pair work. They also materialize intermediate arrays for inspection. That is different from claiming they reproduce a work-efficient GPU scan.
The paper’s hardware contribution combines parallel scan with fusion and recomputation. Rather than write a full [b, T, E, N] expanded state tensor through high-bandwidth device memory between operations, the fused computation constructs and consumes states in faster on-chip storage and recomputes needed intermediates during backward. This reduces memory traffic; it does not make all training activations constant-size. Paper §3.3 and Appendix D
Training can regroup known updates this way. Autoregressive decoding still waits for the newly generated token before it can form that token’s coefficients. Before following that decode path, we need to place the selective operator inside the complete neural-network block.
The selective operator is one part of the Mamba block
The block first projects each model-width input into two expanded branches. One branch provides x; a short causal depthwise convolution and SiLU activation add local context. From that convolved x, smaller projections produce B, C and a low-rank representation that expands into delta. The selective scan then mixes information over the sequence. The other branch provides z, which multiplies the scan result through SiLU before an output projection restores the model width. Paper Figure 3
Original paper · Figure 3. Albert Gu and Tri Dao compare H3, a gated MLP and the Mamba block. Reproduced unchanged under CC BY 4.0. Use the explorer below to trace a small Mamba mixer operation by operation.
Trace a complete Mamba block
Follow the operations in a recorded, untrained CPU block: model width 4 → expanded width 8 → model width 4.
Outside input · residual bypass ↘
Channel 0 · six token states × three coordinates
Teal + / violet −. Intensity = |value| / 0.0376 (largest magnitude in this array).
Recorded sample at the highlighted operation
outside input · full shape [1 × 6 × 4]
outside prenorm · full shape [1 × 6 × 4]
projection · full shape [1 × 6 × 16]
Only these recorded flattened entries are drawn. The full shape is not reconstructed from four samples.
The outside RMS prenorm feeds one projection producing content x and gate z, each width 8.
Inspect exact values and equations
mixer: x/z → conv(x) → Δ,B,C → scan + D*x → × SiLU(z) → projection
Mixer input projection after outside RMS prenorm
outside input · [1 × 6 × 4]
| coordinate | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| flat 0… | -1.278 | -1.167 | -1.056 | -0.944 |
| flat 4… | -0.833 | -0.722 | -0.611 | -0.500 |
outside prenorm · [1 × 6 × 4]
| coordinate | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| flat 0… | -1.143 | -1.043 | -0.944 | -0.845 |
| flat 4… | -1.229 | -1.065 | -0.901 | -0.737 |
projection · [1 × 6 × 16]
| coordinate | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| flat 0… | -0.748 | -0.801 | 0.168 | 0.922 |
| flat 4… | 0.500 | -0.560 | -0.906 | -0.097 |
Recorded CPU float64 trace with untrained, authored sinusoidal weights. Shapes describe full tensors; tables show their first eight flattened values at most.
Recorded SSM state trajectory · channel 0 · [T = 6, N = 3]
| coordinate | n = 0 | n = 1 | n = 2 |
|---|---|---|---|
| after t = 0 | -8.23e-5 | 4.62e-5 | 1.41e-5 |
| after t = 1 | 7.05e-4 | -6.95e-4 | 3.06e-4 |
| after t = 2 | 0.002 | -0.002 | 0.001 |
| after t = 3 | -6.92e-4 | -6.28e-5 | 9.21e-4 |
| after t = 4 | -0.013 | 0.004 | 0.007 |
| after t = 5 | -0.038 | 0.006 | 0.025 |
Project x/z: Mixer input projection after outside RMS prenorm.
This complete trace uses a tiny mixer with model width 4, expanded width 8, state size 3 and convolution kernel width 3. Its six-position inputs and sinusoidal untrained weights are authored; the displayed tensors were recorded on CPU in float64. Select an operation to see its actual tensor shape and a labeled sample of its values. Samples are flattened excerpts, not a display of every element.
There are three distinct ways information travels here. The depthwise convolution mixes neighboring sequence positions within a channel. Linear projections mix channels. The selective recurrence carries a longer-range summary. The z gate changes the mixed output before it leaves the mixer; it is separate from the input-dependent state update itself.
There are also two distinct additions. D × x is a direct path inside the scan output, at expanded width. The enclosing residual adds the mixer’s projected result back to its model-width input, typically after a prenormalization. The figure shows that outside wrapper separately so the direct skip is not mistaken for the block residual. The original paper image and pinned code correspondence appear below with the executable reference.
The whole mixer is causal. Its complete-sequence result should therefore agree with processing one token at a time, provided the implementation preserves every piece of history that the next token needs.
What persists when the next token arrives
The SSM array is only one persistent buffer. The causal convolution also needs recent projected inputs. In the pinned Mamba implementation, the convolution allocation stores K samples per expanded channel, including the latest sample. Each decode step shifts the buffer and appends the arriving projected input before computing the convolution. Separately, the SSM updates its [b, E, N] state. Pinned step and cache allocation
Inspect what survives between tokens
A streamed Mamba-1 mixer retains its local convolution history and channel-by-state array.
Slide the local window; update persistent memory
Channel 0 · oldest → newest
The new projected value enters on the right. This is a three-slot trace; the storage calculation below uses width four.
Complete SSM memory · 8 channels × 3 coordinates
Teal + / violet −. Intensity = |value| / 0.00269 (largest magnitude in this array).
Fixed buffers beside a growing KV cache
Calculated persistent storage · MiB · linear axes
Sequence length
Mamba fixed: 1,966,080 bytes
KV at 2048: 100,663,296 bytes
Inspect exact values and equations
Incoming model-width input · t = 0
| coordinate | d0 | d1 | d2 | d3 |
|---|---|---|---|---|
| input | -1.278 | -1.167 | -1.056 | -0.944 |
Convolution buffer shift · channel 0 · oldest → newest
| coordinate | slot 0 | slot 1 | slot 2 |
|---|---|---|---|
| before token | 0 | 0 | 0 |
| after token | 0 | 0 | -0.748 |
The oldest entry leaves, the remaining entries shift left and the projected new input enters slot 2. Both buffers initially contain zeros.
SSM buffer after this token · [channels = 8, N = 3]
| coordinate | n = 0 | n = 1 | n = 2 |
|---|---|---|---|
| c = 0 | -8.23e-5 | 4.62e-5 | 1.41e-5 |
| c = 1 | -6.31e-4 | 3.54e-4 | 1.08e-4 |
| c = 2 | -0.003 | 0.002 | 4.61e-4 |
| c = 3 | -7.36e-4 | 4.13e-4 | 1.26e-4 |
| c = 4 | -9.80e-4 | 5.51e-4 | 1.68e-4 |
| c = 5 | -0.003 | 0.001 | 4.45e-4 |
| c = 6 | -2.90e-4 | 1.63e-4 | 4.98e-5 |
| c = 7 | -2.30e-4 | 1.29e-4 | 3.94e-5 |
Calculation: batch 1, layers 24, expanded channels 2048, state size 16, stored convolution width 4, KV heads 8, head width 64, 2 bytes per element. The small buffer trace above instead uses channels 8, N = 3 and width 3.
Conv = batch × layers × channels × K × bytes
KV = 2 × batch × layers × length × KV heads × head width × bytes
Calculated persistent storage (bytes)
| coordinate | bytes |
|---|---|
| SSM | 1,572,864 |
| convolution | 393,216 |
| Mamba-1 total | 1,966,080 |
| Transformer KV | 100,663,296 |
Mamba-1 total = 1,966,080 bytes (1.88 MiB); KV at length 2048 = 100,663,296 bytes (96.00 MiB). Fixed buffer size does not guarantee perfect retrieval.
After token 0, the mixer retains an 8 × 3 convolution buffer and an 8 × 3 SSM state.
The small streaming trace starts both buffers at zero. This differs from the nonzero incoming memory supplied to the earlier core recurrence demo. Inspecting the convolution buffer before and after arrival makes the short local memory visible beside the channel-by-state array.
The storage comparison is a calculation under a separate, explicitly stated configuration. If every stored element uses s bytes, the two Mamba buffers across L layers require:
A conventional full-context Transformer KV cache with T positions, H KV heads and head width P requires:
The factor two stores both keys and values. The figure specifies batch, layers, expanded width, state size, stored convolution width, KV heads, head width and element size. Move the sequence-length control to isolate the growth with T. Grouped-query attention changes the KV-head count; a sliding-window cache changes how many positions persist. Mixed state dtypes require accounting for the buffers separately.
These formulas count persistent inference state, excluding weights, temporary workspaces, allocator overhead and training tensors. They compare storage assumptions, not equally accurate models. Compressing all previous tokens into fixed-size memory creates a capacity constraint: a bounded state cannot promise exact retrieval of arbitrary histories. Making storage independent of context length does not remove that tradeoff.
The buffers now close the implementation loop: the same operations must agree as a recurrence, a prefix computation and a streamed block. The reference below makes that check runnable.
Original paper, equations and runnable CPU code
The six rows connect the coefficient projections, state update, scan algebra, local convolution, full mixer and persistent buffers to actual synchronized Python source. Displayed snippets and downloadable files come from the canonical example, rather than separate handwritten pseudocode. The self-check compares sequential and prefix states, checks causality and full-sequence versus token-step behavior, and verifies finite gradients. Float64 comparisons use absolute and relative tolerances of 1e−9; float32 checks use 1e−5.
This authored CPU adaptation follows official v2.2.4 (commit 95d8aba8), with upstream attribution retained. Its deterministic mechanics and random-weight smoke runs do not use a pretrained model.
Original-paper images: Albert Gu and Tri Dao, Mamba (arXiv v2), under CC BY 4.0. PDF excerpts are cropped; Figure 3 is unchanged. Select any source image to enlarge it.
Download Python reference · Pinned requirements
python -m pip install -r requirements-mamba.txt
python mamba.py --self-check
python mamba.py --smoke --backward
1. Produce coefficients from the locally mixed input
Original paper
The learned projections are shared across positions. Their outputs B, C and delta change with the current input; the learned continuous A remains input-independent.
Math and tensor shapes
Here E is expanded channel width, not the expansion factor. B and C are shared across channels in this selected Mamba implementation. The low-rank delta projection has rank ceil(model_width/16). Softplus produces a positive time scale, which affects retention and writing together.
x: [b,T,E]; B/C: [b,T,N]; delta: [b,T,E]; A: [E,N]
Python implementation
def coefficients(self, x):
"""Produce token-dependent delta/B/C after the local convolution."""
low_rank, b, c = self.x_proj(x).split(
(self.rank, self.state_size, self.state_size), -1
)
delta = F.softplus(self.dt_proj(low_rank))
a = -self.A_log.exp() # Learned, input-independent continuous dynamics.
return delta, a, b, c2. Retain, write and read each channel’s state
Original paper
The continuous-time description motivates a discrete update. The exact input rule used here follows selective_scan_ref in the pinned official implementation.
Math and tensor shapes
The readable reference writes delta*B*x; this differs from the exact zero-order-hold input integral in paper Equation 4. Read after updating the state, then add the direct D*x path. An optional incoming state is preserved, so the loop also describes continuation from an earlier prefix.
Current state: [b,E,N]; sequence output: [b,T,E]
Python implementation
def selective_scan(x, delta, a, b, c, d=None, initial_state=None):
"""Return outputs and all states of a diagonal selective recurrence.
Args:
x: Content [batch, length, channels].
delta: Already positive step sizes, same shape as x.
a: Negative continuous dynamics [channels, state].
b: Input-dependent write coefficients [batch, length, state].
c: Input-dependent read coefficients, same shape as b.
d: Optional direct skip [channels], separate from the outside residual.
initial_state: Optional state [batch, channels, state].
Returns:
Outputs [batch,length,channels] and states [batch,length,channels,state].
Input injection is delta*B*x, not the exact zero-order-hold integral.
"""
_validate_scan(x, delta, a, b, c, initial_state)
if d is not None:
_finite_shape(d, (x.shape[-1],), "D")
state = (
x.new_zeros(x.shape[0], x.shape[2], a.shape[1])
if initial_state is None
else initial_state
)
outputs, states = [], []
for index in range(x.shape[1]):
step = delta[:, index, :, None]
retention = torch.exp(step * a)
write = step * b[:, index, None, :] * x[:, index, :, None]
state = retention * state + write
readout = (state * c[:, index, None, :]).sum(-1)
outputs.append(readout if d is None else readout + d * x[:, index])
states.append(state)
return torch.stack(outputs, 1), torch.stack(states, 1)3. Compose chronological affine updates
Original paper
Associativity lets a training computation regroup a known sequence of state updates. The authors combine this algebra with fusion and recomputation to reduce GPU memory traffic.
Math and tensor shapes
Each round reads a snapshot of the previous round; updating in place would change the parallel dependency graph. This teaching Hillis–Steele scan has logarithmic depth but O(T log T) pair work and materializes intermediates. It establishes prefix agreement, not the work or memory efficiency of the production fused kernel.
Affine pairs: [b,T,E,N]; prefix states: [b,T,E,N]
Python implementation
def affine_prefix(x, delta, a, b, initial_state=None):
"""Hillis-Steele scan; each round reads a snapshot, not its own writes."""
_validate_scan(x, delta, a, b, b, initial_state)
retention = torch.exp(delta[..., None] * a)
write = delta[..., None] * b[:, :, None, :] * x[..., None]
pairs = [(retention, write)]
stride = 1
while stride < x.shape[1]:
previous_a, previous_b = pairs[-1]
# later(earlier(h)) = later.a*earlier.a*h + later.a*earlier.b+later.b
combined_a = previous_a[:, stride:] * previous_a[:, :-stride]
combined_b = (
previous_a[:, stride:] * previous_b[:, :-stride]
+ previous_b[:, stride:]
)
pairs.append(
(
torch.cat((previous_a[:, :stride], combined_a), 1),
torch.cat((previous_b[:, :stride], combined_b), 1),
)
)
stride *= 2
final_a, final_b = pairs[-1]
initial = (
x.new_zeros(x.shape[0], x.shape[2], a.shape[1])
if initial_state is None
else initial_state
)
return final_a * initial[:, None] + final_b, pairs4. Add causal local context before selecting coefficients
Original paper
The short depthwise convolution mixes adjacent positions independently in every expanded channel, before the longer-range selective recurrence.
Math and tensor shapes
Left padding supplies zeros at the start. The convolution reads only the present and previous positions; channels interact in the surrounding linear projections. This short local convolution remains valid even though input-dependent SSM coefficients prevent a single fixed global SSM convolution kernel.
Projected branch [b,T,E] → causal depthwise Conv1d → [b,T,E]
Python implementation
def causal_convolution(x, convolution):
"""Convolve [batch,length,channels] with left context only, then SiLU."""
width = convolution.kernel_size[0]
padded = F.pad(x.transpose(1, 2), (width - 1, 0))
return F.silu(
F.conv1d(
padded, convolution.weight, convolution.bias, groups=x.shape[-1]
)
).transpose(1, 2)5. Put the selective operator inside the complete mixer
Original paper
Original Figure 3 by Albert Gu and Tri Dao, Mamba, arXiv v2, reproduced unchanged under CC BY 4.0. The diagram connects local convolution, the SSM and the gated branch. The code follows the pinned mamba_simple mixer.
Math and tensor shapes
The scan already includes D*x. The z branch gates that scan result before output projection. The final equation describes an enclosing prenorm/residual wrapper, implemented separately from this forward method. It is a different addition from D*x inside the operator.
Tiny reference: [b,6,4] → two width-8 branches → [b,6,4]
Python implementation
def forward(self, u, return_trace=False):
"""Trace the mixer; an enclosing block supplies prenorm and residual."""
projected = self.in_proj(u)
x, z = projected.chunk(2, -1)
convolved = causal_convolution(x, self.conv)
delta, a, b, c = self.coefficients(convolved)
scanned, states = selective_scan(convolved, delta, a, b, c, self.D)
gated = scanned * F.silu(z)
output = self.out_proj(gated)
if return_trace:
return output, {
"projected": projected,
"x": x,
"z": z,
"convolved": convolved,
"delta": delta,
"A": a,
"B": b,
"C": c,
"states": states,
"scanned": scanned,
"gated": gated,
"output": output,
}
return output6. Carry both buffers to the next token
Original paper
Autoregressive decoding evaluates one new token at a time. The official allocation keeps a convolution buffer as well as the selective SSM state.
Math and tensor shapes
Shift the convolution history, append the projected current input, convolve, produce coefficients and update the SSM. K counts the stored samples including the latest, matching the pinned allocation rather than assuming K−1 slots. s is bytes per element, with a common dtype assumed for this formula; weights and temporary tensors are excluded.
Tiny reference buffers: [b,8,3] and [b,8,3]
Python implementation
def initial_cache(self, batch):
"""Store K projected samples, including the latest, as the source does."""
reference = self.in_proj.weight
return (
reference.new_zeros(batch, self.channels, self.conv_width),
reference.new_zeros(batch, self.channels, self.state_size),
)
def step(self, token, cache):
"""Process one [batch,model_width] token with functional cache updates."""
conv_state, ssm_state = cache
x, z = self.in_proj(token).chunk(2, -1)
conv_state = torch.cat((conv_state[..., 1:], x[..., None]), -1)
x = F.silu(
(conv_state * self.conv.weight[:, 0]).sum(-1) + self.conv.bias
)
delta, a, b, c = self.coefficients(x)
ssm_state = (
torch.exp(delta[..., None] * a) * ssm_state
+ delta[..., None] * b[:, None, :] * x[..., None]
)
readout = (ssm_state * c[:, None, :]).sum(-1) + self.D * x
output = self.out_proj(readout * F.silu(z))
return output, (conv_state, ssm_state)The reference establishes these mechanics. Its random-weight forward/backward smoke output is not text generation, a model-quality evaluation or a throughput measurement. To assess the learned model, we need the paper’s trained-model experiments and their protocol.
Published results and the compression tradeoff
The following values are published Pile validation perplexities, copied from the original paper’s Table 1. Lower is better. The comparison uses the GPT-NeoX tokenizer and 300 billion training tokens; Mamba and Pythia use context length 2,048. This is a selected model pair, not a claim about every Transformer training recipe.
| Model | Parameters | Pile validation perplexity |
|---|---|---|
| Pythia | 2.8B | 6.73 |
| Mamba | 2.8B | 6.22 |
Source: Mamba v2, Table 1 and §4.2.2. Table 1 also reports downstream zero-shot tasks, which measure different behavior from validation perplexity. RWKV’s context length in that comparison is 1,024, so sharing the dataset and training-token count does not make every experimental setting identical.
The paper separately measures kernel efficiency and end-to-end generation. Those boundaries matter: its generation protocol uses a prompt of 2,048 tokens, generates 128 tokens and varies batch size, while the scan benchmark isolates a different workload. Neither the storage formula nor the browser animation predicts those runtimes. Paper §4.5 and Appendix E.5
Mamba’s central result is that input-dependent compression can support useful sequence modeling while preserving a recurrent decode state. Its limitations follow the same mechanism: state capacity is finite, repeated retention can attenuate older signals, and operation count alone does not determine latency. Long-context success on the paper’s synthetic, audio or genomic tasks does not establish lossless recall of arbitrary language histories.
This leaves a computational question. The scan’s arithmetic is mostly elementwise, while modern GPUs devote substantial capability to matrix multiplication. Can we keep a recurrent state and reorganize much more of the sequence computation into matrix products? Mamba-2 answers by restricting the transition structure, deriving a structured mixing matrix and computing it in chunks.
Key takeaways
- Selection makes delta, B and C depend on the input; the learned continuous A stays input-independent in the implementation shown here.
- The state update combines retained memory and a new write, then reads the updated state. Delta changes both terms and is not a probability.
- Affine composition permits parallel prefix computation during training. Fusion and memory movement determine whether that algebra executes efficiently.
- A complete Mamba block includes local convolution, projections, a multiplicative gate and an outside residual, beyond the selective recurrence.
- Both convolution history and SSM state persist during decoding. Their fixed size trades growing storage for compressed context.
- Authored arithmetic, untrained CPU traces and published model evaluations provide different kinds of evidence.
Related reading
- Mamba-2: structured state space duality: turn a restricted state update into a structured mixing matrix and a chunked algorithm.
- VMamba: follow four spatial traversal orders, aligned cross-merge and a hierarchical image backbone.
- Vision Mamba: follow forward and backward patch sequences through a bidirectional visual mixer.
- Convolution operation: distinguish the short local convolution from the selective running state.
- Linear attention approximations: compare compressed sequence summaries with other alternatives to full pairwise attention.
- Original Mamba paper, arXiv v2 and official v2.2.4 source: the full derivations, trained-model experiments and optimized implementations.
