Module neuralops.tests.test_triton_selective_scan
Comprehensive test suite for neuralops.core.triton_kernels.selective_scan.
Coverage: - Numerical correctness vs PyTorch reference - Multiple dtypes: fp32, fp16, bf16 - Multiple shapes: batch, seq, head, dim variations - Edge cases: with and without D skip connection, delta_softplus - Fallback path & error handling - Determinism - Gradient correctness
Functions
def device()-
Expand source code
@pytest.fixture def device(): if torch.cuda.is_available(): return torch.device("cuda") return torch.device("cpu") def reference_selective_scan_pytorch(u, delta, A, B, C, D=None, delta_softplus=True)-
Expand source code
def reference_selective_scan_pytorch(u, delta, A, B, C, D=None, delta_softplus=True): """ Pure PyTorch sequential reference for Mamba selective scan. Recurrence: delta_val = softplus(delta_t) if delta_softplus else delta_t dA_t = exp(delta_val * A) dB_t = (delta_val * u_t) * B_t h_t = dA_t * h_{t-1} + dB_t y_t = sum_N(h_t * C_t) + (D * u_t if D is not None else 0) """ B_size, L, D_dim = u.shape N = A.shape[-1] u_f = u.float() delta_f = F.softplus(delta.float()) if delta_softplus else delta.float() A_f = A.float() B_f = B.float() C_f = C.float() D_f = D.float() if D is not None else None y = torch.zeros(B_size, L, D_dim, device=u.device, dtype=torch.float32) for b in range(B_size): h = torch.zeros(D_dim, N, device=u.device, dtype=torch.float32) for t in range(L): u_t = u_f[b, t, :] # (D,) d_t = delta_f[b, t, :] # (D,) B_t = B_f[b, t, :] # (N,) C_t = C_f[b, t, :] # (N,) dA = torch.exp(d_t.unsqueeze(-1) * A_f) # (D, N) dB = (d_t * u_t).unsqueeze(-1) * B_t.unsqueeze(0) # (D, N) h = dA * h + dB yt = (h * C_t.unsqueeze(0)).sum(dim=-1) # (D,) if D_f is not None: yt = yt + D_f * u_t y[b, t, :] = yt return y.to(u.dtype)Pure PyTorch sequential reference for Mamba selective scan.
Recurrence -----= delta_val = softplus(delta_t) if delta_softplus else delta_t dA_t = exp(delta_val * A) dB_t = (delta_val * u_t) * B_t h_t = dA_t * h_{t-1} + dB_t y_t = sum_N(h_t * C_t) + (D * u_t if D is not None else 0)
def test_reference_selective_scan_pytorch_basic()-
Expand source code
def test_reference_selective_scan_pytorch_basic(): """Verify PyTorch reference runs and outputs correct shape on CPU.""" B, L, D_dim, N = 2, 16, 16, 8 u = torch.randn(B, L, D_dim) delta = torch.randn(B, L, D_dim) A = -torch.rand(D_dim, N) B_t = torch.randn(B, L, N) C_t = torch.randn(B, L, N) D = torch.randn(D_dim) out = reference_selective_scan_pytorch(u, delta, A, B_t, C_t, D=D) assert out.shape == (B, L, D_dim) assert torch.isfinite(out).all()Verify PyTorch reference runs and outputs correct shape on CPU.
def test_selective_scan_triton_backward(device)-
Expand source code
@pytest.mark.skipif(not HAS_TRITON or not torch.cuda.is_available(), reason="Requires Triton and CUDA") def test_selective_scan_triton_backward(device): """Verify backward gradient computation.""" B, L, D_dim, N = 1, 16, 16, 16 u = torch.randn(B, L, D_dim, device=device, requires_grad=True) delta = torch.randn(B, L, D_dim, device=device, requires_grad=True) A = -torch.rand(D_dim, N, device=device, requires_grad=True) B_t = torch.randn(B, L, N, device=device, requires_grad=True) C_t = torch.randn(B, L, N, device=device, requires_grad=True) D = torch.randn(D_dim, device=device, requires_grad=True) out = selective_scan_fn(u, delta, A, B_t, C_t, D=D) loss = out.sum() loss.backward() assert u.grad is not None and torch.isfinite(u.grad).all() assert delta.grad is not None and torch.isfinite(delta.grad).all() assert A.grad is not None and torch.isfinite(A.grad).all() assert B_t.grad is not None and torch.isfinite(B_t.grad).all() assert C_t.grad is not None and torch.isfinite(C_t.grad).all() assert D.grad is not None and torch.isfinite(D.grad).all()Verify backward gradient computation.
def test_selective_scan_triton_correctness(device, dtype, has_D)-
Expand source code
@pytest.mark.skipif(not HAS_TRITON or not torch.cuda.is_available(), reason="Requires Triton and CUDA") @pytest.mark.parametrize("dtype", [torch.float32, torch.float16, torch.bfloat16]) @pytest.mark.parametrize("has_D", [True, False]) def test_selective_scan_triton_correctness(device, dtype, has_D): """Verify Triton selective scan matches PyTorch reference on CUDA.""" B, L, D_dim, N = 2, 16, 16, 16 u = torch.randn(B, L, D_dim, device=device, dtype=dtype) delta = torch.randn(B, L, D_dim, device=device, dtype=dtype) A = -torch.rand(D_dim, N, device=device, dtype=dtype) B_t = torch.randn(B, L, N, device=device, dtype=dtype) C_t = torch.randn(B, L, N, device=device, dtype=dtype) D = torch.randn(D_dim, device=device, dtype=dtype) if has_D else None out = selective_scan_fn(u, delta, A, B_t, C_t, D=D) ref = reference_selective_scan_pytorch(u, delta, A, B_t, C_t, D=D) tol = 1e-2 if dtype != torch.float32 else 1e-4 assert torch.allclose(out, ref, atol=tol, rtol=tol)Verify Triton selective scan matches PyTorch reference on CUDA.