Coverage for transformer_lens/model_bridge/generalized_components/depthwise_conv1d.py: 42%
30 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"""Bridge for Mamba-style depthwise causal Conv1d (distinct from GPT-2's Conv1D linear)."""
2from typing import Any, Dict, Optional, cast
4import torch
6from transformer_lens.model_bridge.generalized_components.base import (
7 GeneralizedComponent,
8)
11class DepthwiseConv1DBridge(GeneralizedComponent):
12 """Wraps an ``nn.Conv1d`` depthwise causal convolution with input/output hooks.
14 Hook shapes (channel-first, as HF's MambaMixer transposes before the call):
15 hook_in: [batch, channels, seq_len]
16 hook_out: [batch, channels, seq_len + conv_kernel - 1] (pre causal trim)
18 Decode-step limitation: on stateful generation, HF's Mamba/Mamba-2 mixers
19 bypass ``self.conv1d(...)`` and read ``self.conv1d.weight`` directly, so the
20 forward hook never fires on decode steps — only on prefill. For per-step
21 conv output during decode, compute it manually from the cached conv_states
22 and ``conv1d.original_component.weight``, or run token-by-token via
23 ``forward()`` instead of ``generate()``.
24 """
26 def forward(self, input: torch.Tensor, *args: Any, **kwargs: Any) -> torch.Tensor:
27 if self.original_component is None: 27 ↛ 28line 27 didn't jump to line 28 because the condition on line 27 was never true
28 raise RuntimeError(
29 f"Original component not set for {self.name}. "
30 "Call set_original_component() first."
31 )
32 input = self.hook_in(input)
33 output = self.original_component(input, *args, **kwargs)
34 output = self.hook_out(output)
35 return output
37 def get_random_inputs(
38 self,
39 batch_size: int = 2,
40 seq_len: int = 8,
41 device: Optional[torch.device] = None,
42 dtype: Optional[torch.dtype] = None,
43 ) -> Dict[str, Any]:
44 if self.original_component is None:
45 raise RuntimeError(
46 f"Original component not set for {self.name}. "
47 "Call set_original_component() first."
48 )
49 device = device or torch.device("cpu")
50 dtype = dtype or torch.float32
51 conv = cast(torch.nn.Conv1d, self.original_component)
52 channels = conv.in_channels # exact, from the wrapped Conv1d
53 return {"args": (torch.randn(batch_size, channels, seq_len, device=device, dtype=dtype),)}
55 def __repr__(self) -> str:
56 if self.original_component is not None:
57 try:
58 in_channels = self.original_component.in_channels
59 out_channels = self.original_component.out_channels
60 kernel_size = self.original_component.kernel_size
61 groups = self.original_component.groups
62 return (
63 f"DepthwiseConv1DBridge({in_channels} -> {out_channels}, "
64 f"kernel_size={kernel_size}, groups={groups})"
65 )
66 except AttributeError:
67 return (
68 f"DepthwiseConv1DBridge(name={self.name}, "
69 f"original_component={type(self.original_component).__name__})"
70 )
71 return f"DepthwiseConv1DBridge(name={self.name}, original_component=None)"