Mamba3.forward() passes a 3-D tensor to step(), which requires 2-D — decode path raises EinopsError
Mamba3.forward() routes to self.step(...) when
inference_params.seqlen_offset > 0, but passes u through unchanged. u is
(batch, seqlen, hidden_dim) at that point, while step() documents and
requires (batch, d_model). The sequence axis is never squeezed, so the
cached-decode path raises before any kernel runs.
Where
mamba_ssm/modules/mamba3.py, in forward():
def forward(self, u, seq_idx=None, cu_seqlens=None, inference_params=None):
"""
u: (batch, seqlen, hidden_dim)
Returns: same shape as u
"""
batch, seqlen, dim = u.shape # u is 3-D here
...
if inference_params.seqlen_offset > 0:
out, _, _, _, _ = self.step(u, angle_dt_state, ssm_state, k_state, v_state)
return outand in step(), same file:
Args:
u: (batch, d_model) # 2-DThe contract mismatch is between two methods of the same class.
Traceback
Observed with mamba-ssm 2.3.2.post1, torch 2.11.0+cu128, CUDA 12.8, on
state-spaces/mamba3-siso-187m
(6792c27c00f3bb41506db1066dcd1c51bb0f4b02), driving one token at a time with
inference_params.seqlen_offset > 0:
File "mamba_ssm/modules/mamba3.py", line 172, in forward
out, _, _, _, _ = self.step(u, angle_dt_state, ssm_state, k_state, v_state)
File "mamba_ssm/modules/mamba3.py", line 354, in step
DT, B, C, x, z, trap, A, angles = self._preprocess(
File "mamba_ssm/modules/mamba3.py", line 288, in _preprocess
B = rearrange(B, "b (r g s) -> b r g s", g=self.num_bc_heads, r=rank)
einops.EinopsError: Error while processing rearrange-reduction pattern
"b (r g s) -> b r g s".
Input tensor shape: torch.Size([1, 1, 128]).
Additional info: {'g': 1, 'r': 1}.
Wrong shape: expected 2 dims. Received 3-dim tensor.B is (batch, seqlen, d_state) because self.in_proj(u) was given a 3-D
u; _preprocess expects (batch, d_state).
Note the assert mamba3_step_fn is not None at the top of step() passed —
the CuteDSL step kernel was installed and available. This is purely the shape
mismatch, not a missing optional dependency.
Reproducer
import torch
from mamba_ssm.models.mixer_seq_simple import MambaLMHeadModel
model = MambaLMHeadModel.from_pretrained(
"state-spaces/mamba3-siso-187m", device="cuda", dtype=torch.bfloat16
)
class IP: # minimal inference-params shim
def __init__(self):
self.max_seqlen, self.max_batch_size = 8, 1
self.seqlen_offset = 1 # > 0 routes forward() to step()
self.key_value_memory_dict = {}
ip = IP()
for li, layer in enumerate(model.backbone.layers):
cache = layer.mixer.allocate_inference_cache(1, 8)
for t in cache:
t.zero_()
ip.key_value_memory_dict[li] = cache
model(torch.tensor([[1]], device="cuda"), inference_params=ip) # raisesThe divergence from Mamba2
Mamba3.forward() is consistent with Mamba2.forward() — both pass the 3-D
u straight into step() without squeezing:
# mamba2.py
if inference_params.seqlen_offset > 0:
out, _, _ = self.step(u, conv_state, ssm_state)
return outThe difference is in step(). Mamba2.step() absorbs the sequence axis itself
and restores it on return, so forward()'s "Returns: same shape as u"
contract holds:
# mamba2.py
def step(self, hidden_states, conv_state, ssm_state):
assert hidden_states.shape[1] == 1, "Only support decoding with 1 token at a time for now"
zxbcdt = self.in_proj(hidden_states.squeeze(1)) # (B 2D)
...
return out.unsqueeze(1), conv_state, ssm_stateMamba3.step() does neither: it calls self.in_proj(u) unsqueezed and returns
a 2-D out. So Mamba3.step() is the method that departs from the
established convention, not forward().
Proposed fix
Matching the Mamba2 convention keeps forward() untouched and preserves its
public contract:
def step(self, u, angle_state, ssm_state, k_state, v_state, **kwargs):
"""
Args:
u: (batch, 1, d_model)
Returns:
out: (batch, 1, d_model)
"""
assert u.shape[1] == 1, "Only support decoding with 1 token at a time for now"
zxBCdt = self.in_proj(u.squeeze(1))
...
return out.unsqueeze(1), nxt_angle_state, state_out, nxt_k_state, nxt_v_stateThe alternative — squeezing at the forward() call site and leaving step()
as a 2-D API — also resolves the crash, and has one point in its favour:
Mamba3.step() does work correctly today when called directly with a 2-D
(batch, d_model) tensor, which is how we have been exercising it. So the
2-D contract is real and functioning; it simply is not what forward()
supplies. Either method can move, but they currently disagree.
I have not verified the full decode path end-to-end past the shape error — the
step() docstring notes it is "Only tested on H100" and this was observed on
an A10G, so there may be further hardware-specific issues behind it. Filing the
shape defect on its own rather than claiming a validated fix.
Environment
mamba-ssm2.3.2.post1 (also present onmainat time of filing)- torch 2.11.0+cu128, CUDA 12.8
- NVIDIA A10
- model
state-spaces/mamba3-siso-187m@6792c27c00f3bb41506db1066dcd1c51bb0f4b02
Source: state-spaces/mamba