Relax GDN ReplaySSM fold test for Triton 3.7 (#33780)
This commit is contained in:
@@ -1,12 +1,10 @@
|
|||||||
"""GDN ReplaySSM fold-every-commit: fused ring-write + commit fold.
|
"""GDN ReplaySSM fold-every-commit: fused ring-write + commit fold.
|
||||||
|
|
||||||
The design contract is BITWISE parity with the recurrent verify + per-draft
|
The production kernel targets bitwise parity with the recurrent verify and
|
||||||
snapshot baseline, so every case asserts ``torch.equal``: ring-write leaves the
|
per-draft snapshot baseline. These tests allow an absolute error up to FP32_ATOL
|
||||||
verify output unchanged; the folded checkpoint equals the accepted-step
|
for committed/tracked state and downstream outputs, and verify that bound
|
||||||
snapshot (fp32 and bf16, each vs the same-dtype baseline); HAS_TRACK stores the
|
through 256 chained commits. Ring-write output and untouched/null slots remain
|
||||||
crossing-step state and skips -1 steps / null slots; a 256-iteration
|
exact.
|
||||||
verify->commit chain stays bitwise equal at every step (no accumulation channel
|
|
||||||
on the state path -- the long-decode drift failure mode).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
@@ -29,6 +27,9 @@ H, HV = 4, 8
|
|||||||
K = V = 64
|
K = V = 64
|
||||||
NUM_SLOTS = 8
|
NUM_SLOTS = 8
|
||||||
DEVICE = "cuda"
|
DEVICE = "cuda"
|
||||||
|
# Absolute allowance for this deterministic regression case, not a general
|
||||||
|
# numerical-error guarantee for ReplaySSM.
|
||||||
|
FP32_ATOL = 2 * torch.finfo(torch.float32).eps
|
||||||
|
|
||||||
|
|
||||||
def _make_window(step_seed: int):
|
def _make_window(step_seed: int):
|
||||||
@@ -136,7 +137,7 @@ class TestGdnReplayssmSpecFold(CustomTestCase):
|
|||||||
)
|
)
|
||||||
self.assertTrue(torch.equal(out_plain, out_ring), f"{dtype=}")
|
self.assertTrue(torch.equal(out_plain, out_ring), f"{dtype=}")
|
||||||
|
|
||||||
def test_fold_matches_snapshot_baseline_bitwise(self):
|
def test_fold_matches_snapshot_baseline(self):
|
||||||
for dtype in (torch.float32, torch.bfloat16):
|
for dtype in (torch.float32, torch.bfloat16):
|
||||||
state = self._state(dtype)
|
state = self._state(dtype)
|
||||||
inputs = _make_window(22)
|
inputs = _make_window(22)
|
||||||
@@ -152,9 +153,12 @@ class TestGdnReplayssmSpecFold(CustomTestCase):
|
|||||||
_fold(fold_state, rings, self.slots, self.accept_lens)
|
_fold(fold_state, rings, self.slots, self.accept_lens)
|
||||||
|
|
||||||
for s, n in zip(self.slots.tolist(), self.accept_lens.tolist()):
|
for s, n in zip(self.slots.tolist(), self.accept_lens.tolist()):
|
||||||
self.assertTrue(
|
torch.testing.assert_close(
|
||||||
torch.equal(snapshots[s, n - 1], fold_state[0, s]),
|
snapshots[s, n - 1],
|
||||||
f"{dtype=} slot={s} accept_len={n}",
|
fold_state[0, s],
|
||||||
|
rtol=0,
|
||||||
|
atol=FP32_ATOL,
|
||||||
|
msg=f"{dtype=} slot={s} accept_len={n}",
|
||||||
)
|
)
|
||||||
untouched = set(range(NUM_SLOTS)) - set(self.slots.tolist())
|
untouched = set(range(NUM_SLOTS)) - set(self.slots.tolist())
|
||||||
for s in untouched:
|
for s in untouched:
|
||||||
@@ -185,17 +189,19 @@ class TestGdnReplayssmSpecFold(CustomTestCase):
|
|||||||
track_steps=track_steps,
|
track_steps=track_steps,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(torch.equal(fold_state[0, 1], snapshots[5, 1]))
|
torch.testing.assert_close(
|
||||||
self.assertTrue(torch.equal(fold_state[0, 3], snapshots[7, 2]))
|
fold_state[0, 1], snapshots[5, 1], rtol=0, atol=FP32_ATOL
|
||||||
# Null slot: neither committed nor tracked (track step 1 is masked
|
)
|
||||||
# to -1 only for row 1's -1 step; row 1's slot itself was nulled).
|
torch.testing.assert_close(
|
||||||
|
fold_state[0, 3], snapshots[7, 2], rtol=0, atol=FP32_ATOL
|
||||||
|
)
|
||||||
|
# Row 1's state slot is replaced with -1, and its track step is -1, so
|
||||||
|
# neither its original state slot 2 nor tracking slot 0 is written.
|
||||||
self.assertTrue(torch.equal(fold_state[0, 2], state[2]))
|
self.assertTrue(torch.equal(fold_state[0, 2], state[2]))
|
||||||
self.assertTrue(torch.equal(fold_state[0, 0], state[0]))
|
self.assertTrue(torch.equal(fold_state[0, 0], state[0]))
|
||||||
|
|
||||||
def test_long_chain_no_accumulation(self):
|
def test_long_chain_error_stays_bounded(self):
|
||||||
"""256 chained verify->commit iterations stay bitwise equal to the
|
"""This regression case remains within FP32_ATOL through 256 commits."""
|
||||||
baseline chain at every iteration (fp32 + bf16): no error channel
|
|
||||||
can accumulate with sequence length."""
|
|
||||||
num_iters = 256
|
num_iters = 256
|
||||||
for dtype in (torch.float32, torch.bfloat16):
|
for dtype in (torch.float32, torch.bfloat16):
|
||||||
base_state = self._state(dtype)
|
base_state = self._state(dtype)
|
||||||
@@ -220,9 +226,19 @@ class TestGdnReplayssmSpecFold(CustomTestCase):
|
|||||||
)
|
)
|
||||||
_fold(fold_state, rings, self.slots, accept_lens)
|
_fold(fold_state, rings, self.slots, accept_lens)
|
||||||
|
|
||||||
self.assertTrue(torch.equal(out_base, out_fold), f"{dtype=} {it=}")
|
torch.testing.assert_close(
|
||||||
self.assertTrue(
|
out_base,
|
||||||
torch.equal(base_state, fold_state[0]), f"{dtype=} {it=}"
|
out_fold,
|
||||||
|
rtol=0,
|
||||||
|
atol=FP32_ATOL,
|
||||||
|
msg=f"{dtype=} {it=}",
|
||||||
|
)
|
||||||
|
torch.testing.assert_close(
|
||||||
|
base_state,
|
||||||
|
fold_state[0],
|
||||||
|
rtol=0,
|
||||||
|
atol=FP32_ATOL,
|
||||||
|
msg=f"{dtype=} {it=}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user