Files
unslothai--unsloth/tests/utils/test_attention_masks.py
T
wehub-resource-sync e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

288 lines
7.7 KiB
Python

# Copyright 2023-present Daniel Han-Chen, Michael Han-Chen & the Unsloth team. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Unit tests for packed-attention mask helpers with sliding-window logic."""
import math
import torch
from unsloth.utils import attention_dispatch
from unsloth.utils import packing as packing_utils
def _make_seq_info(lengths):
lengths = torch.tensor(lengths, dtype = torch.int32)
cu = torch.cat(
[
torch.zeros(1, dtype = torch.int32),
torch.cumsum(lengths, dim = 0, dtype = torch.int32),
]
)
max_len = int(lengths.max().item())
return lengths, cu, max_len
def test_sdpa_packed_attention_mask_sliding_window():
seq_info = _make_seq_info([5, 3])
mask = packing_utils.build_sdpa_packed_attention_mask(
seq_info,
dtype = torch.float32,
device = torch.device("cpu"),
sliding_window = 3,
)
assert mask.shape == (1, 1, 8, 8)
block_first = mask[0, 0, :5, :5]
upper = torch.triu(torch.ones_like(block_first), diagonal = 1).bool()
assert torch.all(block_first[upper] == float("-inf"))
assert block_first[3, 0].item() == float("-inf")
assert block_first[4, 1].item() == float("-inf")
assert block_first[4, 2].item() > -math.inf
assert mask[0, 0, 0, 6].item() == float("-inf")
def test_xformers_block_mask_sliding_window(monkeypatch):
class _FakeMask:
def __init__(
self,
lengths,
window = None,
):
self.lengths = lengths
self.window = window
@classmethod
def from_seqlens(cls, lengths):
return cls(tuple(lengths))
def make_local_attention(self, window_size):
return _FakeMask(self.lengths, window = window_size)
monkeypatch.setattr(packing_utils, "_XFormersBlockMask", _FakeMask, raising = False)
seq_info = _make_seq_info([4, 4])
mask = packing_utils.build_xformers_block_causal_mask(
seq_info,
sliding_window = 2,
)
assert isinstance(mask, _FakeMask)
assert mask.window == 2
def test_run_attention_sdpa_passes_sliding_window(monkeypatch):
seq_info = _make_seq_info([3, 2])
sliding_window = 2
original_builder = attention_dispatch.build_sdpa_packed_attention_mask
captured = {}
def _capture_builder(
seq_info_arg,
*,
dtype,
device,
sliding_window = None,
):
captured["window"] = sliding_window
return original_builder(
seq_info_arg,
dtype = dtype,
device = device,
sliding_window = sliding_window,
)
monkeypatch.setattr(
attention_dispatch,
"build_sdpa_packed_attention_mask",
_capture_builder,
)
def _fake_sdpa(Q, K, V, **kwargs):
captured["mask"] = kwargs.get("attn_mask")
return torch.zeros_like(Q)
monkeypatch.setattr(attention_dispatch, "scaled_dot_product_attention", _fake_sdpa)
config = attention_dispatch.AttentionConfig(
backend = attention_dispatch.SDPA,
n_kv_heads = 1,
n_groups = 1,
)
context = attention_dispatch.AttentionContext(
bsz = 1,
q_len = 5,
kv_seq_len = 5,
n_heads = 1,
head_dim = 1,
requires_grad = False,
seq_info = seq_info,
attention_mask = None,
causal_mask = None,
sliding_window = sliding_window,
)
Q = torch.zeros(1, 1, 5, 1)
K = torch.zeros_like(Q)
V = torch.zeros_like(Q)
attention_dispatch.run_attention(
config = config,
context = context,
Q = Q,
K = K,
V = V,
)
assert captured["window"] == sliding_window
mask = captured["mask"]
assert mask is not None and mask.shape == (1, 1, 5, 5)
assert mask[0, 0, 4, 1].item() == float("-inf")
def test_run_attention_xformers_passes_sliding_window(monkeypatch):
seq_info = _make_seq_info([4])
sliding_window = 3
class _FakeBias:
pass
captured = {}
def _fake_builder(
seq_info_arg,
*,
sliding_window = None,
base_mask = None,
):
captured["window"] = sliding_window
captured["base"] = base_mask
return _FakeBias()
def _fake_attention(
Q,
K,
V,
attn_bias = None,
**_,
):
captured["bias"] = attn_bias
return torch.zeros_like(Q)
monkeypatch.setattr(attention_dispatch, "build_xformers_block_causal_mask", _fake_builder)
monkeypatch.setattr(attention_dispatch, "xformers_attention", _fake_attention, raising = False)
monkeypatch.setattr(attention_dispatch, "XFORMERS_BLOCK_DIAG_CLS", _FakeBias, raising = False)
config = attention_dispatch.AttentionConfig(
backend = attention_dispatch.XFORMERS,
n_kv_heads = 1,
n_groups = 1,
)
context = attention_dispatch.AttentionContext(
bsz = 1,
q_len = 4,
kv_seq_len = 4,
n_heads = 1,
head_dim = 1,
requires_grad = False,
seq_info = seq_info,
attention_mask = None,
causal_mask = None,
sliding_window = sliding_window,
)
Q = torch.zeros(1, 1, 4, 1)
K = torch.zeros_like(Q)
V = torch.zeros_like(Q)
attention_dispatch.run_attention(
config = config,
context = context,
Q = Q,
K = K,
V = V,
)
assert captured["window"] == sliding_window
assert isinstance(captured["bias"], _FakeBias)
def test_run_attention_flash_varlen_receives_window_and_softcap(monkeypatch):
seq_info = _make_seq_info([4])
sliding_window = 3
softcap = 0.5
window_tuple = (sliding_window, sliding_window)
captured = {}
def _fake_flash_varlen(Q, K, V, cu_q, cu_k, max_q, max_k, **kwargs):
captured["kwargs"] = kwargs
return torch.zeros_like(Q)
monkeypatch.setattr(
attention_dispatch,
"flash_attn_varlen_func",
_fake_flash_varlen,
)
monkeypatch.setattr(attention_dispatch, "HAS_FLASH_ATTENTION", True)
config = attention_dispatch.AttentionConfig(
backend = attention_dispatch.FLASH_VARLEN,
n_kv_heads = 1,
n_groups = 1,
flash_varlen_kwargs = {
"dropout_p": 0.0,
"softmax_scale": 1.0,
"causal": True,
"softcap": softcap,
"window_size": window_tuple,
},
)
context = attention_dispatch.AttentionContext(
bsz = 1,
q_len = 4,
kv_seq_len = 4,
n_heads = 1,
head_dim = 2,
requires_grad = False,
seq_info = seq_info,
attention_mask = None,
causal_mask = None,
sliding_window = sliding_window,
)
Q = torch.zeros(1, 1, 4, 2)
K = torch.zeros_like(Q)
V = torch.zeros_like(Q)
attention_dispatch.run_attention(
config = config,
context = context,
Q = Q,
K = K,
V = V,
)
assert captured["kwargs"]["softcap"] == softcap
assert captured["kwargs"]["window_size"] == window_tuple
"""Unit tests for packed-attention mask helpers with sliding-window logic."""