Module neuralops.tests.test_triton_state_update
Comprehensive test suite for neuralops.core.triton_kernels.state_update.
Coverage: - Numerical correctness vs PyTorch SSM recurrence reference - Multiple dtypes: fp32, fp16, bf16 - Multiple shapes: batch, seq, state dimension - Edge cases: initial state provided vs None, D feedthrough present vs None - Reset behavior and sequential carry-over - Fallback path when Triton unavailable - Determinism
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_ssm_recurrence_pytorch(u, delta, A, B, C, D=None, initial_state=None)-
Expand source code
def reference_ssm_recurrence_pytorch(u, delta, A, B, C, D=None, initial_state=None): """Pure PyTorch reference implementation of continuous SSM recurrence.""" B_sz, L_sz, D_sz = u.shape N_sz = A.shape[-1] if initial_state is not None: state = initial_state.clone() else: state = torch.zeros(B_sz, D_sz, N_sz, device=u.device, dtype=torch.float32) delta_c = delta.expand(B_sz, L_sz, D_sz) if delta.dim() == 2 or delta.shape[-1] != D_sz else delta b_c = B.expand(B_sz, L_sz, N_sz) if B.dim() == 2 else B c_c = C.expand(B_sz, L_sz, N_sz) if C.dim() == 2 else C y = torch.zeros(B_sz, L_sz, D_sz, device=u.device, dtype=torch.float32) u_f = u.float() delta_f = delta_c.float() A_f = A.float() B_f = b_c.float() C_f = c_c.float() for t in range(L_sz): # discrete dA = exp(delta_t * A) d_t = delta_f[:, t, :, None] # [B, D, 1] dA = torch.exp(d_t * A_f) # [B, D, N] dB = d_t * B_f[:, t, None, :] # [B, D, N] u_t = u_f[:, t, :, None] # [B, D, 1] state = state * dA + dB * u_t # y_t = state @ C_t y[:, t, :] = torch.sum(state * C_f[:, t, None, :], dim=-1) if D is not None: d_val = D.float().view(1, 1, D_sz) if D.dim() <= 2 else D.float() y = y + u_f * d_val return y.to(u.dtype), state.to(u.dtype)Pure PyTorch reference implementation of continuous SSM recurrence.
def test_state_carryover(device)-
Expand source code
def test_state_carryover(device): """Running step 1 then step 2 with state carry-over matches full sequence.""" B, L, D_sz, N_sz = 2, 16, 16, 8 u = torch.randn(B, L, D_sz, device=device, dtype=torch.float32) delta = torch.randn(B, L, D_sz, device=device, dtype=torch.float32).sigmoid() * 0.1 A = -torch.rand(D_sz, N_sz, device=device, dtype=torch.float32) B_t = torch.randn(B, L, N_sz, device=device, dtype=torch.float32) C_t = torch.randn(B, L, N_sz, device=device, dtype=torch.float32) # Full sequence run y_full, state_full = fwd(u, delta, A, B_t, C_t) # Split into 2 chunks of 8 y_part1, state_mid = fwd(u[:, :8], delta[:, :8], A, B_t[:, :8], C_t[:, :8]) y_part2, state_end = fwd(u[:, 8:], delta[:, 8:], A, B_t[:, 8:], C_t[:, 8:], state=state_mid) assert torch.allclose(y_full[:, :8], y_part1, atol=1e-4) assert torch.allclose(y_full[:, 8:], y_part2, atol=1e-4) assert torch.allclose(state_full, state_end, atol=1e-4)Running step 1 then step 2 with state carry-over matches full sequence.
def test_state_update_correctness(device, dtype, batch, seq_len, d_dim, n_dim)-
Expand source code
@pytest.mark.parametrize("dtype", [torch.float32, torch.float16]) @pytest.mark.parametrize("batch", [1, 2]) @pytest.mark.parametrize("seq_len", [8, 32]) @pytest.mark.parametrize("d_dim", [16, 32]) @pytest.mark.parametrize("n_dim", [8, 16]) def test_state_update_correctness(device, dtype, batch, seq_len, d_dim, n_dim): """Verify state update recurrence matches PyTorch reference.""" u = torch.randn(batch, seq_len, d_dim, device=device, dtype=dtype) delta = torch.randn(batch, seq_len, d_dim, device=device, dtype=dtype).sigmoid() * 0.1 A = -torch.rand(d_dim, n_dim, device=device, dtype=dtype) B = torch.randn(batch, seq_len, n_dim, device=device, dtype=dtype) C = torch.randn(batch, seq_len, n_dim, device=device, dtype=dtype) D = torch.randn(d_dim, device=device, dtype=dtype) y, next_state = fwd(u, delta, A, B, C, D=D) y_ref, state_ref = reference_ssm_recurrence_pytorch(u, delta, A, B, C, D=D) assert y.shape == (batch, seq_len, d_dim) assert next_state.shape == (batch, d_dim, n_dim) assert torch.allclose(y, y_ref, atol=1e-2, rtol=1e-2) assert torch.allclose(next_state, state_ref, atol=1e-2, rtol=1e-2)Verify state update recurrence matches PyTorch reference.
def test_state_update_cpu_fallback()-
Expand source code
def test_state_update_cpu_fallback(): """Verify PyTorch fallback works seamlessly on CPU.""" u = torch.randn(2, 8, 16, device="cpu") delta = torch.randn(2, 8, 16, device="cpu").sigmoid() * 0.1 A = -torch.rand(16, 8, device="cpu") B = torch.randn(2, 8, 8, device="cpu") C = torch.randn(2, 8, 8, device="cpu") y, state = state_update_pytorch_fallback(u, delta, A, B, C) assert y.shape == (2, 8, 16) assert state.shape == (2, 16, 8) assert torch.isfinite(y).all() assert torch.isfinite(state).all()Verify PyTorch fallback works seamlessly on CPU.
def test_state_update_single_token(device)-
Expand source code
def test_state_update_single_token(device): """Single token sequence (seq_len = 1).""" u = torch.randn(1, 1, 16, device=device) delta = torch.randn(1, 1, 16, device=device).sigmoid() A = -torch.rand(16, 8, device=device) B = torch.randn(1, 1, 8, device=device) C = torch.randn(1, 1, 8, device=device) y, state = fwd(u, delta, A, B, C) assert y.shape == (1, 1, 16) assert state.shape == (1, 16, 8)Single token sequence (seq_len = 1).