Skip to content

Commit 4c81937

Browse files
jomitchellnvclaude
andcommitted
Guard triangle_attention against passing mask and kv_lengths together
Raise ValueError in the public wrapper when both `mask` and `kv_lengths` are supplied. They are mutually exclusive: `kv_lengths` selects the SM100f length fast path, while a dense `mask` uses the fallback path. The guard sits before the backend dispatch, so it surfaces the error early and needs no GPU. Add a GPU-free regression test. Signed-off-by: Jonathan Mitchell <jomitchell@nvidia.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9eda5d5 commit 4c81937

2 files changed

Lines changed: 23 additions & 0 deletions

File tree

cuequivariance_torch/cuequivariance_torch/primitives/triangle.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ def triangle_attention(
191191
torch.Size([1, 1, 2, 128, 128])
192192
"""
193193

194+
if mask is not None and kv_lengths is not None:
195+
raise ValueError(
196+
"triangle_attention: pass either `mask` or `kv_lengths`, not both. "
197+
"`kv_lengths` selects the SM100f length fast path; a dense `mask` uses "
198+
"the fallback path."
199+
)
200+
194201
try:
195202
from cuequivariance_ops_torch import triangle_attention as f
196203
except Exception:

cuequivariance_torch/tests/primitives/triangle_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# limitations under the License.
1515
import math
1616

17+
import pytest
1718
import torch
1819

1920
import cuequivariance_torch as cuet
@@ -144,6 +145,21 @@ def test_triangle_attention_kv_lengths():
144145
torch.testing.assert_close(output[zero_rows], torch.zeros_like(output[zero_rows]))
145146

146147

148+
def test_triangle_attention_mask_and_kv_lengths_mutually_exclusive():
149+
# kv_lengths selects the SM100f length fast path; a dense mask uses the
150+
# fallback. Passing both is contradictory and must raise. The guard lives in
151+
# the public wrapper, before any backend dispatch, so this needs no GPU.
152+
n = 8
153+
q = torch.zeros(1, n, 1, n, 8)
154+
k = torch.zeros(1, n, 1, n, 8)
155+
v = torch.zeros(1, n, 1, n, 8)
156+
bias = torch.zeros(1, 1, 1, n, n)
157+
mask = torch.ones(1, n, 1, 1, n, dtype=torch.bool)
158+
kv_lengths = torch.full((1, n, 1, 1, 1), n, dtype=torch.int32)
159+
with pytest.raises(ValueError, match="not both"):
160+
cuet.triangle_attention(q, k, v, bias, mask=mask, kv_lengths=kv_lengths)
161+
162+
147163
def test_triangle_multiplicative_update():
148164
if torch.cuda.is_available():
149165
device = torch.device("cuda")

0 commit comments

Comments
 (0)