Skip to content

Commit 9ce291b

Browse files
Merge pull request #4305 from huytransformer:htn/fix-jax-splash-soft-cap
PiperOrigin-RevId: 940575479
2 parents 48c6b24 + c479d48 commit 9ce291b

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

src/maxtext/kernels/attention/jax_flash_attention.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,10 @@ def compute_attention_block(output, l, m):
159159
(batch_size, num_kv_heads, q_groups, block_q, block_kv),
160160
)
161161

162-
s_i_j = jnp.where(broadcasted_mask, s_i_j, mask_value)
163162
if cap is not None:
164163
s_i_j = jnp.tanh(s_i_j / cap)
165164
s_i_j = s_i_j * cap
165+
s_i_j = jnp.where(broadcasted_mask, s_i_j, mask_value)
166166
m_i_j = s_i_j.max(axis=-1)
167167
p_i_j = jnp.exp(s_i_j - m_i_j[..., None])
168168
l_i_j = p_i_j.sum(axis=-1)

src/maxtext/layers/attention_op.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,7 @@ def kernel_fn(q, k, v, d, s):
15001500
block_q=self.block_q,
15011501
mask=materialized_mask,
15021502
mask_value=DEFAULT_MASK_VALUE,
1503+
cap=attn_logits_soft_cap,
15031504
)
15041505
if record_max_logits:
15051506
# The native JAX splash attention implementation does not currently expose the softmax statistics

tests/unit/attention_test.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
)
4949
from maxtext.layers.attentions import Attention
5050
from maxtext.layers import embeddings
51+
from maxtext.kernels.attention import jax_flash_attention
5152
from maxtext.configs import pyconfig
5253
from maxtext.models.qwen3 import Qwen3NextGatedDeltaNet
5354
import numpy as np
@@ -57,6 +58,41 @@
5758
from tests.utils.test_helpers import get_test_config_path
5859

5960

61+
class JaxFlashAttentionTest(unittest.TestCase):
62+
"""Tests for JAX flash attention."""
63+
64+
def test_flash_attention_block_masked_soft_cap(self):
65+
cap = 1.0
66+
mask_value = -1.0e9
67+
query = jnp.array([[[[10.0], [10.0]]]], dtype=jnp.float32)
68+
key = jnp.array([[[[10.0], [0.0]]]], dtype=jnp.float32)
69+
value = jnp.array([[[[1.0], [3.0]]]], dtype=jnp.float32)
70+
mask = jnp.array([[True, True], [True, False]])
71+
72+
output = jax_flash_attention.flash_attention_block_masked(
73+
query,
74+
key,
75+
value,
76+
segment_ids=None,
77+
block_kv=2,
78+
block_q=1,
79+
mask=mask,
80+
mask_value=mask_value,
81+
cap=cap,
82+
)
83+
84+
logits = jnp.einsum("bhqd,bhkd->bhqk", query, key)
85+
logits = jnp.tanh(logits / cap) * cap
86+
logits = jnp.where(mask[None, None, :, :], logits, mask_value)
87+
expected = jnp.einsum("bhqk,bhkd->bhqd", jax.nn.softmax(logits, axis=-1), value)
88+
np.testing.assert_allclose(
89+
np.asarray(output),
90+
np.asarray(expected),
91+
rtol=1e-6,
92+
atol=1e-6,
93+
)
94+
95+
6096
class SplashLocalMaskTest(unittest.TestCase):
6197
"""Tests for Splash local masks."""
6298

0 commit comments

Comments
 (0)