family mamba
No attention and no MLP: each block is one norm and a selective-scan mixer with a state per channel, and reading inside the mixer needs mamba_ssm's CUDA kernels routed to transformers' torch functions.
One block of the stack. Hover any part for its nnterp name; click to pin the card.
One block of the stack. Hover any part for its nnterp name; click to pin the card.
One block of the stack. Hover any part for its nnterp name; click to pin the card.
identity layers[i].input + linear_attn.attention_output == layer_output
⚠︎ marks a value this checkpoint has only under some load: hover it for the condition.
What print(model) shows on state-spaces/mamba-130m-hf: the native modules, standard names and every standard value with its layout.
MambaForCausalLM(
(lm_head): Linear(in_features=768, out_features=50280, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(50280, 768)
(layers): ModuleList(
(0-23): 24 x MambaBlock(
(input_layernorm/norm): MambaRMSNorm(768, eps=1e-05)
(linear_attn/mixer): MambaMixer(
(conv1d): Conv1d(1536, 1536, kernel_size=(4,), stride=(1,), padding=(3,), groups=1536)
(act): SiLUActivation()
(in_proj): Linear(in_features=768, out_features=3072, bias=False)
(x_proj): Linear(in_features=1536, out_features=80, bias=False)
(dt_proj): Linear(in_features=48, out_features=1536, bias=True)
(out_proj): Linear(in_features=1536, out_features=768, bias=False)
(attention_queries) -> ScanQK [batch seq groups state_dim]: C, the vector each token reads the state with
(attention_keys) -> ScanQK [batch seq groups state_dim]: B, the vector each token writes into the state with
(attention_values) -> ScanValues [batch seq channels]: x, the input the scan writes into the state
(betas) -> ScanSteps [batch seq channels]: The step size dt = softplus(dt + dt_bias), per token and channel; derived, read-only
(decays) -> ScanDecays [batch seq channels state_dim]: The per-token log decay dt * A of the state; derived, read-only
(state_input) -> ScanState | None [batch channels state_dim]: The state entering the layer on a decode step (a copy of the cache's buffer), None on a prompt
(attention_head_outputs) -> ScanValues [batch seq channels]: y = C.h + D.x, the scan's read of the state before the gate and out_proj
(state_output) -> ScanState [batch channels state_dim]: The state leaving the layer
(state) -> ScanState [batch channels state_dim]: The state after one token of the prompt; iterate it with tracer.iter; needs route_kernels(family, 'torch')
(states) -> ScanStates [batch seq channels state_dim]: The state after every token of this call; needs route_kernels(family, 'torch')
(attention_output) -> Residual [batch seq hidden_size]: What the mixer adds to the residual stream
)
(layer_mid): Unavailable: a state-space block: one sublayer, the mixer, so there is no stream between sublayers and no MLP
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): MambaRMSNorm(768, eps=1e-05)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on state-spaces/mamba-370m-hf: the native modules, standard names and every standard value with its layout.
MambaForCausalLM(
(lm_head): Linear(in_features=1024, out_features=50280, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(50280, 1024)
(layers): ModuleList(
(0-47): 48 x MambaBlock(
(input_layernorm/norm): MambaRMSNorm(1024, eps=1e-05)
(linear_attn/mixer): MambaMixer(
(conv1d): Conv1d(2048, 2048, kernel_size=(4,), stride=(1,), padding=(3,), groups=2048)
(act): SiLUActivation()
(in_proj): Linear(in_features=1024, out_features=4096, bias=False)
(x_proj): Linear(in_features=2048, out_features=96, bias=False)
(dt_proj): Linear(in_features=64, out_features=2048, bias=True)
(out_proj): Linear(in_features=2048, out_features=1024, bias=False)
(attention_queries) -> ScanQK [batch seq groups state_dim]: C, the vector each token reads the state with
(attention_keys) -> ScanQK [batch seq groups state_dim]: B, the vector each token writes into the state with
(attention_values) -> ScanValues [batch seq channels]: x, the input the scan writes into the state
(betas) -> ScanSteps [batch seq channels]: The step size dt = softplus(dt + dt_bias), per token and channel; derived, read-only
(decays) -> ScanDecays [batch seq channels state_dim]: The per-token log decay dt * A of the state; derived, read-only
(state_input) -> ScanState | None [batch channels state_dim]: The state entering the layer on a decode step (a copy of the cache's buffer), None on a prompt
(attention_head_outputs) -> ScanValues [batch seq channels]: y = C.h + D.x, the scan's read of the state before the gate and out_proj
(state_output) -> ScanState [batch channels state_dim]: The state leaving the layer
(state) -> ScanState [batch channels state_dim]: The state after one token of the prompt; iterate it with tracer.iter; needs route_kernels(family, 'torch')
(states) -> ScanStates [batch seq channels state_dim]: The state after every token of this call; needs route_kernels(family, 'torch')
(attention_output) -> Residual [batch seq hidden_size]: What the mixer adds to the residual stream
)
(layer_mid): Unavailable: a state-space block: one sublayer, the mixer, so there is no stream between sublayers and no MLP
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): MambaRMSNorm(1024, eps=1e-05)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on state-spaces/mamba-790m-hf: the native modules, standard names and every standard value with its layout.
MambaForCausalLM(
(lm_head): Linear(in_features=1536, out_features=50280, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(50280, 1536)
(layers): ModuleList(
(0-47): 48 x MambaBlock(
(input_layernorm/norm): MambaRMSNorm(1536, eps=1e-05)
(linear_attn/mixer): MambaMixer(
(conv1d): Conv1d(3072, 3072, kernel_size=(4,), stride=(1,), padding=(3,), groups=3072)
(act): SiLUActivation()
(in_proj): Linear(in_features=1536, out_features=6144, bias=False)
(x_proj): Linear(in_features=3072, out_features=128, bias=False)
(dt_proj): Linear(in_features=96, out_features=3072, bias=True)
(out_proj): Linear(in_features=3072, out_features=1536, bias=False)
(attention_queries) -> ScanQK [batch seq groups state_dim]: C, the vector each token reads the state with
(attention_keys) -> ScanQK [batch seq groups state_dim]: B, the vector each token writes into the state with
(attention_values) -> ScanValues [batch seq channels]: x, the input the scan writes into the state
(betas) -> ScanSteps [batch seq channels]: The step size dt = softplus(dt + dt_bias), per token and channel; derived, read-only
(decays) -> ScanDecays [batch seq channels state_dim]: The per-token log decay dt * A of the state; derived, read-only
(state_input) -> ScanState | None [batch channels state_dim]: The state entering the layer on a decode step (a copy of the cache's buffer), None on a prompt
(attention_head_outputs) -> ScanValues [batch seq channels]: y = C.h + D.x, the scan's read of the state before the gate and out_proj
(state_output) -> ScanState [batch channels state_dim]: The state leaving the layer
(state) -> ScanState [batch channels state_dim]: The state after one token of the prompt; iterate it with tracer.iter; needs route_kernels(family, 'torch')
(states) -> ScanStates [batch seq channels state_dim]: The state after every token of this call; needs route_kernels(family, 'torch')
(attention_output) -> Residual [batch seq hidden_size]: What the mixer adds to the residual stream
)
(layer_mid): Unavailable: a state-space block: one sublayer, the mixer, so there is no stream between sublayers and no MLP
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): MambaRMSNorm(1536, eps=1e-05)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on state-spaces/mamba-1.4b-hf: the native modules, standard names and every standard value with its layout.
MambaForCausalLM(
(lm_head): Linear(in_features=2048, out_features=50280, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(50280, 2048)
(layers): ModuleList(
(0-47): 48 x MambaBlock(
(input_layernorm/norm): MambaRMSNorm(2048, eps=1e-05)
(linear_attn/mixer): MambaMixer(
(conv1d): Conv1d(4096, 4096, kernel_size=(4,), stride=(1,), padding=(3,), groups=4096)
(act): SiLUActivation()
(in_proj): Linear(in_features=2048, out_features=8192, bias=False)
(x_proj): Linear(in_features=4096, out_features=160, bias=False)
(dt_proj): Linear(in_features=128, out_features=4096, bias=True)
(out_proj): Linear(in_features=4096, out_features=2048, bias=False)
(attention_queries) -> ScanQK [batch seq groups state_dim]: C, the vector each token reads the state with
(attention_keys) -> ScanQK [batch seq groups state_dim]: B, the vector each token writes into the state with
(attention_values) -> ScanValues [batch seq channels]: x, the input the scan writes into the state
(betas) -> ScanSteps [batch seq channels]: The step size dt = softplus(dt + dt_bias), per token and channel; derived, read-only
(decays) -> ScanDecays [batch seq channels state_dim]: The per-token log decay dt * A of the state; derived, read-only
(state_input) -> ScanState | None [batch channels state_dim]: The state entering the layer on a decode step (a copy of the cache's buffer), None on a prompt
(attention_head_outputs) -> ScanValues [batch seq channels]: y = C.h + D.x, the scan's read of the state before the gate and out_proj
(state_output) -> ScanState [batch channels state_dim]: The state leaving the layer
(state) -> ScanState [batch channels state_dim]: The state after one token of the prompt; iterate it with tracer.iter; needs route_kernels(family, 'torch')
(states) -> ScanStates [batch seq channels state_dim]: The state after every token of this call; needs route_kernels(family, 'torch')
(attention_output) -> Residual [batch seq hidden_size]: What the mixer adds to the residual stream
)
(layer_mid): Unavailable: a state-space block: one sublayer, the mixer, so there is no stream between sublayers and no MLP
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): MambaRMSNorm(2048, eps=1e-05)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on state-spaces/mamba-2.8b-hf: the native modules, standard names and every standard value with its layout.
MambaForCausalLM(
(lm_head): Linear(in_features=2560, out_features=50280, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(50280, 2560)
(layers): ModuleList(
(0-63): 64 x MambaBlock(
(input_layernorm/norm): MambaRMSNorm(2560, eps=1e-05)
(linear_attn/mixer): MambaMixer(
(conv1d): Conv1d(5120, 5120, kernel_size=(4,), stride=(1,), padding=(3,), groups=5120)
(act): SiLUActivation()
(in_proj): Linear(in_features=2560, out_features=10240, bias=False)
(x_proj): Linear(in_features=5120, out_features=192, bias=False)
(dt_proj): Linear(in_features=160, out_features=5120, bias=True)
(out_proj): Linear(in_features=5120, out_features=2560, bias=False)
(attention_queries) -> ScanQK [batch seq groups state_dim]: C, the vector each token reads the state with
(attention_keys) -> ScanQK [batch seq groups state_dim]: B, the vector each token writes into the state with
(attention_values) -> ScanValues [batch seq channels]: x, the input the scan writes into the state
(betas) -> ScanSteps [batch seq channels]: The step size dt = softplus(dt + dt_bias), per token and channel; derived, read-only
(decays) -> ScanDecays [batch seq channels state_dim]: The per-token log decay dt * A of the state; derived, read-only
(state_input) -> ScanState | None [batch channels state_dim]: The state entering the layer on a decode step (a copy of the cache's buffer), None on a prompt
(attention_head_outputs) -> ScanValues [batch seq channels]: y = C.h + D.x, the scan's read of the state before the gate and out_proj
(state_output) -> ScanState [batch channels state_dim]: The state leaving the layer
(state) -> ScanState [batch channels state_dim]: The state after one token of the prompt; iterate it with tracer.iter; needs route_kernels(family, 'torch')
(states) -> ScanStates [batch seq channels state_dim]: The state after every token of this call; needs route_kernels(family, 'torch')
(attention_output) -> Residual [batch seq hidden_size]: What the mixer adds to the residual stream
)
(layer_mid): Unavailable: a state-space block: one sublayer, the mixer, so there is no stream between sublayers and no MLP
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): MambaRMSNorm(2560, eps=1e-05)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)out = x + mixer(norm(x)) # added in float32 (residual_in_fp32)
mixer(h): u, z = in_proj(h); u = silu(conv1d(u)) # causal, width 4; u is attention_values
dt, B, C = x_proj(u); dt = dt_proj(dt)
y = scan(u, dt, A, B, C) + D * u # y is attention_head_outputs
return out_proj(y * silu(z)) # attention_output
The block's pre-norm is natively norm, and nnterp also serves it as input_layernorm; the
model's final norm is natively norm_f, and nnterp serves it as model.norm. So
model.layers[i].norm is the block's pre-norm, while model.norm is the final norm.
attention_output is the mixer's own output (out_proj's), and it is all the block adds, so
layers[i].input + attention_output == layer_output exactly, and ablating attention_output
ablates the block. The add is in float32: in a bfloat16 load attention_output is bfloat16 and
layer_output float32, and the identity holds after the cast:
with model.trace(prompt):
x = model.layers[1].input.save()
attn = model.layers[1].linear_attn.attention_output.save()
out = model.layers[1].layer_output.save()
torch.testing.assert_close(x + attn.float(), out)
The stream grows through the stack: on 130m its norm at the last token of a ten-token prompt is 7.4 entering block 0 and 188 entering block 23, and block 23's contribution (262) is larger than the stream it joins. A steering vector sized for one depth is small or large at another.
With mamba_ssm installed transformers dispatches the scan and the decode step to its CUDA
kernels. On CPU every trace then fails inside the kernel, even one that reads only
layer_output (RuntimeError: Expected u.is_cuda() to be true). On GPU, unrouted, layer_output,
attention_output and logits read normally and every other linear_attn value raises
nnterp.Unavailable. Route before the trace that reads the mixer's values. Routing still takes
effect after an unrouted trace (one that read only layer_output, one that failed on CPU, or one
whose read was refused):
import nnterp
from nnterp import StandardizedTransformer, route_kernels
route_kernels(nnterp.families.mamba, "torch")
model = StandardizedTransformer("state-spaces/mamba-130m-hf", device="cpu")
The two paths compute the same model. In float32 on GPU the kernel and the routed torch scan give logits within 2e-4 of each other (130m and 370m, logits up to 98), with the same top token everywhere. In bfloat16 they differ by up to 2.25 on 130m, about what the torch scan on CPU and on GPU differ by (2.0), so the gap is bfloat16 rounding. The torch scan is a token loop: on a 22-token prompt it took 83 ms against the kernel's 46 ms on 130m, 286 ms against 46 ms on 370m.
model.train() sends the forward down a third path, the fused mamba_inner_fn, which
route_kernels does not reroute: with mamba_ssm installed and causal-conv1d not, every trace
in training mode fails with AssertionError: causal_conv1d_cuda is not available, on CPU and
GPU, routed or not. Gradients do not need it; backward() runs through the routed scan in eval
mode.
Each channel of the mixer's width (1536 on 130m) keeps its own state of state_size (16)
numbers. attention_queries and attention_keys are C and B, [batch, seq, 1, 16]: one
vector per token, shared by every channel. attention_values is x, [batch, seq, 1536], after
the convolution and the silu. betas is the step size softplus(dt_proj(dt)) (dt_proj's bias included), one per token
and channel; decays is betas[..., None] * A, [batch, seq, 1536, 16], the log of how much of
each state entry a token keeps. Both are read-only.
The rates are far apart on real weights. A = -exp(A_log) runs from -2e-6 to -3e8 on 130m
(median -12.6), so over every block and a ten-token prompt exp(decays) has a median of 0.48,
28% of entries keep less than 1% and 12% keep more than 99% (on 370m: 0.55, 15%, 9%). The pinned
tiny checkpoint has the initialization's A, -1 to -16 on every channel.
The scan is a causal linear map over tokens, one per channel: y_t = sum_s alpha[t, s] x_s + D x_t
with alpha[t, s] = C_t . (exp(decays_{s+1} + ... + decays_t) * betas_s B_s). The served values
rebuild it exactly (float32, as loaded):
mix = model.layers[1].linear_attn
with model.trace(prompt):
x = mix.attention_values[0].save() # [seq, channels]
beta = mix.betas[0].save() # [seq, channels]
decay = mix.decays[0].save() # [seq, channels, state_dim]
B = mix.attention_keys[0, :, 0].save() # [seq, state_dim]
C = mix.attention_queries[0, :, 0].save() # [seq, state_dim]
y = mix.attention_head_outputs[0].save() # [seq, channels]
D = mix._module.D # after the trace: the weights load on the first one
cum = decay.cumsum(0)
causal = torch.ones(len(x), len(x), dtype=torch.bool, device=x.device).tril()[..., None, None]
kept = (cum[:, None] - cum[None, :]).masked_fill(~causal, -torch.inf).exp()
alpha = torch.einsum("tn,tscn,sn->tsc", C, kept, B) * beta # [query t, key s, channel]
torch.testing.assert_close(torch.einsum("tsc,sc->tc", alpha, x) + D * x, y)
kept is [seq, seq, channels, state_dim], 10 MB for ten tokens on 130m and growing with the
square of the prompt: slice the channels on a long one.
state_output is the state after the call's last token, [batch, 1536, 16], and equals
states[:, -1]; states holds every token's and needs the routing. A write to the state at
token t is not all the block carries forward: the width-4 convolution mixes each token's
input with the three before it. On 130m, zeroing every block's state after token 4 of a
ten-token prompt and running the last five tokens alone give logits up to 87 apart and
different top tokens at three of the five positions.
On a decode step the update comes before the read, the reverse of the prompt's scan, so under
generate read state_output before attention_head_outputs on every step after the first.
The wrong order raises nothing: step 1's state_output comes back as step 2's.
heads, leaving = [], []
with model.generate(prompt, max_new_tokens=3, do_sample=False) as tracer:
for step in tracer.iter[:3]:
if step == 0: # the prompt's scan: y, then the state
heads.append(mix.attention_head_outputs.save())
leaving.append(mix.state_output.save())
else: # a decode step: the state, then y
leaving.append(mix.state_output.save())
heads.append(mix.attention_head_outputs.save())
attention_queries and attention_keys are views of torch.split's outputs, and an in-place edit under
autograd raises RuntimeError: Output 0 of Select is a view and is being modified inplace.
Assign a copy, or edit in place under torch.no_grad(); both reach the scan and give the same
logits. attention_values takes in-place edits.
with model.trace(prompt):
q = mix.attention_queries.clone()
q[:, -1] = 0 # the last token reads nothing from the state
mix.attention_queries = q
lm_head is embed_tokens' weight; the checkpoint stores no lm_head, so an edit to one is an
edit to the other. project_on_vocab at the last block's layer_output equals logits
exactly. The final norm's gain is its weight (mean 1.05, up to 5.2 on 130m). Nothing is added
after embed_tokens, so token_embeddings equals layers[0].input.
The checkpoints use GPT-NeoX-20B's BPE (GPTNeoXTokenizer, 50277 tokens; the embedding has 50280
rows). It prepends no BOS, and <|endoftext|> (id 0) is BOS, EOS and padding. The mixer zeroes
padded positions before and after the convolution, so the state stays zero across left padding:
a padded prompt's logits match the prompt alone to 2e-4. On 370m config.d_inner reads 160; the
mixer's width is intermediate_size, 2048.
This family is the -hf conversions of the original Mamba, 130m to 2.8b. Falcon-Mamba is its
own family, falcon_mamba, with RMS norms on B, C and dt inside the mixer; Mamba-2 is
mamba2, a different mixer (SSD, one state per head); Jamba's Mamba blocks carry this mixer.
attention_queries, attention_keys, betas and
decays.layer_output here,
and inserts facts with rank-one edits.Mamba (MambaForCausalLM): Mamba-1, a pure state-space model.
Its tree is backbone.{embeddings, layers[i].{norm, mixer}, norm_f} plus
lm_head. A block is a pre-norm and a selective-scan mixer, nothing else:
layer_output = input + mixer(norm(input)), so there is no self_attn
and no mlp, and the identity is input + linear_attn.attention_output == layer_output. The mixer answers to linear_attn, the standard name
of a recurrent mixer, and carries the SelectiveScan values; the block's
norm answers to input_layernorm.
The kernels: with mamba_ssm installed the forward dispatches the scan
and the decode step to its compiled CUDA kernels, which have no source to
read inside and do not run on CPU; nnterp.route_kernels(model.family, "torch") before the first trace binds them to transformers' pure-torch
functions. With residual_in_fp32 the block adds in float32, so
layer_output is float32 whatever the load dtype; the model casts the
stream to lm_head's dtype and returns float32 logits, and so does the
family's project_on_vocab. The config's
intermediate_size is the mixer's inner width (expand * hidden_size,
the channels of the state); there are no attention heads, so the head sizes
have nothing to read.