Spaces:
Running on Zero
Running on Zero
Download continuousintelligence/ci_proto/_mamba.py from hugging-apps/ci-proto: direct link, hf CLI and curl.
- Browser
- Download file 7.83 kB
-
https://huggingface.co/spaces/hugging-apps/ci-proto/resolve/main/continuousintelligence/ci_proto/_mamba.py
- Command line
-
hf download hf://spaces/hugging-apps/ci-proto/continuousintelligence/ci_proto/_mamba.py
-
curl -L -o _mamba.py https://huggingface.co/spaces/hugging-apps/ci-proto/resolve/main/continuousintelligence/ci_proto/_mamba.py
7.83 kB
| """PyTorch Mamba-3 recurrence for CPU and CUDA inference. | |
| Adapted by Continuous Intelligence from Mamba's module and | |
| SISO/MIMO reference calculations: | |
| https://github.com/state-spaces/mamba/tree/a14b1dff0454a3bc27d9eb31355dc01e4b2490ec | |
| RMS normalisation is adapted from xLSTM Large: | |
| https://github.com/NX-AI/xlstm/blob/ab22eadbd245f293dd8dec38ed29963d73758a12/xlstm/xlstm_large/components.py | |
| Copyright (c) 2026, Dao AI Lab, Goombalab. | |
| Copyright 2024 NXAI GmbH and its affiliates 2024 | |
| Upstream portions are licensed under Apache-2.0; see NOTICE. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| import torch | |
| import torch.nn.functional as F | |
| from einops import rearrange | |
| from torch import Tensor, nn | |
| class RMSNorm(nn.Module): | |
| """RMS norm with optional float32 reductions.""" | |
| def __init__( | |
| self, | |
| num_features: int, | |
| eps: float = 1e-06, | |
| force_float32_reductions: bool = True, | |
| use_bias: bool = False, | |
| ) -> None: | |
| super().__init__() | |
| self.eps = eps | |
| self.force_float32_reductions = force_float32_reductions | |
| self.weight = nn.Parameter(torch.ones(num_features)) | |
| self.bias = nn.Parameter(torch.zeros(num_features)) if use_bias else None | |
| def forward(self, x: Tensor) -> Tensor: | |
| in_dtype = x.dtype | |
| if self.force_float32_reductions: | |
| x = x.float() | |
| x = x * torch.rsqrt(x.pow(2).mean(dim=-1, keepdim=True) + self.eps) | |
| x = x.to(in_dtype) | |
| x = x * self.weight | |
| if self.bias is not None: | |
| x = x + self.bias | |
| return x | |
| def _rotate_pairs(x: Tensor, cos: Tensor, sin: Tensor, interleaved: bool) -> Tensor: | |
| """Rotate pairs of the last axis; angles beyond ``cos`` leave their pairs unrotated.""" | |
| if interleaved: | |
| pairs = x.view(*x.shape[:-1], -1, 2) | |
| first, second = (pairs[..., 0], pairs[..., 1]) | |
| else: | |
| half = x.shape[-1] // 2 | |
| first, second = (x[..., :half], x[..., half:]) | |
| if cos.shape[-1] < first.shape[-1]: | |
| pad = first.shape[-1] - cos.shape[-1] | |
| cos = F.pad(cos, (0, pad), value=1.0) | |
| sin = F.pad(sin, (0, pad), value=0.0) | |
| rotated_first = first * cos - second * sin | |
| rotated_second = first * sin + second * cos | |
| if interleaved: | |
| return torch.stack([rotated_first, rotated_second], dim=-1).view_as(x) | |
| return torch.cat([rotated_first, rotated_second], dim=-1) | |
| class Mamba3Reference(nn.Module): | |
| """Mamba-3 block evaluated step by step in plain PyTorch.""" | |
| def __init__( | |
| self, | |
| d_model: int, | |
| d_state: int = 128, | |
| expand: int = 2, | |
| headdim: int = 64, | |
| is_mimo: bool = False, | |
| mimo_rank: int = 4, | |
| ) -> None: | |
| super().__init__() | |
| if type(d_state) is not int or d_state < 4 or d_state % 2: | |
| raise ValueError("mamba_state_dim must be an even integer of at least 4") | |
| self.d_state = d_state | |
| self.is_mimo = is_mimo | |
| self.mimo_rank = mimo_rank if is_mimo else 1 | |
| self.d_inner = expand * d_model | |
| if self.d_inner % headdim != 0: | |
| raise ValueError(f"expand * d_model {self.d_inner} not divisible by headdim {headdim}") | |
| self.headdim = headdim | |
| self.nheads = self.d_inner // headdim | |
| split = d_state // 2 - d_state // 2 % 2 | |
| self.num_rope_angles = split // 2 | |
| bc = self.d_state * self.mimo_rank | |
| d_in_proj = 2 * self.d_inner + 2 * bc + 3 * self.nheads + self.num_rope_angles | |
| self.in_proj = nn.Linear(d_model, d_in_proj, bias=False) | |
| dt = torch.exp( | |
| torch.rand(self.nheads) * (math.log(0.1) - math.log(0.001)) + math.log(0.001) | |
| ).clamp(min=1e-4) | |
| self.dt_bias = nn.Parameter(dt + torch.log(-torch.expm1(-dt))) | |
| self.B_bias = nn.Parameter(torch.ones(self.nheads, self.mimo_rank, d_state)) | |
| self.C_bias = nn.Parameter(torch.ones(self.nheads, self.mimo_rank, d_state)) | |
| self.B_norm = RMSNorm(d_state, eps=1e-05) | |
| self.C_norm = RMSNorm(d_state, eps=1e-05) | |
| if is_mimo: | |
| shape = (self.nheads, self.mimo_rank, headdim) | |
| self.mimo_x = nn.Parameter(torch.ones(shape) / self.mimo_rank) | |
| self.mimo_z = nn.Parameter(torch.ones(shape)) | |
| self.mimo_o = nn.Parameter(torch.ones(shape) / self.mimo_rank) | |
| self.D = nn.Parameter(torch.ones(self.nheads)) | |
| self.out_proj = nn.Linear(self.d_inner, d_model, bias=False) | |
| def forward(self, u: Tensor) -> Tensor: | |
| batch, seq, _ = u.shape | |
| bc = self.d_state * self.mimo_rank | |
| z, x, B, C, dd_dt, dd_A, trap, angles = torch.split( | |
| self.in_proj(u), | |
| [ | |
| self.d_inner, | |
| self.d_inner, | |
| bc, | |
| bc, | |
| self.nheads, | |
| self.nheads, | |
| self.nheads, | |
| self.num_rope_angles, | |
| ], | |
| dim=-1, | |
| ) | |
| z = rearrange(z, "b l (h p) -> b l h p", p=self.headdim).to(torch.float32) | |
| x = rearrange(x, "b l (h p) -> b l h p", p=self.headdim).to(torch.float32) | |
| B = rearrange(B, "b l (r g n) -> b l r g n", r=self.mimo_rank, g=1) | |
| C = rearrange(C, "b l (r g n) -> b l r g n", r=self.mimo_rank, g=1) | |
| A = torch.clamp(-F.softplus(dd_A.to(torch.float32)), max=-1e-4) | |
| DT = F.softplus(dd_dt.to(torch.float32) + self.dt_bias) | |
| alpha = torch.exp(A * DT) | |
| gate = torch.sigmoid(trap.to(torch.float32)) | |
| beta = (1.0 - gate) * DT * alpha | |
| gamma = gate * DT | |
| k = self.B_norm(B).expand(-1, -1, -1, self.nheads, -1).to(torch.float32) | |
| q = self.C_norm(C).expand(-1, -1, -1, self.nheads, -1).to(torch.float32) | |
| k = k + rearrange(self.B_bias, "h r n -> r h n") | |
| q = q + rearrange(self.C_bias, "h r n -> r h n") | |
| turns = torch.tanh(angles.to(torch.float32))[:, :, None, :] * DT[..., None] * math.pi | |
| cumulative = torch.cumsum(turns, dim=1) | |
| cos, sin = (torch.cos(cumulative)[:, :, None], torch.sin(cumulative)[:, :, None]) | |
| q = _rotate_pairs(q, cos, sin, interleaved=not self.is_mimo) | |
| k = _rotate_pairs(k, cos, sin, interleaved=not self.is_mimo) | |
| if self.is_mimo: | |
| v = torch.einsum("blhp,hrp->blrhp", x, self.mimo_x.to(torch.float32)) | |
| gates = torch.einsum("blhp,hrp->blrhp", z, self.mimo_z.to(torch.float32)) | |
| else: | |
| v, gates = (x[:, :, None], z[:, :, None]) | |
| state = u.new_zeros((batch, self.nheads, self.headdim, self.d_state), dtype=torch.float32) | |
| previous = torch.zeros_like(state) | |
| outputs = [] | |
| for t in range(seq): | |
| current = torch.einsum("brhn,brhp->bhpn", k[:, t], v[:, t]) | |
| state = ( | |
| alpha[:, t, :, None, None] * state | |
| + beta[:, t, :, None, None] * previous | |
| + gamma[:, t, :, None, None] * current | |
| ) | |
| previous = current | |
| step = torch.einsum("bhpn,brhn->brhp", state, q[:, t]) | |
| step = step + self.D[None, None, :, None] * v[:, t] | |
| step = step * F.silu(gates[:, t]) | |
| if self.is_mimo: | |
| step = torch.einsum("brhp,hrp->bhp", step, self.mimo_o.to(torch.float32)) | |
| else: | |
| step = step[:, 0] | |
| outputs.append(step) | |
| y = rearrange(torch.stack(outputs, dim=1), "b l h p -> b l (h p)") | |
| return self.out_proj(y.to(u.dtype)) | |
| class Mamba3(Mamba3Reference): | |
| """Checkpoint-compatible cell with fused CUDA inference when configured by the loader.""" | |
| fused = False | |
| def forward(self, u: Tensor) -> Tensor: | |
| if self.fused and u.is_cuda and not torch.is_grad_enabled(): | |
| from ._cuda import mamba3_forward | |
| return mamba3_forward(self, u) | |
| return super().forward(u) | |