| """ |
| Backward / autograd parity for the CUDA causal TTT path. |
| |
| Validates that `ttt_cuda.causal_ttt` (CUDA forward + recompute backward) produces |
| gradients matching the pure-torch reference `causal_block_fast_weight_swish_glu`, |
| and that torch.autograd.gradcheck passes on the fp64 fallback path. |
| |
| Run (GPU; timing NOT measured, shared card OK): |
| TORCHDYNAMO_DISABLE=1 PYTHONPATH="$PWD" \ |
| /mnt/afs-h200/yuyangcheng/venvs/fla_triton32/bin/python -m src.models.test_ttt_cuda_backward |
| """ |
| import os |
| import sys |
|
|
| import torch |
|
|
| os.environ.setdefault("TORCHDYNAMO_DISABLE", "1") |
| try: |
| torch._dynamo.config.suppress_errors = True |
| except Exception: |
| pass |
|
|
| from src.models.ttt import causal_block_fast_weight_swish_glu |
| from src.models import ttt_cuda |
|
|
|
|
| def _inputs(B, L, d, dh, T, dtype, device, seed=0, req=True): |
| g = torch.Generator(device=device).manual_seed(seed) |
| rk = lambda *s: torch.randn(*s, generator=g, device=device, dtype=dtype).requires_grad_(req) |
| w0 = rk(B, d, dh); w1 = rk(B, dh, d); w2 = rk(B, d, dh) |
| q = rk(B, L, d); k = rk(B, L, d); v = rk(B, L, d) |
| lr0 = (torch.rand(B, L, 1, generator=g, device=device, dtype=torch.float32) * 0.02 + 0.001) |
| lr1 = (torch.rand(B, L, 1, generator=g, device=device, dtype=torch.float32) * 0.02 + 0.001) |
| lr2 = (torch.rand(B, L, 1, generator=g, device=device, dtype=torch.float32) * 0.02 + 0.001) |
| ck = rk(B, T, d); cv = rk(B, T, d) |
| cl0 = (torch.rand(B, T, 1, generator=g, device=device, dtype=torch.float32) * 0.02 + 0.001) |
| cl1 = (torch.rand(B, T, 1, generator=g, device=device, dtype=torch.float32) * 0.02 + 0.001) |
| cl2 = (torch.rand(B, T, 1, generator=g, device=device, dtype=torch.float32) * 0.02 + 0.001) |
| return [w0, w1, w2, q, k, v, lr0, lr1, lr2, ck, cv, cl0, cl1, cl2] |
|
|
|
|
| def main(): |
| if not torch.cuda.is_available(): |
| print("SKIP: no CUDA"); return 0 |
| dev = "cuda" |
| results = [] |
|
|
| |
| for cs in (256, 64): |
| ins = _inputs(B=8, L=128, d=64, dh=64, T=16, dtype=torch.float32, device=dev, seed=3) |
| w0, w1, w2, q, k, v, lr0, lr1, lr2, ck, cv, cl0, cl1, cl2 = ins |
|
|
| |
| ref_ins = [t.detach().clone().requires_grad_(t.requires_grad) for t in ins] |
| ro = causal_block_fast_weight_swish_glu( |
| ref_ins[0], ref_ins[1], ref_ins[2], ref_ins[3], ref_ins[4], ref_ins[5], |
| ref_ins[6], ref_ins[7], ref_ins[8], chunk_size=cs, muon_update_steps=0, |
| vlm_k=ref_ins[9], vlm_v=ref_ins[10], vlm_lr0=ref_ins[11], |
| vlm_lr1=ref_ins[12], vlm_lr2=ref_ins[13])[0] |
| ro.sum().backward() |
|
|
| |
| co = ttt_cuda.causal_ttt( |
| ins[0], ins[1], ins[2], ins[3], ins[4], ins[5], ins[6], ins[7], ins[8], |
| chunk_size=cs, vlm_k=ins[9], vlm_v=ins[10], |
| vlm_lr0=ins[11], vlm_lr1=ins[12], vlm_lr2=ins[13])[0] |
| co.sum().backward() |
|
|
| |
| |
| |
| names = ["w0", "w1", "w2", "q", "k", "v"] |
| max_gerr = 0.0 |
| for nm, a, b in zip(names, ref_ins[:6], ins[:6]): |
| if a.grad is None or b.grad is None: |
| continue |
| e = (a.grad.float() - b.grad.float()).abs().max().item() |
| mag = a.grad.float().abs().max().item() + 1e-9 |
| max_gerr = max(max_gerr, e / mag) |
| ok = max_gerr < 5e-3 |
| print(f"[{'OK ' if ok else 'BAD'}] grad-parity cs={cs:3d} | max grad err={max_gerr:.2e} (tol=5e-3)") |
| results.append(ok) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ins = _inputs(B=6, L=96, d=64, dh=64, T=12, dtype=torch.float32, device=dev, seed=7) |
| ref_ins = [t.detach().clone().requires_grad_(t.requires_grad) for t in ins] |
| target = torch.randn(6, 96, 64, device=dev) |
| ro = causal_block_fast_weight_swish_glu( |
| ref_ins[0], ref_ins[1], ref_ins[2], ref_ins[3], ref_ins[4], ref_ins[5], |
| ref_ins[6], ref_ins[7], ref_ins[8], chunk_size=128, muon_update_steps=0, |
| vlm_k=ref_ins[9], vlm_v=ref_ins[10], vlm_lr0=ref_ins[11], |
| vlm_lr1=ref_ins[12], vlm_lr2=ref_ins[13])[0] |
| ((ro - target) ** 2).mean().backward() |
| co = ttt_cuda.causal_ttt( |
| ins[0], ins[1], ins[2], ins[3], ins[4], ins[5], ins[6], ins[7], ins[8], |
| chunk_size=128, vlm_k=ins[9], vlm_v=ins[10], |
| vlm_lr0=ins[11], vlm_lr1=ins[12], vlm_lr2=ins[13])[0] |
| ((co - target) ** 2).mean().backward() |
| max_gerr = 0.0 |
| for a, b in zip(ref_ins[:6], ins[:6]): |
| if a.grad is None or b.grad is None: |
| continue |
| e = (a.grad.float() - b.grad.float()).abs().max().item() |
| mag = a.grad.float().abs().max().item() + 1e-9 |
| max_gerr = max(max_gerr, e / mag) |
| ok = max_gerr < 5e-3 |
| print(f"[{'OK ' if ok else 'BAD'}] grad-parity (mse loss) | max rel grad err={max_gerr:.2e} (tol=5e-3)") |
| results.append(ok) |
|
|
| allok = all(results) |
| print("\n=== BACKWARD PARITY", "PASS ===" if allok else "FAIL ===") |
| return 0 if allok else 1 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|