Coverage for transformer_lens/model_bridge/supported_architectures/mamba2.py: 96%
44 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-09-01 16:23 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-09-01 16:23 +0000
1"""Architecture adapter for HF's Mamba2ForCausalLM, plus the effective attention helper."""
2import warnings
3from typing import Any, Dict, Optional, Union
5import torch
7from transformer_lens.ActivationCache import ActivationCache
8from transformer_lens.model_bridge.architecture_adapter import ArchitectureAdapter
9from transformer_lens.model_bridge.bridge import TransformerBridge
10from transformer_lens.model_bridge.generalized_components import (
11 DepthwiseConv1DBridge,
12 EmbeddingBridge,
13 GatedRMSNormBridge,
14 LinearBridge,
15 RMSNormalizationBridge,
16 SSM2MixerBridge,
17 SSMBlockBridge,
18 UnembeddingBridge,
19)
22class Mamba2ArchitectureAdapter(ArchitectureAdapter):
23 """Wraps HF's Mamba2ForCausalLM.
25 Differs from Mamba-1 at the mixer level: fused in_proj (no x_proj/dt_proj),
26 two-input inner norm, multi-head structure with ``num_heads``/``head_dim``/
27 ``n_groups``, and an ``[num_heads]``-shaped ``dt_bias``. Shares
28 ``SSMBlockBridge``, ``DepthwiseConv1DBridge``, and the stateful generation
29 loop with Mamba-1.
30 """
32 # White-box forward: P1 is exact vs raw HF (mixer delegates to HF); P2/P3 skip
33 # without a HookedTransformer; P4 is generation.
34 applicable_phases: list[int] = [1, 2, 3, 4]
36 def __init__(self, cfg: Any) -> None:
37 super().__init__(cfg)
39 self.cfg.normalization_type = "RMS"
40 self.cfg.uses_rms_norm = True
41 self.cfg.positional_embedding_type = "none"
42 self.cfg.gated_mlp = False
43 self.cfg.attn_only = False
44 self.cfg.final_rms = True
45 self.cfg.is_stateful = True
47 # Most SSM config fields come from _HF_PASSTHROUGH_ATTRS. Mamba2Config
48 # has no `intermediate_size` field, so we compute it from expand and
49 # derive conv_dim from that. setattr() avoids mypy attr-defined errors
50 # since cfg is duck-typed for architecture-specific extensions.
51 expand = getattr(self.cfg, "expand", 2)
52 hidden_size = self.cfg.d_model
53 intermediate_size = expand * hidden_size
54 setattr(self.cfg, "intermediate_size", intermediate_size)
56 num_heads = self.cfg.n_heads
57 state_size = getattr(self.cfg, "state_size", 128)
58 n_groups = getattr(self.cfg, "n_groups", 1)
59 conv_dim = intermediate_size + 2 * n_groups * state_size
60 setattr(self.cfg, "conv_dim", conv_dim)
62 # HF splits in_proj 5 ways but two d_mlp slots are always size 0.
63 # Stored so the integration test can catch a future HF change that
64 # introduces non-zero d_mlp.
65 in_proj_out_features = 2 * intermediate_size + conv_dim + num_heads
66 setattr(self.cfg, "expected_in_proj_out_features", in_proj_out_features)
68 self.weight_processing_conversions = {}
70 self.component_mapping = {
71 "embed": EmbeddingBridge(name="backbone.embeddings"),
72 "blocks": SSMBlockBridge(
73 name="backbone.layers",
74 submodules={
75 "norm": RMSNormalizationBridge(name="norm", config=self.cfg),
76 "mixer": SSM2MixerBridge(
77 name="mixer",
78 config=self.cfg,
79 submodules={
80 "in_proj": LinearBridge(name="in_proj"),
81 "conv1d": DepthwiseConv1DBridge(name="conv1d"),
82 # TL calls this "inner_norm" to disambiguate from
83 # the block-level norm; name="norm" is the HF path.
84 "inner_norm": GatedRMSNormBridge(name="norm"),
85 "out_proj": LinearBridge(name="out_proj"),
86 },
87 ),
88 },
89 ),
90 "ln_final": RMSNormalizationBridge(name="backbone.norm_f", config=self.cfg),
91 "unembed": UnembeddingBridge(name="lm_head"),
92 }
94 def create_stateful_cache(
95 self,
96 hf_model: Any,
97 batch_size: int,
98 device: Any,
99 dtype: torch.dtype,
100 ) -> Any:
101 """Build a cache for the stateful generation loop."""
102 from transformers.cache_utils import DynamicCache
103 from transformers.models.mamba2 import modeling_mamba2
105 cache_cls = getattr(modeling_mamba2, "Mamba2Cache", None)
106 if cache_cls is not None: 106 ↛ 107line 106 didn't jump to line 107 because the condition on line 106 was never true
107 return cache_cls(hf_model.config, batch_size, device=device, dtype=dtype)
109 return DynamicCache(config=hf_model.config)
112def compute_effective_attention(
113 bridge: TransformerBridge,
114 cache: ActivationCache,
115 layer: Optional[int] = None,
116 include_dt_scaling: bool = False,
117) -> Union[torch.Tensor, Dict[int, torch.Tensor]]:
118 """Mamba-2 effective attention for one or all layers.
120 .. deprecated::
121 Use the family-agnostic ``cache.compute_ssm_effective_attention(layer=...)``
122 instead. This thin wrapper delegates to it and ignores ``bridge`` (the
123 cache already knows its model).
124 """
125 warnings.warn(
126 "mamba2.compute_effective_attention is deprecated; use "
127 "cache.compute_ssm_effective_attention(layer=..., include_dt_scaling=...).",
128 DeprecationWarning,
129 stacklevel=2,
130 )
131 return cache.compute_ssm_effective_attention(layer=layer, include_dt_scaling=include_dt_scaling)
134def compute_ssm_state(
135 bridge: TransformerBridge,
136 cache: ActivationCache,
137 layer: Optional[int] = None,
138 time_step: Optional[int] = None,
139) -> Union[torch.Tensor, Dict[int, torch.Tensor]]:
140 """Reconstruct the recurrent SSM state ``S`` for one or all Mamba-2 layers.
142 .. deprecated::
143 Use ``cache.compute_ssm_state(layer=..., time_step=...)`` instead. This
144 thin wrapper delegates to it and ignores ``bridge``.
145 """
146 warnings.warn(
147 "mamba2.compute_ssm_state is deprecated; use "
148 "cache.compute_ssm_state(layer=..., time_step=...).",
149 DeprecationWarning,
150 stacklevel=2,
151 )
152 return cache.compute_ssm_state(layer=layer, time_step=time_step)