Uploaded using `kernel-builder`.

#8
by kernels-bot - opened
This view is limited to 50 files because it contains too many changes. See the raw diff here.
Files changed (50) hide show
  1. README.md +112 -0
  2. build/torch210-cu126-aarch64-linux/__init__.py +180 -0
  3. build/torch210-cu126-aarch64-linux/_environment.py +59 -0
  4. build/torch210-cu126-aarch64-linux/_libnatten/__init__.py +109 -0
  5. build/torch210-cu126-aarch64-linux/_libnatten/torch_wrappers.py +1006 -0
  6. build/torch210-cu126-aarch64-linux/_natten_cuda_5fe9dbb.abi3.so +3 -0
  7. build/torch210-cu126-aarch64-linux/_ops.py +9 -0
  8. build/torch210-cu126-aarch64-linux/_private_for_testing.py +33 -0
  9. build/torch210-cu126-aarch64-linux/_types.py +85 -0
  10. build/torch210-cu126-aarch64-linux/attn_merge.py +292 -0
  11. build/torch210-cu126-aarch64-linux/backends/__init__.py +254 -0
  12. build/torch210-cu126-aarch64-linux/backends/blackwell_fmha.py +254 -0
  13. build/torch210-cu126-aarch64-linux/backends/blackwell_fna.py +500 -0
  14. build/torch210-cu126-aarch64-linux/backends/configs/__init__.py +584 -0
  15. build/torch210-cu126-aarch64-linux/backends/configs/checks.py +750 -0
  16. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/__init__.py +422 -0
  17. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/backward_knobs.py +228 -0
  18. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_backward_128x128.py +304 -0
  19. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_backward_128x64.py +223 -0
  20. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_backward_64x64.py +168 -0
  21. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_forward_32x128.py +90 -0
  22. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_forward_64x128.py +82 -0
  23. build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_forward_64x64.py +63 -0
  24. build/torch210-cu126-aarch64-linux/backends/configs/cutlass_blackwell/__init__.py +391 -0
  25. build/torch210-cu126-aarch64-linux/backends/configs/cutlass_hopper/__init__.py +522 -0
  26. build/torch210-cu126-aarch64-linux/backends/configs/flex/__init__.py +210 -0
  27. build/torch210-cu126-aarch64-linux/backends/flex.py +799 -0
  28. build/torch210-cu126-aarch64-linux/backends/fmha.py +283 -0
  29. build/torch210-cu126-aarch64-linux/backends/fna.py +417 -0
  30. build/torch210-cu126-aarch64-linux/backends/hopper_fmha.py +261 -0
  31. build/torch210-cu126-aarch64-linux/backends/hopper_fna.py +512 -0
  32. build/torch210-cu126-aarch64-linux/backends/reference.py +343 -0
  33. build/torch210-cu126-aarch64-linux/context.py +231 -0
  34. build/torch210-cu126-aarch64-linux/functional.py +1151 -0
  35. build/torch210-cu126-aarch64-linux/metadata.json +84 -0
  36. build/torch210-cu126-aarch64-linux/metadata.json.sigstore +1 -0
  37. build/torch210-cu126-aarch64-linux/modules.py +449 -0
  38. build/torch210-cu126-aarch64-linux/symbols.json +1257 -0
  39. build/torch210-cu126-aarch64-linux/token_permute/__init__.py +32 -0
  40. build/torch210-cu126-aarch64-linux/token_permute/cutlass_impl.py +286 -0
  41. build/torch210-cu126-aarch64-linux/token_permute/frontend.py +137 -0
  42. build/torch210-cu126-aarch64-linux/token_permute/torch_impl.py +368 -0
  43. build/torch210-cu126-aarch64-linux/utils/__init__.py +22 -0
  44. build/torch210-cu126-aarch64-linux/utils/checks.py +726 -0
  45. build/torch210-cu126-aarch64-linux/utils/device.py +50 -0
  46. build/torch210-cu126-aarch64-linux/utils/dtype.py +36 -0
  47. build/torch210-cu126-aarch64-linux/utils/environment.py +79 -0
  48. build/torch210-cu126-aarch64-linux/utils/log.py +134 -0
  49. build/torch210-cu126-aarch64-linux/utils/tensor.py +113 -0
  50. build/torch210-cu126-aarch64-linux/utils/testing.py +149 -0
README.md ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - kernel
5
+ ---
6
+
7
+ ![Status](https://hubwebhook.dholtz.com/shield?repo=kernels-community/natten)
8
+
9
+ # NATTEN — Neighborhood Attention kernels
10
+
11
+ CUDA kernels for [NATTEN](https://natten.org) (Neighborhood Attention
12
+ Extension, [SHI-Labs/NATTEN](https://github.com/SHI-Labs/NATTEN)), packaged
13
+ for the [kernels](https://github.com/huggingface/kernels) library. Vendored
14
+ from NATTEN v0.21.7 (MIT license).
15
+
16
+ Neighborhood attention restricts each token's attention to a sliding local
17
+ window over 1-D, 2-D, or 3-D token layouts, with optional stride, dilation,
18
+ and per-dimension causal masking. This kernel ships NATTEN's fused
19
+ neighborhood attention (FNA) and FMHA kernels along with the thin functional
20
+ frontend (`na1d`, `na2d`, `na3d`, `attention`, `merge_attentions`) and its
21
+ backend/config selection logic.
22
+
23
+ ## Usage
24
+
25
+ ```python
26
+ import torch
27
+ from kernels import get_kernel
28
+
29
+ natten = get_kernel("kernels-community/natten", version=1)
30
+
31
+ # 2-D neighborhood attention over a 32x32 token layout:
32
+ # [batch, *token_layout, heads, head_dim]
33
+ q = torch.randn(1, 32, 32, 8, 64, device="cuda", dtype=torch.bfloat16)
34
+ k = torch.randn_like(q)
35
+ v = torch.randn_like(q)
36
+
37
+ out = natten.functional.na2d(q, k, v, kernel_size=(7, 7))
38
+
39
+ # 3-D, with stride/dilation/causal masking per dim:
40
+ q3 = torch.randn(1, 8, 16, 16, 8, 64, device="cuda", dtype=torch.bfloat16)
41
+ k3, v3 = torch.randn_like(q3), torch.randn_like(q3)
42
+ out3 = natten.functional.na3d(
43
+ q3, k3, v3, kernel_size=(3, 5, 5), dilation=(1, 2, 2), is_causal=(True, False, False)
44
+ )
45
+ ```
46
+
47
+ All functions are differentiable and torch.compile-compatible (the underlying
48
+ ops are registered via `TORCH_LIBRARY` with fake/meta implementations).
49
+
50
+ ## Backend / architecture support
51
+
52
+ | Backend | Compute capabilities | Notes |
53
+ |---|---|---|
54
+ | Reference CUDA kernels | 7.0 – 12.0 | correctness fallback, all dims |
55
+ | CUTLASS 2.X FNA / FMHA (`cutlass-fna`) | 7.0 – 12.0 (SM80 tensor-core paths) | default on pre-Hopper |
56
+ | Hopper FNA / FMHA (`hopper-fna`) | 9.0a | H100/H200 |
57
+ | Blackwell FNA / FMHA (`blackwell-fna`) | 10.0a | B200/GB200; requires CUDA ≥ 12.8, so cu126 builds fall back to `cutlass-fna` / reference on these GPUs |
58
+ | Flex Attention backend (`flex-fna`) | any | pure PyTorch, via `torch.nn.attention.flex_attention` |
59
+
60
+ Known gaps vs. upstream wheels:
61
+
62
+ - No SM103 (B300 / Blackwell Ultra) binaries — kernel-builder does not
63
+ currently target `10.3a`.
64
+ - SM120 (RTX 50 series) gets the CUTLASS 2.X and reference paths only, same
65
+ as upstream (NATTEN has no SM120-specific fused kernels).
66
+ - No ROCm or CPU support (upstream dropped these in v0.20).
67
+
68
+ The backend is chosen automatically per GPU; pass `backend="..."` to
69
+ `na1d`/`na2d`/`na3d` to override.
70
+
71
+ ## Developing
72
+
73
+ The ~144 kernel instantiation TUs under `csrc/autogen/` are committed
74
+ codegen output. To regenerate (e.g. when bumping the vendored NATTEN
75
+ version), run:
76
+
77
+ ```bash
78
+ scripts/regen.sh # re-runs autogen + rewrites build.toml src lists
79
+ ```
80
+
81
+ Build and test with kernel-builder:
82
+
83
+ ```bash
84
+ nix run .#build-and-copy -L
85
+ nix run .#ci-test -L
86
+ ```
87
+
88
+ ## Credits
89
+
90
+ All kernels and the Python frontend are by Ali Hassani and the NATTEN
91
+ contributors ([SHI-Labs/NATTEN](https://github.com/SHI-Labs/NATTEN), MIT).
92
+ This repository only repackages them for the Kernel Hub: pybind11 bindings
93
+ are replaced with `TORCH_LIBRARY` registrations (Python limited API), and
94
+ build-time codegen is committed statically.
95
+
96
+ If you use NATTEN, please cite:
97
+
98
+ ```bibtex
99
+ @inproceedings{hassani2023neighborhood,
100
+ title = {Neighborhood Attention Transformer},
101
+ author = {Ali Hassani and Steven Walton and Jiachen Li and Shen Li and Humphrey Shi},
102
+ year = 2023,
103
+ booktitle = {IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
104
+ }
105
+ @misc{hassani2024faster,
106
+ title = {Faster Neighborhood Attention: Reducing the O(n^2) Cost of Self Attention at the Threadblock Level},
107
+ author = {Ali Hassani and Wen-Mei Hwu and Humphrey Shi},
108
+ year = 2024,
109
+ eprint = {2403.04690},
110
+ archivePrefix = {arXiv},
111
+ }
112
+ ```
build/torch210-cu126-aarch64-linux/__init__.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from ._environment import HAS_LIBNATTEN
25
+ from .backends import (
26
+ get_bwd_configs_for_cutlass_blackwell_fmha,
27
+ get_bwd_configs_for_cutlass_blackwell_fna,
28
+ get_bwd_configs_for_cutlass_fmha,
29
+ get_bwd_configs_for_cutlass_fna,
30
+ get_bwd_configs_for_cutlass_hopper_fmha,
31
+ get_bwd_configs_for_cutlass_hopper_fna,
32
+ get_configs_for_cutlass_blackwell_fmha,
33
+ get_configs_for_cutlass_blackwell_fna,
34
+ get_configs_for_cutlass_fmha,
35
+ get_configs_for_cutlass_fna,
36
+ get_configs_for_cutlass_hopper_fmha,
37
+ get_configs_for_cutlass_hopper_fna,
38
+ get_configs_for_flex_fmha,
39
+ get_configs_for_flex_fna,
40
+ )
41
+ from .context import (
42
+ allow_flex_compile,
43
+ allow_flex_compile_backprop,
44
+ are_deterministic_algorithms_enabled,
45
+ disable_flex_compile,
46
+ disable_flex_compile_backprop,
47
+ get_memory_usage_preference,
48
+ is_flex_compile_allowed,
49
+ is_flex_compile_backprop_allowed,
50
+ is_kv_parallelism_in_fused_na_enabled,
51
+ is_memory_usage_default,
52
+ is_memory_usage_strict,
53
+ is_memory_usage_unrestricted,
54
+ set_memory_usage_preference,
55
+ use_deterministic_algorithms,
56
+ use_kv_parallelism_in_fused_na,
57
+ )
58
+ from .functional import attention, merge_attentions, na1d, na2d, na3d
59
+ from .modules import (
60
+ NeighborhoodAttention1D,
61
+ NeighborhoodAttention2D,
62
+ NeighborhoodAttention3D,
63
+ )
64
+ from . import _private_for_testing # noqa: F401
65
+ from .version import __version__
66
+
67
+ # kernel-builder port: the package contents are installed flat into the build
68
+ # variant directory, so a module literally named `types` would shadow the
69
+ # standard library `types` module whenever that directory is on PYTHONPATH
70
+ # (e.g. kernel-builder test shells and CI runners), breaking interpreter
71
+ # startup. The module therefore lives in `_types`; alias it here so
72
+ # `natten.types` keeps working like upstream.
73
+ import sys as _sys
74
+
75
+ from . import _types as types
76
+
77
+ _sys.modules[__name__ + ".types"] = types
78
+
79
+ # kernel-builder's compat shim (`natten/__init__.py` inside the build variant
80
+ # directory) executes this package under a path-derived module name and copies
81
+ # our globals into a `natten` module whose __path__ contains no submodules.
82
+ # Attribute access (`natten.functional`) works there, but real submodule
83
+ # imports (`from natten.functional import na2d`, `import natten.utils.testing`)
84
+ # would either fail with ModuleNotFoundError or — when resolvable through a
85
+ # parent package's __path__ — re-execute the module under a second name,
86
+ # duplicating module state. Bridge this with a meta-path finder that resolves
87
+ # any `natten.*` import to our already-loaded module objects. Only installed
88
+ # when `natten` in sys.modules is *our* compat shim, so a real `natten`
89
+ # distribution in the same environment is never hijacked.
90
+ if __name__ != "natten":
91
+ from pathlib import Path as _Path
92
+
93
+ _compat = _sys.modules.get("natten")
94
+ _is_our_compat = (
95
+ _compat is not None
96
+ and getattr(_compat, "__file__", None) is not None
97
+ and _Path(_compat.__file__).resolve()
98
+ == _Path(__file__).resolve().parent / "natten" / "__init__.py"
99
+ )
100
+
101
+ if _is_our_compat:
102
+ import importlib as _importlib
103
+ from importlib.abc import Loader as _Loader
104
+ from importlib.abc import MetaPathFinder as _MetaPathFinder
105
+ from importlib.util import spec_from_loader as _spec_from_loader
106
+
107
+ _real_root = __name__
108
+
109
+ class _NattenAliasLoader(_Loader):
110
+ def __init__(self, module):
111
+ self._module = module
112
+ self._spec = getattr(module, "__spec__", None)
113
+ self._loader = getattr(module, "__loader__", None)
114
+
115
+ def create_module(self, spec):
116
+ return self._module
117
+
118
+ def exec_module(self, module):
119
+ # The import machinery stamped the alias spec onto the real
120
+ # module in module_from_spec; restore its original identity.
121
+ module.__spec__ = self._spec
122
+ module.__loader__ = self._loader
123
+
124
+ class _NattenAliasFinder(_MetaPathFinder):
125
+ def find_spec(self, fullname, path=None, target=None):
126
+ if not fullname.startswith("natten."):
127
+ return None
128
+ real_name = _real_root + fullname[len("natten") :]
129
+ try:
130
+ module = _importlib.import_module(real_name)
131
+ except ImportError:
132
+ return None
133
+ return _spec_from_loader(fullname, _NattenAliasLoader(module))
134
+
135
+ # Must precede PathFinder, which would otherwise re-execute
136
+ # submodules reachable through a real parent package's __path__.
137
+ _sys.meta_path.insert(0, _NattenAliasFinder())
138
+
139
+ __all__ = [
140
+ "__version__",
141
+ "_private_for_testing",
142
+ "NeighborhoodAttention1D",
143
+ "NeighborhoodAttention2D",
144
+ "NeighborhoodAttention3D",
145
+ "are_deterministic_algorithms_enabled",
146
+ "use_deterministic_algorithms",
147
+ "use_kv_parallelism_in_fused_na",
148
+ "is_kv_parallelism_in_fused_na_enabled",
149
+ "set_memory_usage_preference",
150
+ "get_memory_usage_preference",
151
+ "is_memory_usage_default",
152
+ "is_memory_usage_strict",
153
+ "is_memory_usage_unrestricted",
154
+ "is_flex_compile_allowed",
155
+ "is_flex_compile_backprop_allowed",
156
+ "allow_flex_compile",
157
+ "allow_flex_compile_backprop",
158
+ "disable_flex_compile",
159
+ "disable_flex_compile_backprop",
160
+ "get_bwd_configs_for_cutlass_fmha",
161
+ "get_bwd_configs_for_cutlass_fna",
162
+ "get_configs_for_cutlass_fmha",
163
+ "get_configs_for_cutlass_fna",
164
+ "get_configs_for_cutlass_hopper_fmha",
165
+ "get_bwd_configs_for_cutlass_hopper_fmha",
166
+ "get_configs_for_cutlass_hopper_fna",
167
+ "get_bwd_configs_for_cutlass_hopper_fna",
168
+ "get_bwd_configs_for_cutlass_blackwell_fmha",
169
+ "get_bwd_configs_for_cutlass_blackwell_fna",
170
+ "get_configs_for_cutlass_blackwell_fmha",
171
+ "get_configs_for_cutlass_blackwell_fna",
172
+ "get_configs_for_flex_fmha",
173
+ "get_configs_for_flex_fna",
174
+ "HAS_LIBNATTEN",
175
+ "na1d",
176
+ "na2d",
177
+ "na3d",
178
+ "attention",
179
+ "merge_attentions",
180
+ ]
build/torch210-cu126-aarch64-linux/_environment.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from ._libnatten import HAS_LIBNATTEN # noqa: F401
25
+ from .utils.environment import (
26
+ _IS_CUDA_AVAILABLE,
27
+ _IS_TORCH_COMPILE_SUPPORTED,
28
+ _TORCH_VERSION,
29
+ parse_env_flag,
30
+ parse_env_int,
31
+ parse_env_str,
32
+ )
33
+
34
+ # Default tokperm implementation; choices:
35
+ # NATTEN_TOKPERM_DEFAULT_IMPL="cutlass"
36
+ # NATTEN_TOKPERM_DEFAULT_IMPL="torch"
37
+ USE_TORCH_IMPL_DEFAULT = (
38
+ parse_env_str("NATTEN_TOKPERM_DEFAULT_IMPL", "cutlass") == "torch"
39
+ )
40
+
41
+ # Unit tests
42
+ _RUN_EXTENDED_TESTS = parse_env_flag("NATTEN_RUN_EXTENDED_TESTS", False)
43
+ _RUN_FLEX_TESTS = parse_env_flag("NATTEN_RUN_FLEX_TESTS", True)
44
+ _NUM_RAND_SWEEP_TESTS = parse_env_int("NATTEN_RAND_SWEEP_TESTS", 1000)
45
+
46
+ # Profiler
47
+ DISABLE_TQDM = parse_env_flag("NATTEN_DISABLE_TQDM", False)
48
+
49
+
50
+ __all__ = [
51
+ "HAS_LIBNATTEN",
52
+ "_IS_CUDA_AVAILABLE",
53
+ "_IS_TORCH_COMPILE_SUPPORTED",
54
+ "DISABLE_TQDM",
55
+ "_RUN_FLEX_TESTS",
56
+ "_RUN_FLEX_TESTS",
57
+ "_NUM_RAND_SWEEP_TESTS",
58
+ "_TORCH_VERSION",
59
+ ]
build/torch210-cu126-aarch64-linux/_libnatten/__init__.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ # kernel-builder port: libnatten is always compiled into this kernel; the
24
+ # upstream stub fallback path is not needed.
25
+
26
+ import torch # noqa: F401
27
+
28
+ HAS_LIBNATTEN = True
29
+
30
+ from .torch_wrappers import (
31
+ blackwell_fmha_backward,
32
+ blackwell_fmha_forward,
33
+ blackwell_na1d_backward,
34
+ blackwell_na1d_forward,
35
+ blackwell_na2d_backward,
36
+ blackwell_na2d_forward,
37
+ blackwell_na3d_backward,
38
+ blackwell_na3d_forward,
39
+ compute_delta,
40
+ fmha_backward,
41
+ fmha_forward,
42
+ hopper_fmha_backward,
43
+ hopper_fmha_forward,
44
+ hopper_na1d_backward,
45
+ hopper_na1d_forward,
46
+ hopper_na2d_backward,
47
+ hopper_na2d_forward,
48
+ hopper_na3d_backward,
49
+ hopper_na3d_forward,
50
+ na1d_backward,
51
+ na1d_forward,
52
+ na2d_backward,
53
+ na2d_forward,
54
+ na3d_backward,
55
+ na3d_forward,
56
+ reference_na1d_backward,
57
+ reference_na1d_forward,
58
+ reference_na2d_backward,
59
+ reference_na2d_forward,
60
+ reference_na3d_backward,
61
+ reference_na3d_forward,
62
+ token_permute_1d,
63
+ token_permute_2d,
64
+ token_permute_3d,
65
+ token_unpermute_1d,
66
+ token_unpermute_2d,
67
+ token_unpermute_3d,
68
+ )
69
+
70
+ __all__ = [
71
+ "HAS_LIBNATTEN",
72
+ "blackwell_fmha_backward",
73
+ "blackwell_fmha_forward",
74
+ "blackwell_na1d_backward",
75
+ "blackwell_na1d_forward",
76
+ "blackwell_na2d_backward",
77
+ "blackwell_na2d_forward",
78
+ "blackwell_na3d_backward",
79
+ "blackwell_na3d_forward",
80
+ "compute_delta",
81
+ "fmha_backward",
82
+ "fmha_forward",
83
+ "hopper_fmha_backward",
84
+ "hopper_fmha_forward",
85
+ "hopper_na1d_backward",
86
+ "hopper_na1d_forward",
87
+ "hopper_na2d_backward",
88
+ "hopper_na2d_forward",
89
+ "hopper_na3d_backward",
90
+ "hopper_na3d_forward",
91
+ "na1d_backward",
92
+ "na1d_forward",
93
+ "na2d_backward",
94
+ "na2d_forward",
95
+ "na3d_backward",
96
+ "na3d_forward",
97
+ "reference_na1d_backward",
98
+ "reference_na1d_forward",
99
+ "reference_na2d_backward",
100
+ "reference_na2d_forward",
101
+ "reference_na3d_backward",
102
+ "reference_na3d_forward",
103
+ "token_permute_1d",
104
+ "token_permute_2d",
105
+ "token_permute_3d",
106
+ "token_unpermute_1d",
107
+ "token_unpermute_2d",
108
+ "token_unpermute_3d",
109
+ ]
build/torch210-cu126-aarch64-linux/_libnatten/torch_wrappers.py ADDED
@@ -0,0 +1,1006 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ # kernel-builder port of upstream `natten/_libnatten/torch_wrappers.py`.
24
+ #
25
+ # Upstream registers Python `torch.library.custom_op`s that allocate outputs
26
+ # and call into the pybind11 `libnatten` extension. In this port the ops are
27
+ # registered in C++ (`torch-ext/torch_binding.cpp`) as out-variant ops under
28
+ # the build-time namespace exposed through `.._ops`. The functions here keep
29
+ # the exact upstream calling conventions (allocate outputs, handle kv-split
30
+ # defaults, varlen zero-init) and call the C++ ops, and each C++ op gets a
31
+ # fake (meta) registration so the whole surface stays torch.compile-safe.
32
+ #
33
+ # Schema conventions of the C++ ops:
34
+ # - `kernel_size`/`stride`/`dilation`/tile shapes are `int[]`.
35
+ # - Multi-dimensional causal masks are passed as `int[]` (0/1) because
36
+ # boolean arrays are less uniformly supported in op schemas.
37
+ # - `scale` is a `float`.
38
+
39
+ import math
40
+ from typing import Optional, Sequence, Tuple
41
+
42
+ import torch
43
+ from torch import Tensor
44
+
45
+ from .._ops import add_op_namespace_prefix, ops
46
+ from ..utils.tuples import ceil_div_tuple, mul_tuple
47
+
48
+ register_fake = torch.library.register_fake
49
+
50
+
51
+ def maybe_contiguous(x):
52
+ return x.contiguous()
53
+
54
+
55
+ def _ints(v: Sequence) -> list:
56
+ return [int(x) for x in v]
57
+
58
+
59
+ ################################################################################
60
+ ############################ Fake (meta) registration ##########################
61
+ ################################################################################
62
+ # All C++ ops are out-variant: they only mutate output arguments and return
63
+ # nothing, so their fake impls are no-ops. Shape inference happens in the
64
+ # Python wrappers below, which allocate the outputs.
65
+
66
+
67
+ def _register_noop_fake(op_name: str) -> None:
68
+ def _fake(*args, **kwargs) -> None:
69
+ return None
70
+
71
+ register_fake(add_op_namespace_prefix(op_name))(_fake)
72
+
73
+
74
+ for _na_dim in (1, 2, 3):
75
+ for _prefix in ("", "hopper_", "blackwell_", "reference_"):
76
+ _register_noop_fake(f"{_prefix}na{_na_dim}d_forward")
77
+ _register_noop_fake(f"{_prefix}na{_na_dim}d_backward")
78
+ _register_noop_fake(f"token_permute_{_na_dim}d")
79
+ _register_noop_fake(f"token_unpermute_{_na_dim}d")
80
+
81
+ for _prefix in ("", "hopper_", "blackwell_"):
82
+ _register_noop_fake(f"{_prefix}fmha_forward")
83
+ _register_noop_fake(f"{_prefix}fmha_backward")
84
+
85
+ _register_noop_fake("compute_delta")
86
+
87
+
88
+ ################################################################################
89
+ ################################### FMHA ops ###################################
90
+ ################################################################################
91
+
92
+
93
+ def blackwell_fmha_forward(
94
+ query: Tensor,
95
+ key: Tensor,
96
+ value: Tensor,
97
+ is_causal: bool,
98
+ scale: float,
99
+ q_tile_size: int,
100
+ kv_tile_size: int,
101
+ run_persistent_kernel: bool,
102
+ cumulative_seqlen_Q: Optional[Tensor],
103
+ cumulative_seqlen_KV: Optional[Tensor],
104
+ max_seqlen_Q: int,
105
+ max_seqlen_KV: int,
106
+ ) -> Tuple[Tensor, Tensor]:
107
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
108
+
109
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
110
+
111
+ # NOTE: always zero-init outputs when doing varlen for safety
112
+ is_varlen = cumulative_seqlen_Q is not None
113
+ init_fn = torch.zeros if is_varlen else torch.empty
114
+
115
+ output = init_fn(output_shape, device=query.device, dtype=query.dtype)
116
+ logsumexp = init_fn(query.shape[:-1], dtype=torch.float32, device=query.device)
117
+
118
+ # Skip kernel launch when all sequences are empty
119
+ if is_varlen and max_seqlen_Q == 0 and max_seqlen_KV == 0:
120
+ return output, logsumexp
121
+
122
+ ops.blackwell_fmha_forward(
123
+ output,
124
+ query,
125
+ key,
126
+ value,
127
+ logsumexp,
128
+ bool(is_causal),
129
+ float(scale),
130
+ int(q_tile_size),
131
+ int(kv_tile_size),
132
+ bool(run_persistent_kernel),
133
+ cumulative_seqlen_Q,
134
+ cumulative_seqlen_KV,
135
+ int(max_seqlen_Q),
136
+ int(max_seqlen_KV),
137
+ )
138
+
139
+ return output, logsumexp
140
+
141
+
142
+ def blackwell_fmha_backward(
143
+ query: Tensor,
144
+ key: Tensor,
145
+ value: Tensor,
146
+ output: Tensor,
147
+ d_output: Tensor,
148
+ logsumexp: Tensor,
149
+ is_causal: bool,
150
+ scale: float,
151
+ q_tile_size: int,
152
+ kv_tile_size: int,
153
+ cumulative_seqlen_Q: Optional[Tensor],
154
+ cumulative_seqlen_KV: Optional[Tensor],
155
+ max_seqlen_Q: int,
156
+ max_seqlen_KV: int,
157
+ deterministic: bool,
158
+ ) -> Tuple[Tensor, Tensor, Tensor]:
159
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
160
+ output, d_output, logsumexp = [
161
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
162
+ ]
163
+
164
+ # NOTE: always zero-init outputs when doing varlen for safety
165
+ is_varlen = cumulative_seqlen_Q is not None
166
+ init_fn = torch.zeros_like if is_varlen else torch.empty_like
167
+
168
+ d_query = init_fn(query)
169
+ d_key = init_fn(key)
170
+ d_value = init_fn(value)
171
+
172
+ # Skip kernel launch when all sequences are empty
173
+ if is_varlen and max_seqlen_Q == 0 and max_seqlen_KV == 0:
174
+ return d_query, d_key, d_value
175
+
176
+ ops.blackwell_fmha_backward(
177
+ d_query,
178
+ d_key,
179
+ d_value,
180
+ query,
181
+ key,
182
+ value,
183
+ output,
184
+ d_output,
185
+ logsumexp,
186
+ bool(is_causal),
187
+ float(scale),
188
+ int(q_tile_size),
189
+ int(kv_tile_size),
190
+ cumulative_seqlen_Q,
191
+ cumulative_seqlen_KV,
192
+ int(max_seqlen_Q),
193
+ int(max_seqlen_KV),
194
+ bool(deterministic),
195
+ )
196
+
197
+ return d_query, d_key, d_value
198
+
199
+
200
+ def hopper_fmha_forward(
201
+ query: Tensor,
202
+ key: Tensor,
203
+ value: Tensor,
204
+ is_causal: bool,
205
+ scale: float,
206
+ q_tile_size: int,
207
+ kv_tile_size: int,
208
+ kernel_schedule_int: int,
209
+ cumulative_seqlen_Q: Optional[Tensor],
210
+ cumulative_seqlen_KV: Optional[Tensor],
211
+ max_seqlen_Q: int,
212
+ max_seqlen_KV: int,
213
+ ) -> Tuple[Tensor, Tensor]:
214
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
215
+
216
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
217
+
218
+ # NOTE: always zero-init outputs when doing varlen for safety
219
+ is_varlen = cumulative_seqlen_Q is not None
220
+ init_fn = torch.zeros if is_varlen else torch.empty
221
+
222
+ output = init_fn(output_shape, device=query.device, dtype=query.dtype)
223
+ logsumexp = init_fn(query.shape[:-1], dtype=torch.float32, device=query.device)
224
+
225
+ # Skip kernel launch when all sequences are empty
226
+ if is_varlen and max_seqlen_Q == 0 and max_seqlen_KV == 0:
227
+ return output, logsumexp
228
+
229
+ ops.hopper_fmha_forward(
230
+ output,
231
+ query,
232
+ key,
233
+ value,
234
+ logsumexp,
235
+ bool(is_causal),
236
+ float(scale),
237
+ int(q_tile_size),
238
+ int(kv_tile_size),
239
+ int(kernel_schedule_int),
240
+ cumulative_seqlen_Q,
241
+ cumulative_seqlen_KV,
242
+ int(max_seqlen_Q),
243
+ int(max_seqlen_KV),
244
+ )
245
+
246
+ return output, logsumexp
247
+
248
+
249
+ def hopper_fmha_backward(
250
+ query: Tensor,
251
+ key: Tensor,
252
+ value: Tensor,
253
+ output: Tensor,
254
+ d_output: Tensor,
255
+ logsumexp: Tensor,
256
+ is_causal: bool,
257
+ scale: float,
258
+ q_tile_size: int,
259
+ kv_tile_size: int,
260
+ cumulative_seqlen_Q: Optional[Tensor],
261
+ cumulative_seqlen_KV: Optional[Tensor],
262
+ max_seqlen_Q: int,
263
+ max_seqlen_KV: int,
264
+ ) -> Tuple[Tensor, Tensor, Tensor]:
265
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
266
+ output, d_output, logsumexp = [
267
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
268
+ ]
269
+
270
+ # NOTE: always zero-init outputs when doing varlen for safety
271
+ is_varlen = cumulative_seqlen_Q is not None
272
+ init_fn = torch.zeros_like if is_varlen else torch.empty_like
273
+
274
+ d_query = init_fn(query)
275
+ d_key = init_fn(key)
276
+ d_value = init_fn(value)
277
+
278
+ # Skip kernel launch when all sequences are empty
279
+ if is_varlen and max_seqlen_Q == 0 and max_seqlen_KV == 0:
280
+ return d_query, d_key, d_value
281
+
282
+ ops.hopper_fmha_backward(
283
+ d_query,
284
+ d_key,
285
+ d_value,
286
+ query,
287
+ key,
288
+ value,
289
+ output,
290
+ d_output,
291
+ logsumexp,
292
+ bool(is_causal),
293
+ float(scale),
294
+ int(q_tile_size),
295
+ int(kv_tile_size),
296
+ cumulative_seqlen_Q,
297
+ cumulative_seqlen_KV,
298
+ int(max_seqlen_Q),
299
+ int(max_seqlen_KV),
300
+ )
301
+
302
+ return d_query, d_key, d_value
303
+
304
+
305
+ def fmha_forward(
306
+ query: Tensor,
307
+ key: Tensor,
308
+ value: Tensor,
309
+ is_causal: bool,
310
+ scale: float,
311
+ q_tile_size: int,
312
+ kv_tile_size: int,
313
+ cumulative_seqlen_Q: Optional[Tensor],
314
+ cumulative_seqlen_KV: Optional[Tensor],
315
+ max_seqlen_Q: int,
316
+ max_seqlen_KV: int,
317
+ ) -> Tuple[Tensor, Tensor]:
318
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
319
+
320
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
321
+
322
+ # NOTE: always zero-init outputs when doing varlen for safety
323
+ is_varlen = cumulative_seqlen_Q is not None
324
+ init_fn = torch.zeros if is_varlen else torch.empty
325
+
326
+ output = init_fn(output_shape, device=query.device, dtype=query.dtype)
327
+ logsumexp = init_fn(query.shape[:-1], dtype=torch.float32, device=query.device)
328
+
329
+ # Skip kernel launch when all sequences are empty
330
+ if is_varlen and max_seqlen_Q == 0 and max_seqlen_KV == 0:
331
+ return output, logsumexp
332
+
333
+ ops.fmha_forward(
334
+ output,
335
+ query,
336
+ key,
337
+ value,
338
+ logsumexp,
339
+ bool(is_causal),
340
+ float(scale),
341
+ int(q_tile_size),
342
+ int(kv_tile_size),
343
+ cumulative_seqlen_Q,
344
+ cumulative_seqlen_KV,
345
+ int(max_seqlen_Q),
346
+ int(max_seqlen_KV),
347
+ )
348
+
349
+ return output, logsumexp
350
+
351
+
352
+ def fmha_backward(
353
+ query: Tensor,
354
+ key: Tensor,
355
+ value: Tensor,
356
+ output: Tensor,
357
+ d_output: Tensor,
358
+ logsumexp: Tensor,
359
+ is_causal: bool,
360
+ scale: float,
361
+ q_tile_size: int,
362
+ kv_tile_size: int,
363
+ num_kv_splits: Optional[int],
364
+ compute_delta_with_pt: bool,
365
+ cumulative_seqlen_Q: Optional[Tensor],
366
+ cumulative_seqlen_KV: Optional[Tensor],
367
+ max_seqlen_Q: int,
368
+ max_seqlen_KV: int,
369
+ deterministic: bool,
370
+ ) -> Tuple[Tensor, Tensor, Tensor]:
371
+ from ..backends.configs.cutlass.backward_knobs import check_fmha_kv_splits
372
+
373
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
374
+ output, d_output, logsumexp = [
375
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
376
+ ]
377
+
378
+ # NOTE: always zero-init outputs when doing varlen for safety
379
+ is_varlen = cumulative_seqlen_Q is not None
380
+ init_fn = torch.zeros_like if is_varlen else torch.empty_like
381
+
382
+ d_query = init_fn(query)
383
+ d_key = init_fn(key)
384
+ d_value = init_fn(value)
385
+
386
+ # Skip kernel launch when all sequences are empty
387
+ if is_varlen and max_seqlen_Q == 0 and max_seqlen_KV == 0:
388
+ return d_query, d_key, d_value
389
+
390
+ if deterministic:
391
+ # Torch reduction seems to have slight reproducibility issues, even with determinism on
392
+ compute_delta_with_pt = False
393
+ # TODO: this is the only way to get determinism in this kernel, but it's very slow
394
+ num_kv_splits = 1
395
+ else:
396
+ # Compute default kv_splits if not specified
397
+ # max_seqlen must be at least 2 to satisfy static checks that are just too complicated to
398
+ # relax at this point. Kernel launch will be skipped if max_seqlen is 0 anyway. Prior checks
399
+ # should prevent negative max seqlens.
400
+ max_seqlen = max(2, max_seqlen_KV) if is_varlen else None
401
+ num_kv_splits = check_fmha_kv_splits(
402
+ kv_splits=num_kv_splits,
403
+ input_tensor=key,
404
+ kv_tile_size=kv_tile_size,
405
+ deterministic=deterministic,
406
+ max_seqlen=max_seqlen,
407
+ )
408
+
409
+ ops.fmha_backward(
410
+ d_query,
411
+ d_key,
412
+ d_value,
413
+ query,
414
+ key,
415
+ value,
416
+ output,
417
+ d_output,
418
+ logsumexp,
419
+ bool(is_causal),
420
+ float(scale),
421
+ int(q_tile_size),
422
+ int(kv_tile_size),
423
+ int(num_kv_splits),
424
+ bool(compute_delta_with_pt),
425
+ cumulative_seqlen_Q,
426
+ cumulative_seqlen_KV,
427
+ int(max_seqlen_Q),
428
+ int(max_seqlen_KV),
429
+ )
430
+
431
+ return d_query, d_key, d_value
432
+
433
+
434
+ ################################################################################
435
+ ################################### FNA ops ###################################
436
+ ################################################################################
437
+
438
+
439
+ def make_blackwell_fna_ops(na_dim):
440
+ fwd_op = getattr(ops, f"blackwell_na{na_dim}d_forward")
441
+ bwd_op = getattr(ops, f"blackwell_na{na_dim}d_backward")
442
+
443
+ def blackwell_fna_forward(
444
+ query: Tensor,
445
+ key: Tensor,
446
+ value: Tensor,
447
+ kernel_size,
448
+ stride,
449
+ dilation,
450
+ is_causal,
451
+ scale: float,
452
+ q_shape,
453
+ kv_shape,
454
+ qkv_shape,
455
+ q_tile_shape,
456
+ kv_tile_shape,
457
+ run_persistent_kernel: bool,
458
+ ) -> Tuple[Tensor, Tensor]:
459
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
460
+
461
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
462
+ output = torch.empty(output_shape, device=query.device, dtype=query.dtype)
463
+
464
+ logsumexp = torch.empty(
465
+ query.shape[:-1], dtype=torch.float32, device=query.device
466
+ )
467
+
468
+ fwd_op(
469
+ output,
470
+ query,
471
+ key,
472
+ value,
473
+ logsumexp,
474
+ _ints(kernel_size),
475
+ _ints(stride),
476
+ _ints(dilation),
477
+ _ints(is_causal),
478
+ float(scale),
479
+ _ints(q_shape),
480
+ _ints(kv_shape),
481
+ _ints(qkv_shape),
482
+ _ints(q_tile_shape),
483
+ _ints(kv_tile_shape),
484
+ bool(run_persistent_kernel),
485
+ )
486
+
487
+ return output, logsumexp
488
+
489
+ def blackwell_fna_backward(
490
+ query: Tensor,
491
+ key: Tensor,
492
+ value: Tensor,
493
+ output: Tensor,
494
+ d_output: Tensor,
495
+ logsumexp: Tensor,
496
+ kernel_size,
497
+ stride,
498
+ dilation,
499
+ is_causal,
500
+ scale: float,
501
+ q_shape,
502
+ kv_shape,
503
+ qkv_shape,
504
+ q_tile_shape,
505
+ kv_tile_shape,
506
+ ) -> Tuple[Tensor, Tensor, Tensor]:
507
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
508
+ output, d_output, logsumexp = [
509
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
510
+ ]
511
+
512
+ d_query = torch.empty_like(query)
513
+ d_key = torch.empty_like(key)
514
+ d_value = torch.empty_like(value)
515
+
516
+ bwd_op(
517
+ d_query,
518
+ d_key,
519
+ d_value,
520
+ query,
521
+ key,
522
+ value,
523
+ output,
524
+ d_output,
525
+ logsumexp,
526
+ _ints(kernel_size),
527
+ _ints(stride),
528
+ _ints(dilation),
529
+ _ints(is_causal),
530
+ float(scale),
531
+ _ints(q_shape),
532
+ _ints(kv_shape),
533
+ _ints(qkv_shape),
534
+ _ints(q_tile_shape),
535
+ _ints(kv_tile_shape),
536
+ )
537
+
538
+ return d_query, d_key, d_value
539
+
540
+ return blackwell_fna_forward, blackwell_fna_backward
541
+
542
+
543
+ def make_hopper_fna_ops(na_dim):
544
+ fwd_op = getattr(ops, f"hopper_na{na_dim}d_forward")
545
+ bwd_op = getattr(ops, f"hopper_na{na_dim}d_backward")
546
+
547
+ def hopper_fna_forward(
548
+ query: Tensor,
549
+ key: Tensor,
550
+ value: Tensor,
551
+ kernel_size,
552
+ stride,
553
+ dilation,
554
+ is_causal,
555
+ scale: float,
556
+ q_shape,
557
+ kv_shape,
558
+ qkv_shape,
559
+ q_tile_shape,
560
+ kv_tile_shape,
561
+ kernel_schedule_int: int,
562
+ ) -> Tuple[Tensor, Tensor]:
563
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
564
+
565
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
566
+ output = torch.empty(output_shape, device=query.device, dtype=query.dtype)
567
+
568
+ logsumexp = torch.empty(
569
+ query.shape[:-1], dtype=torch.float32, device=query.device
570
+ )
571
+
572
+ fwd_op(
573
+ output,
574
+ query,
575
+ key,
576
+ value,
577
+ logsumexp,
578
+ _ints(kernel_size),
579
+ _ints(stride),
580
+ _ints(dilation),
581
+ _ints(is_causal),
582
+ float(scale),
583
+ _ints(q_shape),
584
+ _ints(kv_shape),
585
+ _ints(qkv_shape),
586
+ _ints(q_tile_shape),
587
+ _ints(kv_tile_shape),
588
+ int(kernel_schedule_int),
589
+ )
590
+
591
+ return output, logsumexp
592
+
593
+ def hopper_fna_backward(
594
+ query: Tensor,
595
+ key: Tensor,
596
+ value: Tensor,
597
+ output: Tensor,
598
+ d_output: Tensor,
599
+ logsumexp: Tensor,
600
+ kernel_size,
601
+ stride,
602
+ dilation,
603
+ is_causal,
604
+ scale: float,
605
+ q_shape,
606
+ kv_shape,
607
+ qkv_shape,
608
+ q_tile_shape,
609
+ kv_tile_shape,
610
+ ) -> Tuple[Tensor, Tensor, Tensor]:
611
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
612
+ output, d_output, logsumexp = [
613
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
614
+ ]
615
+
616
+ d_query = torch.empty_like(query)
617
+ d_key = torch.empty_like(key)
618
+ d_value = torch.empty_like(value)
619
+
620
+ bwd_op(
621
+ d_query,
622
+ d_key,
623
+ d_value,
624
+ query,
625
+ key,
626
+ value,
627
+ output,
628
+ d_output,
629
+ logsumexp,
630
+ _ints(kernel_size),
631
+ _ints(stride),
632
+ _ints(dilation),
633
+ _ints(is_causal),
634
+ float(scale),
635
+ _ints(q_shape),
636
+ _ints(kv_shape),
637
+ _ints(qkv_shape),
638
+ _ints(q_tile_shape),
639
+ _ints(kv_tile_shape),
640
+ )
641
+
642
+ return d_query, d_key, d_value
643
+
644
+ return hopper_fna_forward, hopper_fna_backward
645
+
646
+
647
+ def make_fna_ops(na_dim):
648
+ fwd_op = getattr(ops, f"na{na_dim}d_forward")
649
+ bwd_op = getattr(ops, f"na{na_dim}d_backward")
650
+
651
+ def fna_forward(
652
+ query: Tensor,
653
+ key: Tensor,
654
+ value: Tensor,
655
+ kernel_size,
656
+ stride,
657
+ dilation,
658
+ is_causal,
659
+ scale: float,
660
+ q_tile_shape,
661
+ kv_tile_shape,
662
+ ) -> Tuple[Tensor, Tensor]:
663
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
664
+
665
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
666
+ output = torch.empty(output_shape, device=query.device, dtype=query.dtype)
667
+
668
+ logsumexp = torch.empty(
669
+ query.shape[:-1], dtype=torch.float32, device=query.device
670
+ )
671
+
672
+ fwd_op(
673
+ output,
674
+ query,
675
+ key,
676
+ value,
677
+ logsumexp,
678
+ _ints(kernel_size),
679
+ _ints(stride),
680
+ _ints(dilation),
681
+ _ints(is_causal),
682
+ float(scale),
683
+ _ints(q_tile_shape),
684
+ _ints(kv_tile_shape),
685
+ )
686
+
687
+ return output, logsumexp
688
+
689
+ def fna_backward(
690
+ query: Tensor,
691
+ key: Tensor,
692
+ value: Tensor,
693
+ output: Tensor,
694
+ d_output: Tensor,
695
+ logsumexp: Tensor,
696
+ kernel_size,
697
+ stride,
698
+ dilation,
699
+ is_causal,
700
+ scale: float,
701
+ q_tile_shape,
702
+ kv_tile_shape,
703
+ num_kv_splits,
704
+ compute_delta_with_pt: bool,
705
+ deterministic: bool,
706
+ ) -> Tuple[Tensor, Tensor, Tensor]:
707
+ from ..backends.configs.cutlass.backward_knobs import check_fna_kv_splits
708
+
709
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
710
+ output, d_output, logsumexp = [
711
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
712
+ ]
713
+
714
+ d_query = torch.empty_like(query)
715
+ d_key = torch.empty_like(key)
716
+ d_value = torch.empty_like(value)
717
+
718
+ if deterministic:
719
+ # Torch reduction seems to have slight reproducibility issues, even with determinism on
720
+ compute_delta_with_pt = False
721
+ # TODO: this is the only way to get determinism in this kernel, but it's very slow
722
+ num_kv_splits = tuple(1 for _ in range(na_dim))
723
+ else:
724
+ # Compute default kv_splits if not specified
725
+ num_kv_splits = check_fna_kv_splits(
726
+ kv_splits=tuple(num_kv_splits) if num_kv_splits is not None else None,
727
+ input_tensor=key,
728
+ kv_tile_shape=tuple(kv_tile_shape),
729
+ deterministic=deterministic,
730
+ dilation=tuple(dilation),
731
+ )
732
+
733
+ bwd_op(
734
+ d_query,
735
+ d_key,
736
+ d_value,
737
+ query,
738
+ key,
739
+ value,
740
+ output,
741
+ d_output,
742
+ logsumexp,
743
+ _ints(kernel_size),
744
+ _ints(stride),
745
+ _ints(dilation),
746
+ _ints(is_causal),
747
+ float(scale),
748
+ _ints(q_tile_shape),
749
+ _ints(kv_tile_shape),
750
+ _ints(num_kv_splits),
751
+ bool(compute_delta_with_pt),
752
+ )
753
+
754
+ return d_query, d_key, d_value
755
+
756
+ return fna_forward, fna_backward
757
+
758
+
759
+ def make_reference_fna_ops(na_dim):
760
+ fwd_op = getattr(ops, f"reference_na{na_dim}d_forward")
761
+ bwd_op = getattr(ops, f"reference_na{na_dim}d_backward")
762
+
763
+ def reference_fna_forward(
764
+ query: Tensor,
765
+ key: Tensor,
766
+ value: Tensor,
767
+ kernel_size,
768
+ stride,
769
+ dilation,
770
+ is_causal,
771
+ scale: float,
772
+ qkv_shape,
773
+ num_extra_kv: int,
774
+ ) -> Tuple[Tensor, Tensor]:
775
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
776
+
777
+ output_shape = [s for s in query.shape[:-1]] + [value.shape[-1]]
778
+ output = torch.empty(output_shape, device=query.device, dtype=query.dtype)
779
+
780
+ logsumexp = torch.empty(
781
+ query.shape[:-1], dtype=torch.float32, device=query.device
782
+ )
783
+
784
+ fwd_op(
785
+ output,
786
+ query,
787
+ key,
788
+ value,
789
+ logsumexp,
790
+ _ints(kernel_size),
791
+ _ints(stride),
792
+ _ints(dilation),
793
+ _ints(is_causal),
794
+ float(scale),
795
+ _ints(qkv_shape),
796
+ int(num_extra_kv),
797
+ )
798
+
799
+ return output, logsumexp
800
+
801
+ def reference_fna_backward(
802
+ query: Tensor,
803
+ key: Tensor,
804
+ value: Tensor,
805
+ output: Tensor,
806
+ d_output: Tensor,
807
+ logsumexp: Tensor,
808
+ kernel_size,
809
+ stride,
810
+ dilation,
811
+ is_causal,
812
+ scale: float,
813
+ qkv_shape,
814
+ num_extra_kv: int,
815
+ ) -> Tuple[Tensor, Tensor, Tensor]:
816
+ query, key, value = [maybe_contiguous(x) for x in (query, key, value)]
817
+ output, d_output, logsumexp = [
818
+ maybe_contiguous(x) for x in (output, d_output, logsumexp)
819
+ ]
820
+
821
+ d_query = torch.empty_like(query)
822
+ d_key = torch.empty_like(key)
823
+ d_value = torch.empty_like(value)
824
+
825
+ bwd_op(
826
+ d_query,
827
+ d_key,
828
+ d_value,
829
+ query,
830
+ key,
831
+ value,
832
+ output,
833
+ d_output,
834
+ logsumexp,
835
+ _ints(kernel_size),
836
+ _ints(stride),
837
+ _ints(dilation),
838
+ _ints(is_causal),
839
+ float(scale),
840
+ _ints(qkv_shape),
841
+ int(num_extra_kv),
842
+ )
843
+
844
+ return d_query, d_key, d_value
845
+
846
+ return reference_fna_forward, reference_fna_backward
847
+
848
+
849
+ ################################################################################
850
+ ################################# TokPerm ops #################################
851
+ ################################################################################
852
+
853
+
854
+ def make_token_permute_ops(na_dim):
855
+ permute_op = getattr(ops, f"token_permute_{na_dim}d")
856
+ unpermute_op = getattr(ops, f"token_unpermute_{na_dim}d")
857
+
858
+ def token_permute(
859
+ input_tensor: Tensor,
860
+ tile_shape,
861
+ dilation,
862
+ flip_tiled_dims: bool,
863
+ ) -> Tensor:
864
+ input_tensor = maybe_contiguous(input_tensor)
865
+
866
+ token_layout = tuple(x for x in input_tensor.shape[1 : na_dim + 1])
867
+ token_layout_padded = mul_tuple(
868
+ mul_tuple(
869
+ ceil_div_tuple(ceil_div_tuple(token_layout, tile_shape), dilation),
870
+ dilation,
871
+ ),
872
+ tile_shape,
873
+ )
874
+ output_shape = [
875
+ input_tensor.shape[0],
876
+ math.prod(token_layout_padded),
877
+ input_tensor.shape[-2],
878
+ input_tensor.shape[-1],
879
+ ]
880
+ output = torch.empty(
881
+ output_shape, device=input_tensor.device, dtype=input_tensor.dtype
882
+ )
883
+ permute_op(
884
+ output,
885
+ input_tensor,
886
+ _ints(tile_shape),
887
+ _ints(dilation),
888
+ bool(flip_tiled_dims),
889
+ )
890
+
891
+ # Fold dilation in batch dimension so that attention is correct.
892
+ output = output.reshape(
893
+ input_tensor.shape[0] * math.prod(dilation),
894
+ -1,
895
+ input_tensor.shape[-2],
896
+ input_tensor.shape[-1],
897
+ )
898
+
899
+ return output
900
+
901
+ def token_unpermute(
902
+ input_tensor: Tensor,
903
+ token_layout_shape,
904
+ tile_shape,
905
+ dilation,
906
+ flip_tiled_dims: bool,
907
+ ) -> Tensor:
908
+ input_tensor = maybe_contiguous(input_tensor)
909
+
910
+ # Unfold dilation in batch dimension
911
+ num_dilation_groups = math.prod(dilation)
912
+ assert input_tensor.shape[0] % num_dilation_groups == 0
913
+ input_tensor = input_tensor.reshape(
914
+ input_tensor.shape[0] // num_dilation_groups,
915
+ -1,
916
+ input_tensor.shape[-2],
917
+ input_tensor.shape[-1],
918
+ )
919
+
920
+ output_shape = [
921
+ input_tensor.shape[0],
922
+ *token_layout_shape,
923
+ input_tensor.shape[-2],
924
+ input_tensor.shape[-1],
925
+ ]
926
+ output = torch.empty(
927
+ output_shape, device=input_tensor.device, dtype=input_tensor.dtype
928
+ )
929
+ unpermute_op(
930
+ output,
931
+ input_tensor,
932
+ _ints(tile_shape),
933
+ _ints(dilation),
934
+ bool(flip_tiled_dims),
935
+ )
936
+
937
+ return output
938
+
939
+ return token_permute, token_unpermute
940
+
941
+
942
+ (blackwell_na1d_forward, blackwell_na1d_backward) = make_blackwell_fna_ops(1)
943
+ (blackwell_na2d_forward, blackwell_na2d_backward) = make_blackwell_fna_ops(2)
944
+ (blackwell_na3d_forward, blackwell_na3d_backward) = make_blackwell_fna_ops(3)
945
+
946
+ (hopper_na1d_forward, hopper_na1d_backward) = make_hopper_fna_ops(1)
947
+ (hopper_na2d_forward, hopper_na2d_backward) = make_hopper_fna_ops(2)
948
+ (hopper_na3d_forward, hopper_na3d_backward) = make_hopper_fna_ops(3)
949
+
950
+ (na1d_forward, na1d_backward) = make_fna_ops(1)
951
+ (na2d_forward, na2d_backward) = make_fna_ops(2)
952
+ (na3d_forward, na3d_backward) = make_fna_ops(3)
953
+
954
+ (reference_na1d_forward, reference_na1d_backward) = make_reference_fna_ops(1)
955
+ (reference_na2d_forward, reference_na2d_backward) = make_reference_fna_ops(2)
956
+ (reference_na3d_forward, reference_na3d_backward) = make_reference_fna_ops(3)
957
+
958
+ (token_permute_1d, token_unpermute_1d) = make_token_permute_ops(1)
959
+ (token_permute_2d, token_unpermute_2d) = make_token_permute_ops(2)
960
+ (token_permute_3d, token_unpermute_3d) = make_token_permute_ops(3)
961
+
962
+
963
+ # This is only used in unit tests, and not even auto-diffable
964
+ def compute_delta(out: Tensor, d_out: Tensor, delta: Tensor) -> None:
965
+ ops.compute_delta(out, d_out, delta)
966
+
967
+
968
+ __all__ = [
969
+ "blackwell_fmha_backward",
970
+ "blackwell_fmha_forward",
971
+ "blackwell_na1d_backward",
972
+ "blackwell_na1d_forward",
973
+ "blackwell_na2d_backward",
974
+ "blackwell_na2d_forward",
975
+ "blackwell_na3d_backward",
976
+ "blackwell_na3d_forward",
977
+ "compute_delta",
978
+ "fmha_backward",
979
+ "fmha_forward",
980
+ "hopper_fmha_backward",
981
+ "hopper_fmha_forward",
982
+ "hopper_na1d_backward",
983
+ "hopper_na1d_forward",
984
+ "hopper_na2d_backward",
985
+ "hopper_na2d_forward",
986
+ "hopper_na3d_backward",
987
+ "hopper_na3d_forward",
988
+ "na1d_backward",
989
+ "na1d_forward",
990
+ "na2d_backward",
991
+ "na2d_forward",
992
+ "na3d_backward",
993
+ "na3d_forward",
994
+ "reference_na1d_backward",
995
+ "reference_na1d_forward",
996
+ "reference_na2d_backward",
997
+ "reference_na2d_forward",
998
+ "reference_na3d_backward",
999
+ "reference_na3d_forward",
1000
+ "token_permute_1d",
1001
+ "token_permute_2d",
1002
+ "token_permute_3d",
1003
+ "token_unpermute_1d",
1004
+ "token_unpermute_2d",
1005
+ "token_unpermute_3d",
1006
+ ]
build/torch210-cu126-aarch64-linux/_natten_cuda_5fe9dbb.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:02f9f3cca0fe961f460ce848017cc7a391aa74e34ca29e5f6431d1138c433600
3
+ size 100978320
build/torch210-cu126-aarch64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _natten_cuda_5fe9dbb
3
+ ops = torch.ops._natten_cuda_5fe9dbb
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_natten_cuda_5fe9dbb::{op_name}"
build/torch210-cu126-aarch64-linux/_private_for_testing.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from . import (
2
+ _environment,
3
+ _libnatten,
4
+ _ops,
5
+ _types,
6
+ backends,
7
+ context,
8
+ functional,
9
+ token_permute,
10
+ )
11
+ from .backends import reference
12
+ from .backends.configs import cutlass, cutlass_blackwell, cutlass_hopper
13
+ from .utils import checks, dtype, log, testing, varlen
14
+
15
+ __all__ = [
16
+ "_environment",
17
+ "_libnatten",
18
+ "_ops",
19
+ "_types",
20
+ "backends",
21
+ "checks",
22
+ "context",
23
+ "cutlass",
24
+ "cutlass_blackwell",
25
+ "cutlass_hopper",
26
+ "dtype",
27
+ "functional",
28
+ "log",
29
+ "reference",
30
+ "testing",
31
+ "token_permute",
32
+ "varlen",
33
+ ]
build/torch210-cu126-aarch64-linux/_types.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from enum import Enum
25
+ from typing import Tuple, Union
26
+
27
+ NoneType = type(None)
28
+
29
+ Dimension1DType = Tuple[int]
30
+ Dimension2DType = Tuple[int, int]
31
+ Dimension3DType = Tuple[int, int, int]
32
+
33
+ CausalArg1DType = Tuple[bool]
34
+ CausalArg2DType = Tuple[bool, bool]
35
+ CausalArg3DType = Tuple[bool, bool, bool]
36
+
37
+ # NOTE: switch to | when < 3.10 support is dropped
38
+ Dimension1DTypeOrDed = Union[int, Dimension1DType]
39
+ Dimension2DTypeOrDed = Union[int, Dimension2DType]
40
+ Dimension3DTypeOrDed = Union[int, Dimension3DType]
41
+
42
+ CausalArg1DTypeOrDed = Union[bool, CausalArg1DType]
43
+ CausalArg2DTypeOrDed = Union[bool, CausalArg2DType]
44
+ CausalArg3DTypeOrDed = Union[bool, CausalArg3DType]
45
+
46
+ DimensionType = Union[Dimension1DType, Dimension2DType, Dimension3DType]
47
+ CausalArgType = Union[CausalArg1DType, CausalArg2DType, CausalArg3DType]
48
+
49
+ DimensionTypeOrDed = Union[int, DimensionType]
50
+ CausalArgTypeOrDed = Union[bool, CausalArgType]
51
+
52
+ # (query_tile_shape, kv_tile_shape)
53
+ QKTileShapeType = Union[
54
+ Tuple[Dimension1DType, Dimension1DType],
55
+ Tuple[Dimension2DType, Dimension2DType],
56
+ Tuple[Dimension3DType, Dimension3DType],
57
+ ]
58
+
59
+
60
+ # TODO: Only applies to Hopper FMHA/FNA for now -- extend to other applicable kernels
61
+ class KernelSchedule(Enum):
62
+ NonPersistent = 0
63
+ WarpSpecializedCooperative = 1
64
+ WarpSpecializedPingpong = 2
65
+
66
+
67
+ CutlassFnaForwardConfigType = QKTileShapeType
68
+ CutlassFnaBackwardConfigType = QKTileShapeType
69
+ CutlassBlackwellFnaForwardConfigType = QKTileShapeType
70
+ CutlassBlackwellFnaBackwardConfigType = QKTileShapeType
71
+ CutlassHopperFnaForwardConfigType = Tuple[QKTileShapeType, KernelSchedule]
72
+ CutlassHopperFnaBackwardConfigType = QKTileShapeType
73
+ FlexFnaForwardConfigType = QKTileShapeType
74
+
75
+ # FMHA configs
76
+ FmhaForwardConfigType = Tuple[int, int]
77
+
78
+ CutlassFmhaForwardConfigType = FmhaForwardConfigType
79
+ CutlassFmhaBackwardConfigType = FmhaForwardConfigType
80
+
81
+ FlexFmhaForwardConfigType = FmhaForwardConfigType
82
+ CutlassBlackwellFmhaForwardConfigType = FmhaForwardConfigType
83
+ CutlassBlackwellFmhaBackwardConfigType = FmhaForwardConfigType
84
+ CutlassHopperFmhaForwardConfigType = Tuple[FmhaForwardConfigType, KernelSchedule]
85
+ CutlassHopperFmhaBackwardConfigType = FmhaForwardConfigType
build/torch210-cu126-aarch64-linux/attn_merge.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import functools
25
+ from typing import List, Tuple
26
+
27
+ import torch
28
+ from torch import Tensor
29
+ from torch.amp import custom_bwd, custom_fwd
30
+ from torch.autograd import Function
31
+
32
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
33
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
34
+
35
+ from ._environment import _IS_TORCH_COMPILE_SUPPORTED
36
+
37
+
38
+ def _maybe_torch_compile(*args, **kwargs):
39
+ def decorator(f):
40
+ if _IS_TORCH_COMPILE_SUPPORTED:
41
+ return torch.compile(f, *args, **kwargs)
42
+ return f
43
+
44
+ return decorator
45
+
46
+
47
+ # TODO: if use cases for this grow, we might want to do a custom kernel
48
+ def _merge_attentions_fn(
49
+ outputs: List[Tensor], lse_tensors: List[Tensor]
50
+ ) -> Tuple[Tensor, Tensor]:
51
+
52
+ assert len(outputs) >= 2, "Expected at least two tensors."
53
+ num_splits = len(outputs)
54
+ assert (
55
+ len(lse_tensors) == num_splits
56
+ ), "Expected number of outputs and LSE tensors to match."
57
+
58
+ assert all(
59
+ output.dim() == 4 and output.is_contiguous() for output in outputs
60
+ ), "Output tensors must be rank-4 tensors with (batch, seq, heads, dim) contiguous layout."
61
+
62
+ batch, seqlen, heads, dim = outputs[0].shape
63
+
64
+ assert all(
65
+ [x for x in output.shape] == [batch, seqlen, heads, dim] for output in outputs
66
+ ), "Output tensors must match in shape."
67
+
68
+ assert all(
69
+ lse.dim() == 3
70
+ and lse.is_contiguous()
71
+ and [x for x in lse.shape] == [batch, seqlen, heads]
72
+ for lse in lse_tensors
73
+ ), "LSE tensors must be rank-3 tensors with (batch, seq, heads) contiguous layout, and match in shape."
74
+
75
+ accum_type = torch.float32
76
+ output_type = outputs[0].dtype
77
+
78
+ lse_tensors = [lse.to(accum_type).unsqueeze(-1) for lse in lse_tensors]
79
+
80
+ outputs = [output.to(accum_type) for output in outputs]
81
+
82
+ # New approach based on https://github.com/zhuzilin/ring-flash-attention/pull/34
83
+ output = outputs[0] - torch.nn.functional.sigmoid(
84
+ lse_tensors[1] - lse_tensors[0]
85
+ ) * (outputs[0] - outputs[1])
86
+ logsumexp = lse_tensors[0] - torch.nn.functional.logsigmoid(
87
+ lse_tensors[0] - lse_tensors[1]
88
+ )
89
+ for i in range(2, num_splits):
90
+ output = output - torch.nn.functional.sigmoid(lse_tensors[i] - logsumexp) * (
91
+ output - outputs[i]
92
+ )
93
+ logsumexp = logsumexp - torch.nn.functional.logsigmoid(
94
+ logsumexp - lse_tensors[i]
95
+ )
96
+
97
+ output = output.to(output_type)
98
+ logsumexp = logsumexp.squeeze(-1)
99
+
100
+ assert logsumexp.dim() == 3
101
+ assert logsumexp.shape[0] == batch
102
+ assert logsumexp.shape[1] == seqlen
103
+ assert logsumexp.shape[2] == heads
104
+
105
+ return output, logsumexp
106
+
107
+
108
+ @_maybe_torch_compile(fullgraph=True)
109
+ def _merge_attentions_compile(
110
+ outputs: List[Tensor], lse_tensors: List[Tensor]
111
+ ) -> Tuple[Tensor, Tensor]:
112
+ return _merge_attentions_fn(outputs, lse_tensors)
113
+
114
+
115
+ def _merge_attentions_op(
116
+ outputs: List[Tensor], lse_tensors: List[Tensor], torch_compile: bool = True
117
+ ) -> Tuple[Tensor, Tensor]:
118
+
119
+ if not torch_compile:
120
+ return _merge_attentions_fn(
121
+ [output.contiguous() for output in outputs],
122
+ [lse.contiguous() for lse in lse_tensors],
123
+ )
124
+
125
+ return _merge_attentions_compile(
126
+ [output.contiguous() for output in outputs],
127
+ [lse.contiguous() for lse in lse_tensors],
128
+ )
129
+
130
+
131
+ class MergeAttentionsAutogradFn(Function):
132
+ @staticmethod
133
+ @amp_fwd
134
+ def forward(
135
+ ctx,
136
+ *args,
137
+ ) -> Tuple[Tensor, Tensor]:
138
+
139
+ assert len(args) >= 5, (
140
+ "Expected at least 5 args (two outputs, two lse tensors, 1 torch compile flag) "
141
+ + f"in attention merge, got {len(args)}."
142
+ )
143
+ assert (len(args) - 1) % 2 == 0, (
144
+ f"Expected pairs of outputs and lse tensors, got {len(args)-1} args "
145
+ + "(excluding torch compile flag)"
146
+ )
147
+ num_pairs = (len(args) - 1) // 2
148
+ assert num_pairs >= 2
149
+
150
+ torch_compile = args[-1]
151
+ outputs = args[:num_pairs]
152
+ lses = args[num_pairs:-1]
153
+
154
+ assert len(outputs) == len(lses), (
155
+ "Expected the same number of outputs as logsumexp tensors, "
156
+ + f"got {len(outputs)=}, {len(lses)=}"
157
+ )
158
+
159
+ merged_output, merged_lse = _merge_attentions_op(
160
+ outputs, # type: ignore[arg-type]
161
+ lses, # type: ignore[arg-type]
162
+ torch_compile=torch_compile,
163
+ )
164
+
165
+ ctx.num_pairs = num_pairs
166
+ ctx.save_for_backward(merged_output, merged_lse, *outputs, *lses)
167
+
168
+ return merged_output, merged_lse
169
+
170
+ @staticmethod
171
+ @amp_bwd
172
+ def backward(ctx, grad_out: Tensor, grad_lse: Tensor) -> Tuple:
173
+
174
+ num_pairs = ctx.num_pairs
175
+ merged_output, merged_lse = ctx.saved_tensors[:2]
176
+ outputs = ctx.saved_tensors[2 : num_pairs + 2]
177
+ lses = ctx.saved_tensors[num_pairs + 2 :]
178
+
179
+ # Outputs and LSEs from the originating attention ops must be replaced with
180
+ # the merged ones inplace so that we get correct behavior, and not break torch.compile
181
+ # graphs in the process.
182
+ for output, lse in zip(outputs, lses):
183
+ output.data.copy_(merged_output.data.reshape(output.shape))
184
+ lse.data.copy_(merged_lse.data.reshape(lse.shape))
185
+
186
+ return (
187
+ *(grad_out for _ in range(num_pairs)),
188
+ *(grad_lse for _ in range(num_pairs)),
189
+ None,
190
+ )
191
+
192
+
193
+ def merge_attentions(
194
+ outputs: List[Tensor],
195
+ lse_tensors: List[Tensor],
196
+ torch_compile: bool = True,
197
+ use_autograd_fix: bool = True,
198
+ ) -> Tuple[Tensor, Tensor]:
199
+ """Takes multiple attention *outputs* originating from the same query tensor, and their
200
+ corresponding logsumexps, and merges them as if their context (key/value pair) had been
201
+ concatenated.
202
+
203
+ This operation is used to implement cross-neighborhood attention, and can also be used for
204
+ distributed setups, such as context-parallelism.
205
+
206
+ This operation also attempts to use `torch.compile` to fuse the elementwise operations. This
207
+ can be disabled by passing `torch_compile=False`.
208
+
209
+ Parameters:
210
+ outputs (List[Tensor]): List of 4-D attention output tensors, with the heads last layout
211
+ (`[batch, seqlen, heads, head_dim]`)
212
+
213
+ lse_tensors (List[Tensor]): List of 3-D logsumexp tensors, with the heads last layout
214
+ (`[batch, seqlen, heads]`)
215
+
216
+ torch_compile (bool): Attempt to use `torch.compile` to fuse the underlying elementwise
217
+ operations. Default: True.
218
+
219
+ use_autograd_fix (bool): fix backpropagation by using a custom autograd function. Only
220
+ compatible with fused attention operations (Flash/FMHA/FNA), only as long as the inputs
221
+ of this function are (views) of outputs from said attention operation.
222
+ NATTEN's tests (tests/test_attn_merge.py) only verify correctness for when using
223
+ attention operations from NATTEN. Integration for non-NATTEN ops must be verified by the
224
+ end user.
225
+ This must be disabled when using unfused Attention, which includes Flex without
226
+ torch.compile. Default: True.
227
+
228
+ Returns:
229
+ output (Tensor): merged attention output.
230
+
231
+ logsumexp (Tensor): updated logsumexp.
232
+ """
233
+
234
+ if len(outputs) < 2:
235
+ raise ValueError("`merge_attentions` expects at least two tensors.")
236
+
237
+ if len(outputs) != len(lse_tensors):
238
+ raise ValueError(
239
+ "`merge_attentions` expected number of outputs and LSE tensors to match, "
240
+ f"got {len(outputs)=} != {len(lse_tensors)}."
241
+ )
242
+ assert len(outputs) == len(lse_tensors)
243
+
244
+ requires_grad = outputs[0].requires_grad
245
+ shape = outputs[0].shape
246
+
247
+ for i, (output, lse) in enumerate(zip(outputs, lse_tensors)):
248
+ if output.dim() != 4 or not output.is_contiguous():
249
+ raise ValueError(
250
+ "Output tensors must be rank-4 tensors with (batch, seq, heads, dim), "
251
+ f"but got output {i} with rank={output.dim()}."
252
+ )
253
+
254
+ if output.shape != shape:
255
+ raise ValueError(
256
+ f"Output tensors must must match in shape, but got output {i} "
257
+ f"with shape={output.shape}."
258
+ )
259
+
260
+ if lse.dim() != 3:
261
+ raise ValueError(
262
+ "LSE tensors must be rank-3 tensors with (batch, seq, heads)"
263
+ f"but got LSE {i} with rank={lse.dim()}."
264
+ )
265
+
266
+ if lse.shape != shape[:3]:
267
+ raise ValueError(
268
+ f"LSE tensors must must match outputs in shape except last dim "
269
+ f"({shape=}), but got LSE {i} with shape={lse.shape}."
270
+ )
271
+
272
+ if output.requires_grad and not requires_grad:
273
+ raise ValueError(
274
+ "Either all attentions must require grad, or none of them."
275
+ )
276
+
277
+ # This path is the correct way to do backward pass, but since we can't have lists as inputs to
278
+ # autograd functions, we're forced to specialize it for 2-way for now.
279
+ if use_autograd_fix:
280
+ merged_output, merged_lse = MergeAttentionsAutogradFn.apply(
281
+ *outputs, *lse_tensors, torch_compile
282
+ )
283
+ return merged_output, merged_lse
284
+
285
+ return _merge_attentions_op(
286
+ [output.contiguous() for output in outputs],
287
+ [lse.contiguous() for lse in lse_tensors],
288
+ torch_compile=torch_compile,
289
+ )
290
+
291
+
292
+ __all__ = ["merge_attentions"]
build/torch210-cu126-aarch64-linux/backends/__init__.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import List
26
+
27
+ from ..utils import log
28
+
29
+ logger = log.get_logger(__name__)
30
+
31
+ import torch # noqa: F401
32
+ from torch import Tensor
33
+
34
+ from ..backends.blackwell_fmha import cutlass_blackwell_fmha
35
+ from ..backends.blackwell_fna import (
36
+ cutlass_blackwell_fna_generic,
37
+ na1d_cutlass_blackwell_fna,
38
+ na2d_cutlass_blackwell_fna,
39
+ na3d_cutlass_blackwell_fna,
40
+ )
41
+ from ..backends.configs import (
42
+ get_bwd_configs_for_cutlass_blackwell_fmha,
43
+ get_bwd_configs_for_cutlass_blackwell_fna,
44
+ get_bwd_configs_for_cutlass_fmha,
45
+ get_bwd_configs_for_cutlass_fna,
46
+ get_bwd_configs_for_cutlass_hopper_fmha,
47
+ get_bwd_configs_for_cutlass_hopper_fna,
48
+ get_configs_for_cutlass_blackwell_fmha,
49
+ get_configs_for_cutlass_blackwell_fna,
50
+ get_configs_for_cutlass_fmha,
51
+ get_configs_for_cutlass_fna,
52
+ get_configs_for_cutlass_hopper_fmha,
53
+ get_configs_for_cutlass_hopper_fna,
54
+ get_configs_for_flex_fmha,
55
+ get_configs_for_flex_fna,
56
+ )
57
+ from ..backends.configs.checks import (
58
+ can_run_cutlass_blackwell_fmha,
59
+ can_run_cutlass_blackwell_fna,
60
+ can_run_cutlass_fna,
61
+ can_run_cutlass_hopper_fmha,
62
+ can_run_cutlass_hopper_fna,
63
+ can_run_flex_attention,
64
+ )
65
+ from ..backends.flex import (
66
+ flex_fmha,
67
+ flex_fna_generic,
68
+ na1d_flex,
69
+ na2d_flex,
70
+ na3d_flex,
71
+ )
72
+ from ..backends.fmha import can_run_cutlass_fmha, cutlass_fmha
73
+ from ..backends.fna import (
74
+ cutlass_fna_generic,
75
+ na1d_cutlass_fna,
76
+ na2d_cutlass_fna,
77
+ na3d_cutlass_fna,
78
+ )
79
+ from ..backends.hopper_fmha import cutlass_hopper_fmha
80
+ from ..backends.hopper_fna import (
81
+ cutlass_hopper_fna_generic,
82
+ na1d_cutlass_hopper_fna,
83
+ na2d_cutlass_hopper_fna,
84
+ na3d_cutlass_hopper_fna,
85
+ )
86
+
87
+
88
+ def choose_backend(
89
+ query: Tensor, key: Tensor, value: Tensor, torch_compile: bool
90
+ ) -> str:
91
+ if can_run_cutlass_blackwell_fna(query, key, value):
92
+ logger.debug("Backend not set; picked Blackwell FNA kernel.")
93
+ return "blackwell-fna"
94
+
95
+ if can_run_cutlass_hopper_fna(query, key, value):
96
+ logger.debug("Backend not set; picked Hopper FNA kernel.")
97
+ return "hopper-fna"
98
+
99
+ if can_run_cutlass_fna(query, key, value):
100
+ logger.debug("Backend not set; picked CUTLASS (2.X) FNA kernel.")
101
+ return "cutlass-fna"
102
+
103
+ if can_run_flex_attention(query, key, value, torch_compile=torch_compile):
104
+ logger.debug("Backend not set; picked Flex Attention kernel.")
105
+ return "flex-fna"
106
+
107
+ raise NotImplementedError(
108
+ "NATTEN could not find a suitable backend for this use case. "
109
+ "Run with NATTEN_LOG_LEVEL=DEBUG to find out why."
110
+ )
111
+
112
+
113
+ def choose_fmha_backend(
114
+ query: Tensor,
115
+ key: Tensor,
116
+ value: Tensor,
117
+ is_causal: bool,
118
+ is_varlen: bool,
119
+ torch_compile: bool,
120
+ ) -> str:
121
+ if can_run_cutlass_blackwell_fmha(
122
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen
123
+ ):
124
+ logger.debug("Backend not set; picked Blackwell FMHA kernel.")
125
+ return "blackwell-fmha"
126
+
127
+ if can_run_cutlass_hopper_fmha(
128
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen
129
+ ):
130
+ logger.debug("Backend not set; picked Hopper FMHA kernel.")
131
+ return "hopper-fmha"
132
+
133
+ if can_run_cutlass_fmha(
134
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen
135
+ ):
136
+ logger.debug("Backend not set; picked CUTLASS (2.X) FMHA kernel.")
137
+ return "cutlass-fmha"
138
+
139
+ if can_run_flex_attention(
140
+ query,
141
+ key,
142
+ value,
143
+ is_causal=is_causal,
144
+ is_varlen=is_varlen,
145
+ torch_compile=torch_compile,
146
+ ):
147
+ logger.debug("Backend not set; picked Flex Attention kernel.")
148
+ return "flex-fmha"
149
+
150
+ raise NotImplementedError(
151
+ "NATTEN could not find a suitable backend for this FMHA use case. "
152
+ "Run with NATTEN_LOG_LEVEL=DEBUG to find out why."
153
+ )
154
+
155
+
156
+ def get_compatible_backends(
157
+ query: Tensor, key: Tensor, value: Tensor, torch_compile: bool
158
+ ) -> List[str]:
159
+ compatible_backends = []
160
+ if can_run_cutlass_blackwell_fna(query, key, value):
161
+ compatible_backends.append("blackwell-fna")
162
+
163
+ if can_run_cutlass_hopper_fna(query, key, value):
164
+ compatible_backends.append("hopper-fna")
165
+
166
+ if can_run_cutlass_fna(query, key, value):
167
+ compatible_backends.append("cutlass-fna")
168
+
169
+ if can_run_flex_attention(query, key, value, torch_compile=torch_compile):
170
+ compatible_backends.append("flex-fna")
171
+
172
+ return compatible_backends
173
+
174
+
175
+ def get_compatible_fmha_backends(
176
+ query: Tensor,
177
+ key: Tensor,
178
+ value: Tensor,
179
+ is_causal: bool,
180
+ is_varlen: bool,
181
+ torch_compile: bool,
182
+ ) -> List[str]:
183
+ compatible_backends = []
184
+ if can_run_cutlass_blackwell_fmha(
185
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen
186
+ ):
187
+ compatible_backends.append("blackwell-fmha")
188
+
189
+ if can_run_cutlass_hopper_fmha(
190
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen
191
+ ):
192
+ compatible_backends.append("hopper-fmha")
193
+
194
+ if can_run_cutlass_fmha(
195
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen
196
+ ):
197
+ compatible_backends.append("cutlass-fmha")
198
+
199
+ if can_run_flex_attention(
200
+ query,
201
+ key,
202
+ value,
203
+ is_causal=is_causal,
204
+ is_varlen=is_varlen,
205
+ torch_compile=torch_compile,
206
+ ):
207
+ compatible_backends.append("flex-fmha")
208
+
209
+ return compatible_backends
210
+
211
+
212
+ __all__ = [
213
+ "can_run_cutlass_fmha",
214
+ "can_run_cutlass_fna",
215
+ "can_run_cutlass_blackwell_fmha",
216
+ "can_run_cutlass_blackwell_fna",
217
+ "can_run_cutlass_hopper_fmha",
218
+ "can_run_cutlass_hopper_fna",
219
+ "can_run_flex_attention",
220
+ "cutlass_fmha",
221
+ "cutlass_fna_generic",
222
+ "na1d_cutlass_fna",
223
+ "na2d_cutlass_fna",
224
+ "na3d_cutlass_fna",
225
+ "cutlass_blackwell_fmha",
226
+ "cutlass_blackwell_fna_generic",
227
+ "cutlass_hopper_fmha",
228
+ "cutlass_hopper_fna_generic",
229
+ "na1d_cutlass_blackwell_fna",
230
+ "na2d_cutlass_blackwell_fna",
231
+ "na3d_cutlass_blackwell_fna",
232
+ "flex_fmha",
233
+ "flex_fna_generic",
234
+ "na1d_flex",
235
+ "na2d_flex",
236
+ "na3d_flex",
237
+ "na1d_cutlass_hopper_fna",
238
+ "na2d_cutlass_hopper_fna",
239
+ "na3d_cutlass_hopper_fna",
240
+ "get_bwd_configs_for_cutlass_fmha",
241
+ "get_bwd_configs_for_cutlass_fna",
242
+ "get_bwd_configs_for_cutlass_blackwell_fmha",
243
+ "get_bwd_configs_for_cutlass_blackwell_fna",
244
+ "get_configs_for_cutlass_blackwell_fmha",
245
+ "get_configs_for_cutlass_blackwell_fna",
246
+ "get_configs_for_cutlass_fmha",
247
+ "get_configs_for_cutlass_fna",
248
+ "get_configs_for_cutlass_hopper_fmha",
249
+ "get_bwd_configs_for_cutlass_hopper_fmha",
250
+ "get_configs_for_cutlass_hopper_fna",
251
+ "get_bwd_configs_for_cutlass_hopper_fna",
252
+ "get_configs_for_flex_fmha",
253
+ "get_configs_for_flex_fna",
254
+ ]
build/torch210-cu126-aarch64-linux/backends/blackwell_fmha.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import blackwell_fmha_backward, blackwell_fmha_forward
35
+ from ..backends.configs.checks import can_run_cutlass_blackwell_fmha
36
+ from ..backends.configs.cutlass_blackwell import (
37
+ check_cutlass_blackwell_fmha_backward_config,
38
+ check_cutlass_blackwell_fmha_forward_config,
39
+ )
40
+ from .._types import (
41
+ CutlassBlackwellFmhaBackwardConfigType,
42
+ CutlassBlackwellFmhaForwardConfigType,
43
+ NoneType,
44
+ )
45
+ from ..utils import log
46
+ from ..utils.checks import fmha_tensor_checks, varlen_tensor_checks
47
+
48
+ logger = log.get_logger(__name__)
49
+
50
+
51
+ class CutlassBlackwellFmhaAutogradFn(Function):
52
+ @staticmethod
53
+ @amp_fwd
54
+ def forward(
55
+ ctx,
56
+ query: Tensor,
57
+ key: Tensor,
58
+ value: Tensor,
59
+ is_causal: bool,
60
+ scale: float,
61
+ forward_config: CutlassBlackwellFmhaForwardConfigType,
62
+ backward_config: CutlassBlackwellFmhaBackwardConfigType,
63
+ run_persistent_kernel: bool,
64
+ cumulative_seqlen_Q: Optional[Tensor],
65
+ cumulative_seqlen_KV: Optional[Tensor],
66
+ max_seqlen_Q: int,
67
+ max_seqlen_KV: int,
68
+ ) -> Tuple[Tensor, Tensor]:
69
+ query = query.contiguous()
70
+ key = key.contiguous()
71
+ value = value.contiguous()
72
+
73
+ q_tile_size, kv_tile_size = forward_config
74
+
75
+ output, logsumexp = blackwell_fmha_forward(
76
+ query,
77
+ key,
78
+ value,
79
+ is_causal,
80
+ scale,
81
+ q_tile_size,
82
+ kv_tile_size,
83
+ run_persistent_kernel,
84
+ cumulative_seqlen_Q,
85
+ cumulative_seqlen_KV,
86
+ max_seqlen_Q,
87
+ max_seqlen_KV,
88
+ )
89
+
90
+ ctx.save_for_backward(
91
+ query,
92
+ key,
93
+ value,
94
+ logsumexp,
95
+ output,
96
+ cumulative_seqlen_Q,
97
+ cumulative_seqlen_KV,
98
+ )
99
+ ctx.scale = scale
100
+ ctx.is_causal = is_causal
101
+ ctx.max_seqlen_Q = max_seqlen_Q
102
+ ctx.max_seqlen_KV = max_seqlen_KV
103
+ ctx.backward_config = backward_config
104
+ # Always record determinism behavior during forward pass (forward pass itself is
105
+ # deterministic anyway).
106
+ # Determinism could be limited to part of the program, which means during forward pass
107
+ # it'll be true, but on .backward() call, if it's been turned off, it will stay off when we
108
+ # get to this operation's backward call.
109
+ ctx.deterministic = torch.are_deterministic_algorithms_enabled()
110
+
111
+ return output, logsumexp
112
+
113
+ @staticmethod
114
+ @amp_bwd
115
+ def backward(ctx, grad_out: Tensor, grad_lse: Tensor) -> Tuple[
116
+ Tensor,
117
+ Tensor,
118
+ Tensor,
119
+ NoneType,
120
+ NoneType,
121
+ NoneType,
122
+ NoneType,
123
+ NoneType,
124
+ # varlen
125
+ NoneType,
126
+ NoneType,
127
+ NoneType,
128
+ NoneType,
129
+ ]:
130
+ (
131
+ query,
132
+ key,
133
+ value,
134
+ logsumexp,
135
+ output,
136
+ cumulative_seqlen_Q,
137
+ cumulative_seqlen_KV,
138
+ ) = ctx.saved_tensors
139
+ d_output = grad_out.contiguous() # noqa: F841
140
+
141
+ q_tile_size, k_tile_size = ctx.backward_config
142
+
143
+ d_query, d_key, d_value = blackwell_fmha_backward(
144
+ query,
145
+ key,
146
+ value,
147
+ output,
148
+ d_output,
149
+ logsumexp,
150
+ ctx.is_causal,
151
+ ctx.scale,
152
+ q_tile_size,
153
+ k_tile_size,
154
+ cumulative_seqlen_Q,
155
+ cumulative_seqlen_KV,
156
+ ctx.max_seqlen_Q,
157
+ ctx.max_seqlen_KV,
158
+ ctx.deterministic,
159
+ )
160
+
161
+ return (
162
+ d_query,
163
+ d_key,
164
+ d_value,
165
+ None,
166
+ None,
167
+ None,
168
+ None,
169
+ None,
170
+ None,
171
+ None,
172
+ None,
173
+ None,
174
+ )
175
+
176
+
177
+ def cutlass_blackwell_fmha(
178
+ query: Tensor,
179
+ key: Tensor,
180
+ value: Tensor,
181
+ is_causal: bool = False,
182
+ scale: Optional[float] = None,
183
+ q_tile_size: Optional[int] = None,
184
+ kv_tile_size: Optional[int] = None,
185
+ run_persistent_kernel: bool = False,
186
+ backward_q_tile_size: Optional[int] = None,
187
+ backward_kv_tile_size: Optional[int] = None,
188
+ return_lse: bool = False,
189
+ # varlen parameters
190
+ cumulative_seqlen_Q: Optional[Tensor] = None,
191
+ cumulative_seqlen_KV: Optional[Tensor] = None,
192
+ max_seqlen_Q: int = 0,
193
+ max_seqlen_KV: int = 0,
194
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
195
+
196
+ fmha_tensor_checks(
197
+ query,
198
+ key,
199
+ value,
200
+ must_match_head_dims=True,
201
+ supports_gqa_mqa=True,
202
+ backend_name="Blackwell FMHA",
203
+ )
204
+
205
+ (
206
+ cumulative_seqlen_Q,
207
+ cumulative_seqlen_KV,
208
+ max_seqlen_Q,
209
+ max_seqlen_KV,
210
+ ) = varlen_tensor_checks(
211
+ query=query,
212
+ key=key,
213
+ value=value,
214
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
215
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
216
+ max_seqlen_Q=max_seqlen_Q,
217
+ max_seqlen_KV=max_seqlen_KV,
218
+ )
219
+ is_varlen = cumulative_seqlen_Q is not None
220
+
221
+ assert can_run_cutlass_blackwell_fmha(
222
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen, raise_error=True
223
+ )
224
+
225
+ forward_config = check_cutlass_blackwell_fmha_forward_config(
226
+ input_tensor=query, q_tile_size=q_tile_size, kv_tile_size=kv_tile_size
227
+ )
228
+ backward_config = check_cutlass_blackwell_fmha_backward_config(
229
+ input_tensor=query,
230
+ q_tile_size=backward_q_tile_size,
231
+ kv_tile_size=backward_kv_tile_size,
232
+ )
233
+
234
+ scale = scale or query.shape[-1] ** -0.5
235
+
236
+ output, lse = CutlassBlackwellFmhaAutogradFn.apply(
237
+ query,
238
+ key,
239
+ value,
240
+ is_causal,
241
+ scale,
242
+ forward_config,
243
+ backward_config,
244
+ run_persistent_kernel,
245
+ cumulative_seqlen_Q,
246
+ cumulative_seqlen_KV,
247
+ max_seqlen_Q,
248
+ max_seqlen_KV,
249
+ )
250
+
251
+ if return_lse:
252
+ return output, lse
253
+
254
+ return output
build/torch210-cu126-aarch64-linux/backends/blackwell_fna.py ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import (
35
+ blackwell_na1d_backward,
36
+ blackwell_na1d_forward,
37
+ blackwell_na2d_backward,
38
+ blackwell_na2d_forward,
39
+ blackwell_na3d_backward,
40
+ blackwell_na3d_forward,
41
+ )
42
+ from ..backends.configs.checks import can_run_cutlass_blackwell_fna
43
+ from ..backends.configs.cutlass_blackwell import (
44
+ check_cutlass_blackwell_fna_backward_config,
45
+ check_cutlass_blackwell_fna_forward_config,
46
+ )
47
+ from ..token_permute import token_permute_operation, token_unpermute_operation
48
+ from .._types import (
49
+ CausalArg1DTypeOrDed,
50
+ CausalArg2DTypeOrDed,
51
+ CausalArg3DTypeOrDed,
52
+ CausalArgType,
53
+ CausalArgTypeOrDed,
54
+ CutlassBlackwellFnaBackwardConfigType,
55
+ CutlassBlackwellFnaForwardConfigType,
56
+ Dimension1DType,
57
+ Dimension1DTypeOrDed,
58
+ Dimension2DType,
59
+ Dimension2DTypeOrDed,
60
+ Dimension3DType,
61
+ Dimension3DTypeOrDed,
62
+ DimensionType,
63
+ DimensionTypeOrDed,
64
+ NoneType,
65
+ )
66
+ from ..utils.checks import (
67
+ check_all_args,
68
+ check_args_against_input,
69
+ na_tensor_checks,
70
+ )
71
+
72
+
73
+ def make_cutlass_blackwell_fna_autograd_fn(na_dim):
74
+ assert na_dim in [1, 2, 3]
75
+
76
+ FORWARD_OPS = {
77
+ 1: blackwell_na1d_forward,
78
+ 2: blackwell_na2d_forward,
79
+ 3: blackwell_na3d_forward,
80
+ }
81
+
82
+ BACKWARD_OPS = {
83
+ 1: blackwell_na1d_backward,
84
+ 2: blackwell_na2d_backward,
85
+ 3: blackwell_na3d_backward,
86
+ }
87
+
88
+ class CutlassBlackwellFnaGenericAutogradFn(Function):
89
+ @staticmethod
90
+ @amp_fwd
91
+ def forward(
92
+ ctx,
93
+ query: Tensor,
94
+ key: Tensor,
95
+ value: Tensor,
96
+ kernel_size: DimensionType,
97
+ stride: DimensionType,
98
+ dilation: DimensionType,
99
+ is_causal: CausalArgType,
100
+ scale: float,
101
+ forward_config: CutlassBlackwellFnaForwardConfigType,
102
+ backward_config: CutlassBlackwellFnaBackwardConfigType,
103
+ run_persistent_kernel: bool,
104
+ ) -> Tuple[Tensor, Tensor]:
105
+ kernel_size, stride, dilation, is_causal = check_all_args(
106
+ na_dim, kernel_size, stride, dilation, is_causal
107
+ )
108
+
109
+ q_tile_shape, kv_tile_shape = forward_config
110
+
111
+ # Token permute begin
112
+ query_perm, qkv_shape, q_shape = token_permute_operation(
113
+ query,
114
+ tile_shape=q_tile_shape,
115
+ dilation=dilation,
116
+ flip_tiled_dims=True,
117
+ )
118
+ key_perm, _, k_shape = token_permute_operation(
119
+ key, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=True
120
+ )
121
+ value_perm, _, v_shape = token_permute_operation(
122
+ value, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=True
123
+ )
124
+
125
+ assert k_shape == v_shape
126
+ kv_shape = k_shape
127
+ # Token permute end
128
+
129
+ query_perm = query_perm.contiguous()
130
+ key_perm = key_perm.contiguous()
131
+ value_perm = value_perm.contiguous()
132
+
133
+ output_perm, logsumexp_perm = FORWARD_OPS[na_dim](
134
+ query_perm,
135
+ key_perm,
136
+ value_perm,
137
+ kernel_size,
138
+ stride,
139
+ dilation,
140
+ is_causal,
141
+ scale,
142
+ q_shape,
143
+ kv_shape,
144
+ qkv_shape,
145
+ q_tile_shape,
146
+ kv_tile_shape,
147
+ run_persistent_kernel,
148
+ )
149
+
150
+ # Token un-permute begin
151
+ output = token_unpermute_operation(
152
+ output_perm,
153
+ token_layout_shape=qkv_shape,
154
+ tile_shape=q_tile_shape,
155
+ dilation=dilation,
156
+ flip_tiled_dims=True,
157
+ )
158
+ logsumexp = token_unpermute_operation(
159
+ logsumexp_perm.unsqueeze(-1),
160
+ token_layout_shape=qkv_shape,
161
+ tile_shape=q_tile_shape,
162
+ dilation=dilation,
163
+ flip_tiled_dims=True,
164
+ ).squeeze(-1)
165
+ # Token un-permute end
166
+
167
+ ctx.save_for_backward(query, key, value, logsumexp, output)
168
+ ctx.kernel_size = kernel_size
169
+ ctx.stride = stride
170
+ ctx.dilation = dilation
171
+ ctx.is_causal = is_causal
172
+ ctx.scale = scale
173
+ ctx.backward_config = backward_config
174
+ # Always record determinism behavior during forward pass (forward pass itself is
175
+ # deterministic anyway).
176
+ # Determinism could be limited to part of the program, which means during forward pass
177
+ # it'll be true, but on .backward() call, if it's been turned off, it will stay off when we
178
+ # get to this operation's backward call.
179
+ ctx.deterministic = torch.are_deterministic_algorithms_enabled()
180
+
181
+ return output, logsumexp
182
+
183
+ @staticmethod
184
+ @amp_bwd
185
+ def backward(ctx, d_output: Tensor, d_lse: Tensor) -> Tuple[
186
+ Tensor,
187
+ Tensor,
188
+ Tensor,
189
+ NoneType,
190
+ NoneType,
191
+ NoneType,
192
+ NoneType,
193
+ NoneType,
194
+ NoneType,
195
+ NoneType,
196
+ NoneType,
197
+ ]:
198
+ query, key, value, logsumexp, output = ctx.saved_tensors
199
+ kernel_size, stride, dilation, is_causal, scale = (
200
+ ctx.kernel_size,
201
+ ctx.stride,
202
+ ctx.dilation,
203
+ ctx.is_causal,
204
+ ctx.scale,
205
+ )
206
+
207
+ q_tile_shape, kv_tile_shape = ctx.backward_config
208
+
209
+ if ctx.deterministic:
210
+ raise RuntimeError(
211
+ "Blackwell FNA backward pass does not have a deterministic mode, "
212
+ "but PyTorch's deterministic algorithms were enabled. To proceed, "
213
+ "you must either disable torch's deterministic mode, or choose a "
214
+ "different backend."
215
+ )
216
+
217
+ # Token permute begin
218
+
219
+ query_perm, qkv_shape, q_shape = token_permute_operation(
220
+ query, tile_shape=q_tile_shape, dilation=dilation, flip_tiled_dims=True
221
+ )
222
+ output_perm, _, o_shape = token_permute_operation(
223
+ output, tile_shape=q_tile_shape, dilation=dilation, flip_tiled_dims=True
224
+ )
225
+ d_output_perm, _, d_o_shape = token_permute_operation(
226
+ d_output,
227
+ tile_shape=q_tile_shape,
228
+ dilation=dilation,
229
+ flip_tiled_dims=True,
230
+ )
231
+ logsumexp_perm, _, _ = token_permute_operation(
232
+ logsumexp.unsqueeze(-1),
233
+ tile_shape=q_tile_shape,
234
+ dilation=dilation,
235
+ flip_tiled_dims=True,
236
+ )
237
+ key_perm, _, k_shape = token_permute_operation(
238
+ key, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=True
239
+ )
240
+ value_perm, _, v_shape = token_permute_operation(
241
+ value, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=True
242
+ )
243
+
244
+ assert q_shape == o_shape == d_o_shape
245
+ assert k_shape == v_shape
246
+ kv_shape = k_shape
247
+ # Token permute end
248
+
249
+ query_perm = query_perm.contiguous()
250
+ key_perm = key_perm.contiguous()
251
+ value_perm = value_perm.contiguous()
252
+ output_perm = output_perm.contiguous()
253
+ d_output_perm = d_output_perm.contiguous()
254
+ logsumexp_perm = logsumexp_perm.squeeze(-1)
255
+
256
+ d_query_perm, d_key_perm, d_value_perm = BACKWARD_OPS[na_dim](
257
+ query_perm,
258
+ key_perm,
259
+ value_perm,
260
+ output_perm,
261
+ d_output_perm,
262
+ logsumexp_perm,
263
+ kernel_size,
264
+ stride,
265
+ dilation,
266
+ is_causal,
267
+ scale,
268
+ q_shape,
269
+ kv_shape,
270
+ qkv_shape,
271
+ q_tile_shape,
272
+ kv_tile_shape,
273
+ )
274
+
275
+ # Token un-permute begin
276
+ d_query = token_unpermute_operation(
277
+ d_query_perm,
278
+ token_layout_shape=qkv_shape,
279
+ tile_shape=q_tile_shape,
280
+ dilation=dilation,
281
+ flip_tiled_dims=True,
282
+ )
283
+ d_key = token_unpermute_operation(
284
+ d_key_perm,
285
+ token_layout_shape=qkv_shape,
286
+ tile_shape=kv_tile_shape,
287
+ dilation=dilation,
288
+ flip_tiled_dims=True,
289
+ )
290
+ d_value = token_unpermute_operation(
291
+ d_value_perm,
292
+ token_layout_shape=qkv_shape,
293
+ tile_shape=kv_tile_shape,
294
+ dilation=dilation,
295
+ flip_tiled_dims=True,
296
+ )
297
+ # Token un-permute end
298
+
299
+ assert d_query.shape == query.shape
300
+ assert d_key.shape == key.shape
301
+ assert d_value.shape == value.shape
302
+
303
+ return (
304
+ d_query,
305
+ d_key,
306
+ d_value,
307
+ None,
308
+ None,
309
+ None,
310
+ None,
311
+ None,
312
+ None,
313
+ None,
314
+ None,
315
+ )
316
+
317
+ return CutlassBlackwellFnaGenericAutogradFn
318
+
319
+
320
+ CutlassBlackwellFna1DAutogradFn = make_cutlass_blackwell_fna_autograd_fn(1)
321
+ CutlassBlackwellFna2DAutogradFn = make_cutlass_blackwell_fna_autograd_fn(2)
322
+ CutlassBlackwellFna3DAutogradFn = make_cutlass_blackwell_fna_autograd_fn(3)
323
+
324
+
325
+ CutlassBlackwellFNAAutogradFns = {
326
+ 1: CutlassBlackwellFna1DAutogradFn,
327
+ 2: CutlassBlackwellFna2DAutogradFn,
328
+ 3: CutlassBlackwellFna3DAutogradFn,
329
+ }
330
+
331
+
332
+ def cutlass_blackwell_fna_generic(
333
+ query: Tensor,
334
+ key: Tensor,
335
+ value: Tensor,
336
+ kernel_size: DimensionTypeOrDed,
337
+ stride: DimensionTypeOrDed = 1,
338
+ dilation: DimensionTypeOrDed = 1,
339
+ is_causal: Optional[CausalArgTypeOrDed] = False,
340
+ scale: Optional[float] = None,
341
+ q_tile_shape: Optional[DimensionType] = None,
342
+ kv_tile_shape: Optional[DimensionType] = None,
343
+ backward_q_tile_shape: Optional[DimensionType] = None,
344
+ backward_kv_tile_shape: Optional[DimensionType] = None,
345
+ run_persistent_kernel: bool = False,
346
+ return_lse: bool = False,
347
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
348
+
349
+ na_tensor_checks(
350
+ query, key, value, must_match_head_dims=True, supports_gqa_mqa=True
351
+ )
352
+
353
+ assert can_run_cutlass_blackwell_fna(query, key, value, raise_error=True)
354
+
355
+ na_dim = query.dim() - 3 # batch, heads, head_dim
356
+
357
+ kernel_size, stride, dilation, is_causal = check_all_args(
358
+ na_dim, kernel_size, stride, dilation, is_causal
359
+ )
360
+
361
+ check_args_against_input(
362
+ query,
363
+ kernel_size=kernel_size,
364
+ stride=stride,
365
+ dilation=dilation,
366
+ is_causal=is_causal,
367
+ )
368
+
369
+ forward_config = check_cutlass_blackwell_fna_forward_config(
370
+ input_tensor=query, q_tile_shape=q_tile_shape, kv_tile_shape=kv_tile_shape
371
+ )
372
+
373
+ backward_config = check_cutlass_blackwell_fna_backward_config(
374
+ input_tensor=query,
375
+ q_tile_shape=backward_q_tile_shape,
376
+ kv_tile_shape=backward_kv_tile_shape,
377
+ )
378
+
379
+ scale = scale or query.shape[-1] ** -0.5
380
+
381
+ output, lse = CutlassBlackwellFNAAutogradFns[na_dim].apply(
382
+ query,
383
+ key,
384
+ value,
385
+ kernel_size,
386
+ stride,
387
+ dilation,
388
+ is_causal,
389
+ scale,
390
+ forward_config,
391
+ backward_config,
392
+ run_persistent_kernel,
393
+ )
394
+
395
+ if return_lse:
396
+ return output, lse
397
+
398
+ return output
399
+
400
+
401
+ def na1d_cutlass_blackwell_fna(
402
+ query: Tensor,
403
+ key: Tensor,
404
+ value: Tensor,
405
+ kernel_size: Dimension1DTypeOrDed,
406
+ stride: Dimension1DTypeOrDed = 1,
407
+ dilation: Dimension1DTypeOrDed = 1,
408
+ is_causal: Optional[CausalArg1DTypeOrDed] = False,
409
+ scale: Optional[float] = None,
410
+ q_tile_shape: Optional[Dimension1DType] = None,
411
+ kv_tile_shape: Optional[Dimension1DType] = None,
412
+ backward_q_tile_shape: Optional[Dimension1DType] = None,
413
+ backward_kv_tile_shape: Optional[Dimension1DType] = None,
414
+ run_persistent_kernel: bool = False,
415
+ return_lse: bool = False,
416
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
417
+ return cutlass_blackwell_fna_generic(
418
+ query=query,
419
+ key=key,
420
+ value=value,
421
+ kernel_size=kernel_size,
422
+ stride=stride,
423
+ dilation=dilation,
424
+ is_causal=is_causal,
425
+ scale=scale,
426
+ q_tile_shape=q_tile_shape,
427
+ kv_tile_shape=kv_tile_shape,
428
+ backward_q_tile_shape=backward_q_tile_shape,
429
+ backward_kv_tile_shape=backward_kv_tile_shape,
430
+ run_persistent_kernel=run_persistent_kernel,
431
+ return_lse=return_lse,
432
+ )
433
+
434
+
435
+ def na2d_cutlass_blackwell_fna(
436
+ query: Tensor,
437
+ key: Tensor,
438
+ value: Tensor,
439
+ kernel_size: Dimension2DTypeOrDed,
440
+ stride: Dimension2DTypeOrDed = 1,
441
+ dilation: Dimension2DTypeOrDed = 1,
442
+ is_causal: Optional[CausalArg2DTypeOrDed] = False,
443
+ scale: Optional[float] = None,
444
+ q_tile_shape: Optional[Dimension2DType] = None,
445
+ kv_tile_shape: Optional[Dimension2DType] = None,
446
+ backward_q_tile_shape: Optional[Dimension2DType] = None,
447
+ backward_kv_tile_shape: Optional[Dimension2DType] = None,
448
+ run_persistent_kernel: bool = False,
449
+ return_lse: bool = False,
450
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
451
+ return cutlass_blackwell_fna_generic(
452
+ query=query,
453
+ key=key,
454
+ value=value,
455
+ kernel_size=kernel_size,
456
+ stride=stride,
457
+ dilation=dilation,
458
+ is_causal=is_causal,
459
+ scale=scale,
460
+ q_tile_shape=q_tile_shape,
461
+ kv_tile_shape=kv_tile_shape,
462
+ backward_q_tile_shape=backward_q_tile_shape,
463
+ backward_kv_tile_shape=backward_kv_tile_shape,
464
+ run_persistent_kernel=run_persistent_kernel,
465
+ return_lse=return_lse,
466
+ )
467
+
468
+
469
+ def na3d_cutlass_blackwell_fna(
470
+ query: Tensor,
471
+ key: Tensor,
472
+ value: Tensor,
473
+ kernel_size: Dimension3DTypeOrDed,
474
+ stride: Dimension3DTypeOrDed = 1,
475
+ dilation: Dimension3DTypeOrDed = 1,
476
+ is_causal: Optional[CausalArg3DTypeOrDed] = False,
477
+ scale: Optional[float] = None,
478
+ q_tile_shape: Optional[Dimension3DType] = None,
479
+ kv_tile_shape: Optional[Dimension3DType] = None,
480
+ backward_q_tile_shape: Optional[Dimension3DType] = None,
481
+ backward_kv_tile_shape: Optional[Dimension3DType] = None,
482
+ run_persistent_kernel: bool = False,
483
+ return_lse: bool = False,
484
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
485
+ return cutlass_blackwell_fna_generic(
486
+ query=query,
487
+ key=key,
488
+ value=value,
489
+ kernel_size=kernel_size,
490
+ stride=stride,
491
+ dilation=dilation,
492
+ is_causal=is_causal,
493
+ scale=scale,
494
+ q_tile_shape=q_tile_shape,
495
+ kv_tile_shape=kv_tile_shape,
496
+ backward_q_tile_shape=backward_q_tile_shape,
497
+ backward_kv_tile_shape=backward_kv_tile_shape,
498
+ run_persistent_kernel=run_persistent_kernel,
499
+ return_lse=return_lse,
500
+ )
build/torch210-cu126-aarch64-linux/backends/configs/__init__.py ADDED
@@ -0,0 +1,584 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import List
26
+
27
+ from ...utils import log
28
+
29
+ logger = log.get_logger(__name__)
30
+
31
+ import torch # noqa: F401
32
+ from torch import Tensor
33
+
34
+ from ...backends.configs.checks import (
35
+ can_run_cutlass_blackwell_fmha,
36
+ can_run_cutlass_blackwell_fna,
37
+ can_run_cutlass_fmha,
38
+ can_run_cutlass_fna,
39
+ can_run_cutlass_hopper_fmha,
40
+ can_run_cutlass_hopper_fna,
41
+ can_run_flex_attention,
42
+ )
43
+ from ...backends.configs.cutlass import (
44
+ get_all_tile_shapes_backward as get_all_cutlass_fna_backward_configs,
45
+ get_all_tile_shapes_forward as get_all_cutlass_fna_forward_configs,
46
+ get_all_tile_sizes_backward as get_all_cutlass_fmha_backward_configs,
47
+ get_all_tile_sizes_forward as get_all_cutlass_fmha_forward_configs,
48
+ )
49
+ from ...backends.configs.cutlass_blackwell import (
50
+ get_all_backward_configs as get_all_blackwell_fna_backward_configs,
51
+ get_all_fmha_backward_configs as get_all_blackwell_fmha_backward_configs,
52
+ get_all_fmha_forward_configs as get_all_blackwell_fmha_forward_configs,
53
+ get_all_forward_configs as get_all_blackwell_fna_forward_configs,
54
+ )
55
+ from ...backends.configs.cutlass_hopper import (
56
+ get_all_backward_configs as get_all_hopper_fna_backward_configs,
57
+ get_all_fmha_backward_configs as get_all_hopper_fmha_backward_configs,
58
+ get_all_fmha_forward_configs as get_all_hopper_fmha_forward_configs,
59
+ get_all_forward_configs as get_all_hopper_fna_forward_configs,
60
+ )
61
+ from ...backends.configs.flex import (
62
+ get_all_tile_shapes_forward as get_all_flex_fna_forward_configs,
63
+ get_all_tile_sizes_forward as get_all_flex_fmha_forward_configs,
64
+ )
65
+ from ..._types import (
66
+ CutlassBlackwellFmhaBackwardConfigType,
67
+ CutlassBlackwellFmhaForwardConfigType,
68
+ CutlassBlackwellFnaBackwardConfigType,
69
+ CutlassBlackwellFnaForwardConfigType,
70
+ CutlassFmhaBackwardConfigType,
71
+ CutlassFmhaForwardConfigType,
72
+ CutlassFnaBackwardConfigType,
73
+ CutlassFnaForwardConfigType,
74
+ CutlassHopperFmhaBackwardConfigType,
75
+ CutlassHopperFmhaForwardConfigType,
76
+ CutlassHopperFnaBackwardConfigType,
77
+ CutlassHopperFnaForwardConfigType,
78
+ FlexFmhaForwardConfigType,
79
+ FlexFnaForwardConfigType,
80
+ )
81
+
82
+ ### CUTLASS Blackwell kernels
83
+
84
+
85
+ def get_configs_for_cutlass_blackwell_fmha(
86
+ query: Tensor,
87
+ key: Tensor,
88
+ value: Tensor,
89
+ ) -> List[CutlassBlackwellFmhaForwardConfigType]:
90
+ """Returns Blackwell FMHA configurations compatible with input tensors, if any.
91
+
92
+ Checks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability
93
+ 10.0), and if so, returns *forward pass* configurations compatible with the tensor dtype and
94
+ head dim.
95
+
96
+ Each configuration for this operation is a tuple of two integers: `(q_tile_size,
97
+ kv_tile_size)`. These are arguments to [natten.attention][natten.attention].
98
+
99
+ Args:
100
+ query: Query tensor matching the shape, dtype, and device of your use case.
101
+ key: Key tensor matching the shape, dtype, and device of your use case.
102
+ value: Value tensor matching the shape, dtype, and device of your use case.
103
+
104
+ Returns:
105
+ (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile
106
+ sizes.
107
+ """
108
+
109
+ if not can_run_cutlass_blackwell_fmha(
110
+ query=query,
111
+ key=key,
112
+ value=value,
113
+ is_causal=False,
114
+ is_varlen=False,
115
+ raise_error=False,
116
+ ):
117
+ return []
118
+
119
+ return get_all_blackwell_fmha_forward_configs(input_tensor=query)
120
+
121
+
122
+ def get_bwd_configs_for_cutlass_blackwell_fmha(
123
+ query: Tensor,
124
+ key: Tensor,
125
+ value: Tensor,
126
+ ) -> List[CutlassBlackwellFmhaBackwardConfigType]:
127
+ """Returns Blackwell FMHA backward pass configurations compatible with input tensors, if any.
128
+
129
+ Checks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability
130
+ 10.0), and if so, returns *backward pass* configurations compatible with the tensor dtype and
131
+ head dim.
132
+
133
+ Each configuration for this operation is a tuple of two integers: `(q_tile_size,
134
+ kv_tile_size)`. These are arguments to [natten.attention][natten.attention].
135
+
136
+ Args:
137
+ query: Query tensor matching the shape, dtype, and device of your use case.
138
+ key: Key tensor matching the shape, dtype, and device of your use case.
139
+ value: Value tensor matching the shape, dtype, and device of your use case.
140
+
141
+ Returns:
142
+ (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile
143
+ sizes.
144
+ """
145
+
146
+ if not can_run_cutlass_blackwell_fmha(
147
+ query=query,
148
+ key=key,
149
+ value=value,
150
+ is_causal=False,
151
+ is_varlen=False,
152
+ raise_error=False,
153
+ ):
154
+ return []
155
+
156
+ return get_all_blackwell_fmha_backward_configs(input_tensor=query)
157
+
158
+
159
+ def get_configs_for_cutlass_blackwell_fna(
160
+ query: Tensor,
161
+ key: Tensor,
162
+ value: Tensor,
163
+ ) -> List[CutlassBlackwellFnaForwardConfigType]:
164
+ """Returns Blackwell FNA configurations compatible with input tensors, if any.
165
+
166
+ Checks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability
167
+ 10.0), and if so, returns *forward pass* configurations compatible with the tensor dtype and
168
+ head dim, and according to the rank of the token layout (1D/2D/3D).
169
+
170
+ Each configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,
171
+ kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],
172
+ and [natten.na3d][natten.na3d].
173
+
174
+ Args:
175
+ query: Query tensor matching the shape, dtype, and device of your use case.
176
+ key: Key tensor matching the shape, dtype, and device of your use case.
177
+ value: Value tensor matching the shape, dtype, and device of your use case.
178
+
179
+ Returns:
180
+ (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query
181
+ and KV tile *shapes*.
182
+ """
183
+ if not can_run_cutlass_blackwell_fna(
184
+ query=query, key=key, value=value, raise_error=False
185
+ ):
186
+ return []
187
+
188
+ return get_all_blackwell_fna_forward_configs(input_tensor=query)
189
+
190
+
191
+ def get_bwd_configs_for_cutlass_blackwell_fna(
192
+ query: Tensor,
193
+ key: Tensor,
194
+ value: Tensor,
195
+ ) -> List[CutlassBlackwellFnaBackwardConfigType]:
196
+ """Returns Blackwell FNA backward pass configurations compatible with input tensors, if any.
197
+
198
+ Checks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability
199
+ 10.0), and if so, returns *backward pass* configurations compatible with the tensor dtype and
200
+ head dim, and according to the rank of the token layout (1D/2D/3D).
201
+
202
+ Each configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,
203
+ kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],
204
+ and [natten.na3d][natten.na3d].
205
+
206
+ Args:
207
+ query: Query tensor matching the shape, dtype, and device of your use case.
208
+ key: Key tensor matching the shape, dtype, and device of your use case.
209
+ value: Value tensor matching the shape, dtype, and device of your use case.
210
+
211
+ Returns:
212
+ (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query
213
+ and KV tile *shapes*.
214
+ """
215
+ if not can_run_cutlass_blackwell_fna(
216
+ query=query, key=key, value=value, raise_error=False
217
+ ):
218
+ return []
219
+
220
+ return get_all_blackwell_fna_backward_configs(input_tensor=query)
221
+
222
+
223
+ ### CUTLASS Hopper kernels
224
+
225
+
226
+ def get_configs_for_cutlass_hopper_fmha(
227
+ query: Tensor,
228
+ key: Tensor,
229
+ value: Tensor,
230
+ ) -> List[CutlassHopperFmhaForwardConfigType]:
231
+ """Returns Hopper FMHA configurations compatible with input tensors, if any.
232
+
233
+ Checks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,
234
+ returns *forward pass* configurations compatible with the tensor dtype and head dim.
235
+
236
+ Each configuration for this operation is a tuple of one integer tuple, and another integer:
237
+ `((q_tile_size, kv_tile_size), kernel_schedule)`. These are arguments to
238
+ [natten.attention][natten.attention].
239
+ `kernel_schedule` is specific to Hopper FNA/FMHA only.
240
+
241
+ Args:
242
+ query: Query tensor matching the shape, dtype, and device of your use case.
243
+ key: Key tensor matching the shape, dtype, and device of your use case.
244
+ value: Value tensor matching the shape, dtype, and device of your use case.
245
+
246
+ Returns:
247
+ (List[Tuple[Tuple[int, int], KernelSchedule]]): List of tuples of one tuple of two integers
248
+ corresponding to query and KV tile sizes, and a kernel schedule enum type.
249
+ """
250
+ if not can_run_cutlass_hopper_fmha(
251
+ query=query,
252
+ key=key,
253
+ value=value,
254
+ is_causal=False,
255
+ is_varlen=False,
256
+ raise_error=False,
257
+ ):
258
+ return []
259
+
260
+ return get_all_hopper_fmha_forward_configs(input_tensor=query)
261
+
262
+
263
+ def get_bwd_configs_for_cutlass_hopper_fmha(
264
+ query: Tensor,
265
+ key: Tensor,
266
+ value: Tensor,
267
+ ) -> List[CutlassHopperFmhaBackwardConfigType]:
268
+ """Returns Hopper FMHA backward pass configurations compatible with input tensors, if any.
269
+
270
+ Checks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,
271
+ returns *backward pass* configurations compatible with the tensor dtype and head dim.
272
+
273
+ Each configuration for this operation is an integer tuple:
274
+ `(backward_q_tile_size, backward_kv_tile_size)`. These are arguments to
275
+ [natten.attention][natten.attention].
276
+
277
+ Note that unlike forward pass, kernel schedule is not part of the configuration. All backward
278
+ pass kernels are persistent warp-specialized.
279
+ See CUTLASS's [example 88](https://github.com/NVIDIA/cutlass/tree/main/examples/88_hopper_fmha)
280
+ for more.
281
+
282
+ Args:
283
+ query: Query tensor matching the shape, dtype, and device of your use case.
284
+ key: Key tensor matching the shape, dtype, and device of your use case.
285
+ value: Value tensor matching the shape, dtype, and device of your use case.
286
+
287
+ Returns:
288
+ (List[Tuple[int, int]]): List of integer tuples corresponding to query and KV tile sizes.
289
+ """
290
+ if not can_run_cutlass_hopper_fmha(
291
+ query=query,
292
+ key=key,
293
+ value=value,
294
+ is_causal=False,
295
+ is_varlen=False,
296
+ raise_error=False,
297
+ ):
298
+ return []
299
+
300
+ return get_all_hopper_fmha_backward_configs(input_tensor=query)
301
+
302
+
303
+ def get_configs_for_cutlass_hopper_fna(
304
+ query: Tensor,
305
+ key: Tensor,
306
+ value: Tensor,
307
+ ) -> List[CutlassHopperFnaForwardConfigType]:
308
+ """Returns Hopper FNA configurations compatible with input tensors, if any.
309
+
310
+ Checks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,
311
+ returns *forward pass* configurations compatible with the tensor dtype and head dim.
312
+
313
+ Each configuration for this operation is a tuple of one tuple, and another integer:
314
+ `((q_tile_shape, kv_tile_shape), kernel_schedule)`. These are arguments to
315
+ [natten.na1d][natten.na1d], [natten.na2d][natten.na2d], and [natten.na3d][natten.na3d].
316
+
317
+ `kernel_schedule` is specific to Hopper FNA/FMHA only.
318
+
319
+ Args:
320
+ query: Query tensor matching the shape, dtype, and device of your use case.
321
+ key: Key tensor matching the shape, dtype, and device of your use case.
322
+ value: Value tensor matching the shape, dtype, and device of your use case.
323
+
324
+ Returns:
325
+ (List[Tuple[Tuple[tuple, tuple], KernelSchedule]]): List of tuples of one tuple of two
326
+ shape tuples, corresponding to query and KV tile *shapes*, and a kernel schedule enum
327
+ type.
328
+ """
329
+ if not can_run_cutlass_hopper_fna(
330
+ query=query, key=key, value=value, raise_error=False
331
+ ):
332
+ return []
333
+
334
+ return get_all_hopper_fna_forward_configs(input_tensor=query)
335
+
336
+
337
+ def get_bwd_configs_for_cutlass_hopper_fna(
338
+ query: Tensor,
339
+ key: Tensor,
340
+ value: Tensor,
341
+ ) -> List[CutlassHopperFnaBackwardConfigType]:
342
+ """Returns Hopper FNA backward pass configurations compatible with input tensors, if any.
343
+
344
+ Checks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,
345
+ returns *backward pass* configurations compatible with the tensor dtype and head dim.
346
+
347
+ Each configuration for this operation is a tuple of two tuples:
348
+ `(q_tile_shape, kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d],
349
+ [natten.na2d][natten.na2d], and [natten.na3d][natten.na3d].
350
+
351
+ Note that unlike forward pass, kernel schedule is not part of the configuration. All backward
352
+ pass kernels are persistent warp-specialized.
353
+ See CUTLASS's [example 88](https://github.com/NVIDIA/cutlass/tree/main/examples/88_hopper_fmha)
354
+ for more.
355
+
356
+ Args:
357
+ query: Query tensor matching the shape, dtype, and device of your use case.
358
+ key: Key tensor matching the shape, dtype, and device of your use case.
359
+ value: Value tensor matching the shape, dtype, and device of your use case.
360
+
361
+ Returns:
362
+ (List[Tuple[tuple, tuple]]): List of tuples of two shape tuples, corresponding to query and
363
+ KV tile *shapes*.
364
+ """
365
+ if not can_run_cutlass_hopper_fna(
366
+ query=query, key=key, value=value, raise_error=False
367
+ ):
368
+ return []
369
+
370
+ return get_all_hopper_fna_backward_configs(input_tensor=query)
371
+
372
+
373
+ ### CUTLASS 2.X kernels
374
+
375
+
376
+ def get_configs_for_cutlass_fmha(
377
+ query: Tensor,
378
+ key: Tensor,
379
+ value: Tensor,
380
+ ) -> List[CutlassFmhaForwardConfigType]:
381
+ """Returns CUTLASS FMHA configurations compatible with input tensors, if any.
382
+
383
+ Checks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,
384
+ returns *forward pass* configurations compatible with the specific compute capability, tensor
385
+ dtype and head dim.
386
+
387
+ Each configuration for this operation is a tuple of two integers: `(q_tile_size,
388
+ kv_tile_size)`. These are arguments to [natten.attention][natten.attention].
389
+
390
+ Args:
391
+ query: Query tensor matching the shape, dtype, and device of your use case.
392
+ key: Key tensor matching the shape, dtype, and device of your use case.
393
+ value: Value tensor matching the shape, dtype, and device of your use case.
394
+
395
+ Returns:
396
+ (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile
397
+ sizes.
398
+ """
399
+ if not can_run_cutlass_fmha(
400
+ query=query,
401
+ key=key,
402
+ value=value,
403
+ is_causal=False,
404
+ is_varlen=False,
405
+ raise_error=False,
406
+ ):
407
+ return []
408
+
409
+ return get_all_cutlass_fmha_forward_configs(input_tensor=query)
410
+
411
+
412
+ def get_bwd_configs_for_cutlass_fmha(
413
+ query: Tensor,
414
+ key: Tensor,
415
+ value: Tensor,
416
+ ) -> List[CutlassFmhaBackwardConfigType]:
417
+ """Returns CUTLASS FMHA backward pass configurations compatible with input tensors, if any.
418
+
419
+ Checks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,
420
+ returns *backward pass* configurations compatible with the specific compute capability, tensor
421
+ dtype and head dim.
422
+
423
+ Each configuration for this operation is a tuple of two integers: `(backward_q_tile_size,
424
+ backward_kv_tile_size)`. These are arguments to [natten.attention][natten.attention].
425
+
426
+ Args:
427
+ query: Query tensor matching the shape, dtype, and device of your use case.
428
+ key: Key tensor matching the shape, dtype, and device of your use case.
429
+ value: Value tensor matching the shape, dtype, and device of your use case.
430
+
431
+ Returns:
432
+ (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile
433
+ sizes in the *backward pass*.
434
+ """
435
+ if not can_run_cutlass_fmha(
436
+ query=query,
437
+ key=key,
438
+ value=value,
439
+ is_causal=False,
440
+ is_varlen=False,
441
+ raise_error=False,
442
+ ):
443
+ return []
444
+
445
+ return get_all_cutlass_fmha_backward_configs(
446
+ input_tensor=key if key.shape[-1] >= value.shape[-1] else value
447
+ )
448
+
449
+
450
+ def get_configs_for_cutlass_fna(
451
+ query: Tensor,
452
+ key: Tensor,
453
+ value: Tensor,
454
+ ) -> List[CutlassFnaForwardConfigType]:
455
+ """Returns CUTLASS FNA configurations compatible with input tensors, if any.
456
+
457
+ Checks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,
458
+ returns *forward pass* configurations compatible with the specific compute capability, tensor
459
+ dtype and head dim, and according to the rank of the token layout (1D/2D/3D).
460
+
461
+ Each configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,
462
+ kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],
463
+ and [natten.na3d][natten.na3d].
464
+
465
+ Args:
466
+ query: Query tensor matching the shape, dtype, and device of your use case.
467
+ key: Key tensor matching the shape, dtype, and device of your use case.
468
+ value: Value tensor matching the shape, dtype, and device of your use case.
469
+
470
+ Returns:
471
+ (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query
472
+ and KV tile *shapes*.
473
+ """
474
+ if not can_run_cutlass_fna(query=query, key=key, value=value, raise_error=False):
475
+ return []
476
+
477
+ return get_all_cutlass_fna_forward_configs(input_tensor=query)
478
+
479
+
480
+ def get_bwd_configs_for_cutlass_fna(
481
+ query: Tensor,
482
+ key: Tensor,
483
+ value: Tensor,
484
+ ) -> List[CutlassFnaBackwardConfigType]:
485
+ """Returns CUTLASS FNA backward pass configurations compatible with input tensors, if any.
486
+
487
+ Checks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,
488
+ returns *backward pass* configurations compatible with the specific compute capability, tensor
489
+ dtype and head dim.
490
+
491
+ Each configuration for this operation is a tuple of two integers: `(backward_q_tile_shape,
492
+ backward_kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d],
493
+ [natten.na2d][natten.na2d], and [natten.na3d][natten.na3d].
494
+
495
+ Args:
496
+ query: Query tensor matching the shape, dtype, and device of your use case.
497
+ key: Key tensor matching the shape, dtype, and device of your use case.
498
+ value: Value tensor matching the shape, dtype, and device of your use case.
499
+
500
+ Returns:
501
+ (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query
502
+ and KV tile *shapes* in the *backward pass*.
503
+ """
504
+ if not can_run_cutlass_fna(query=query, key=key, value=value, raise_error=False):
505
+ return []
506
+
507
+ return get_all_cutlass_fna_backward_configs(
508
+ input_tensor=key if key.shape[-1] >= value.shape[-1] else value
509
+ )
510
+
511
+
512
+ ### Flex
513
+
514
+
515
+ def get_configs_for_flex_fmha(
516
+ query: Tensor,
517
+ key: Tensor,
518
+ value: Tensor,
519
+ torch_compile: bool = False,
520
+ ) -> List[FlexFmhaForwardConfigType]:
521
+ """Returns Flex FMHA configurations compatible with input tensors, if any.
522
+
523
+ Each configuration for this operation is a tuple of two integers: `(q_tile_size,
524
+ kv_tile_size)`. These are arguments to [natten.attention][natten.attention].
525
+ Not specifying these arguments while backend is Flex will default to `q_tile_size = 64` and
526
+ `kv_tile_size = 64`.
527
+
528
+ Args:
529
+ query: Query tensor matching the shape, dtype, and device of your use case.
530
+ key: Key tensor matching the shape, dtype, and device of your use case.
531
+ value: Value tensor matching the shape, dtype, and device of your use case.
532
+ torch_compile: Whether or not you intend to use compiled block mask and flex attention kernel.
533
+
534
+ Returns:
535
+ (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile
536
+ sizes.
537
+ """
538
+ if not can_run_flex_attention(
539
+ query=query,
540
+ key=key,
541
+ value=value,
542
+ torch_compile=torch_compile,
543
+ raise_error=False,
544
+ ):
545
+ return []
546
+
547
+ return get_all_flex_fmha_forward_configs(input_tensor=query)
548
+
549
+
550
+ def get_configs_for_flex_fna(
551
+ query: Tensor,
552
+ key: Tensor,
553
+ value: Tensor,
554
+ torch_compile: bool = False,
555
+ ) -> List[FlexFnaForwardConfigType]:
556
+ """Returns Flex FNA configurations compatible with input tensors, if any.
557
+
558
+ Each configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,
559
+ kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],
560
+ and [natten.na3d][natten.na3d].
561
+ Not specifying these arguments while backend is Flex will default to single-dimensional tiling,
562
+ and will not use our Token Permutation approach. By explicitly specifying tile shapes, you will
563
+ automatically use our Token Permutation approach, which saves you the most compute.
564
+
565
+ Args:
566
+ query: Query tensor matching the shape, dtype, and device of your use case.
567
+ key: Key tensor matching the shape, dtype, and device of your use case.
568
+ value: Value tensor matching the shape, dtype, and device of your use case.
569
+ torch_compile: Whether or not you intend to use compiled block mask and flex attention kernel.
570
+
571
+ Returns:
572
+ (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query
573
+ and KV tile *shapes*.
574
+ """
575
+ if not can_run_flex_attention(
576
+ query=query,
577
+ key=key,
578
+ value=value,
579
+ torch_compile=torch_compile,
580
+ raise_error=False,
581
+ ):
582
+ return []
583
+
584
+ return get_all_flex_fna_forward_configs(input_tensor=query)
build/torch210-cu126-aarch64-linux/backends/configs/checks.py ADDED
@@ -0,0 +1,750 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ import math
25
+
26
+ import torch
27
+ from torch import Tensor
28
+
29
+ from ..._environment import _IS_TORCH_COMPILE_SUPPORTED, _TORCH_VERSION
30
+ from ..._libnatten import HAS_LIBNATTEN
31
+ from ...context import is_flex_compile_allowed, is_flex_compile_backprop_allowed
32
+ from ...utils.checks import fmha_tensor_checks, log_or_raise_error, na_tensor_checks
33
+ from ...utils.device import get_device_cc, is_cpu, is_cuda, is_rocm
34
+ from ...utils.dtype import is_fp8
35
+
36
+ ### Blackwell FMHA/FNA
37
+
38
+
39
+ def can_run_cutlass_blackwell_fmha(
40
+ query: Tensor,
41
+ key: Tensor,
42
+ value: Tensor,
43
+ is_causal: bool,
44
+ is_varlen: bool,
45
+ raise_error: bool = False,
46
+ ) -> bool:
47
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
48
+
49
+ if not HAS_LIBNATTEN:
50
+ target_fn("Can't run Blackwell FMHA; NATTEN was not built with libnatten.")
51
+ return False
52
+
53
+ if not fmha_tensor_checks(
54
+ query,
55
+ key,
56
+ value,
57
+ must_match_head_dims=True,
58
+ supports_gqa_mqa=True,
59
+ raise_error=raise_error,
60
+ backend_name="Blackwell FMHA",
61
+ ):
62
+ return False
63
+
64
+ if query.dim() != 4:
65
+ target_fn(
66
+ f"Blackwell FMHA expects rank-4 input tensors, got {query.shape=}.",
67
+ exception=ValueError,
68
+ )
69
+ return False
70
+
71
+ if not is_cuda(query.device):
72
+ target_fn("Can't run Blackwell FMHA; not a CUDA tensor.")
73
+ return False
74
+
75
+ device_cc = get_device_cc(query.device)
76
+
77
+ if device_cc not in [100, 103]:
78
+ target_fn(
79
+ "Can't run Blackwell FMHA; tensor was on CUDA device with "
80
+ f"compute capability {device_cc}, expected 100 or 103."
81
+ )
82
+ return False
83
+
84
+ head_dim = query.shape[-1]
85
+ head_dim_v = value.shape[-1]
86
+
87
+ if head_dim != head_dim_v:
88
+ target_fn(
89
+ "Can't run Blackwell FMHA; it does not support different head dims for QK and V, "
90
+ f"got {head_dim=}, {head_dim_v=}.",
91
+ exception=ValueError,
92
+ )
93
+ return False
94
+
95
+ if query.dtype not in [
96
+ torch.float16,
97
+ torch.bfloat16,
98
+ torch.float8_e4m3fn,
99
+ torch.float8_e5m2,
100
+ ]:
101
+ target_fn(
102
+ "Can't run Blackwell FMHA; it only supports FP16, BF16, FP8-E4M3 and FP8-E5M2.",
103
+ exception=ValueError,
104
+ )
105
+ return False
106
+
107
+ requires_grad = query.requires_grad or key.requires_grad or value.requires_grad
108
+ if requires_grad and is_fp8(query.dtype):
109
+ target_fn(
110
+ "Blackwell FMHA does not support FP8 backward pass, but "
111
+ f"got {requires_grad=}, {query.dtype=}.",
112
+ exception=ValueError,
113
+ )
114
+ return False
115
+
116
+ if head_dim > 128:
117
+ target_fn(
118
+ f"Can't run Blackwell FMHA; maximum supported head dim is 128, got {head_dim}.",
119
+ exception=NotImplementedError,
120
+ )
121
+ return False
122
+
123
+ if is_fp8(query.dtype):
124
+ if head_dim < 16 or head_dim % 16 != 0:
125
+ target_fn(
126
+ "Can't run Blackwell FMHA; FP8 requires head dims that are multiples of 16 "
127
+ f"(minimum 16), got {head_dim}.",
128
+ exception=NotImplementedError,
129
+ )
130
+ return False
131
+ else:
132
+ if head_dim < 8 or head_dim % 8 != 0:
133
+ target_fn(
134
+ "Can't run Blackwell FMHA; FP16 and BF16 require head dims that are multiples "
135
+ f"of 8 (minimum 8), got {head_dim}.",
136
+ exception=NotImplementedError,
137
+ )
138
+ return False
139
+
140
+ return True
141
+
142
+
143
+ def can_run_cutlass_blackwell_fna(
144
+ query: Tensor, key: Tensor, value: Tensor, raise_error: bool = False
145
+ ) -> bool:
146
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
147
+
148
+ if not HAS_LIBNATTEN:
149
+ target_fn("Can't run Blackwell FNA; NATTEN was not built with libnatten.")
150
+ return False
151
+
152
+ if not na_tensor_checks(
153
+ query,
154
+ key,
155
+ value,
156
+ must_match_head_dims=True,
157
+ supports_gqa_mqa=True,
158
+ raise_error=raise_error,
159
+ backend_name="Blackwell FNA",
160
+ ):
161
+ return False
162
+
163
+ if query.dim() not in [4, 5, 6]:
164
+ target_fn(
165
+ "Blackwell FNA expects 4-D, 5-D, or 6-D tensors as inputs (corresponding to NA1D, NA2D, and NA3D), "
166
+ f"got {query.dim()=}.",
167
+ exception=ValueError,
168
+ )
169
+ return False
170
+
171
+ if not is_cuda(query.device):
172
+ target_fn("Can't run Blackwell FNA; not a CUDA tensor.")
173
+ return False
174
+
175
+ device_cc = get_device_cc(query.device)
176
+
177
+ if device_cc not in [100, 103]:
178
+ target_fn(
179
+ "Can't run Blackwell FNA; tensor was on CUDA device with "
180
+ f"compute capability {device_cc}, expected 100 or 103."
181
+ )
182
+ return False
183
+
184
+ requires_grad = query.requires_grad or key.requires_grad or value.requires_grad
185
+ if requires_grad and torch.are_deterministic_algorithms_enabled():
186
+ target_fn(
187
+ "Can't run Blackwell FNA; its backprop does not have a deterministic mode, but "
188
+ "PyTorch's deterministic mode was enabled.",
189
+ exception=NotImplementedError,
190
+ )
191
+ return False
192
+
193
+ head_dim = query.shape[-1]
194
+ head_dim_v = value.shape[-1]
195
+
196
+ if head_dim != head_dim_v:
197
+ target_fn(
198
+ "Can't run Blackwell FNA; it does not support different head dims for QK and V, "
199
+ f"got {head_dim=}, {head_dim_v=}.",
200
+ exception=ValueError,
201
+ )
202
+ return False
203
+
204
+ if query.dtype not in [
205
+ torch.float16,
206
+ torch.bfloat16,
207
+ torch.float8_e4m3fn,
208
+ torch.float8_e5m2,
209
+ ]:
210
+ target_fn(
211
+ "Can't run Blackwell FNA; it only supports FP16, BF16, FP8-E4M3 and FP8-E5M2.",
212
+ exception=ValueError,
213
+ )
214
+ return False
215
+
216
+ if requires_grad and is_fp8(query.dtype):
217
+ target_fn(
218
+ "Blackwell FNA does not support FP8 backward pass, but "
219
+ f"got {requires_grad=}, {query.dtype=}.",
220
+ exception=ValueError,
221
+ )
222
+ return False
223
+
224
+ if head_dim > 128:
225
+ target_fn(
226
+ f"Can't run Blackwell FNA; maximum supported head dim is 128, got {head_dim}.",
227
+ exception=NotImplementedError,
228
+ )
229
+ return False
230
+
231
+ if is_fp8(query.dtype):
232
+ if head_dim < 16 or head_dim % 16 != 0:
233
+ target_fn(
234
+ "Can't run Blackwell FNA; FP8 requires head dims that are multiples of 16 "
235
+ f"(minimum 16), got {head_dim}.",
236
+ exception=NotImplementedError,
237
+ )
238
+ return False
239
+ else:
240
+ if head_dim < 8 or head_dim % 8 != 0:
241
+ target_fn(
242
+ "Can't run Blackwell FNA; FP16 and BF16 require head dims that are multiples "
243
+ f"of 8 (minimum 8), got {head_dim}.",
244
+ exception=NotImplementedError,
245
+ )
246
+ return False
247
+
248
+ return True
249
+
250
+
251
+ ### Hopper FMHA/FNA
252
+
253
+
254
+ def can_run_cutlass_hopper_fmha(
255
+ query: Tensor,
256
+ key: Tensor,
257
+ value: Tensor,
258
+ is_causal: bool,
259
+ is_varlen: bool,
260
+ raise_error: bool = False,
261
+ ) -> bool:
262
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
263
+
264
+ if not HAS_LIBNATTEN:
265
+ target_fn("Can't run Hopper FMHA; NATTEN was not built with libnatten.")
266
+ return False
267
+
268
+ if not fmha_tensor_checks(
269
+ query,
270
+ key,
271
+ value,
272
+ must_match_head_dims=True,
273
+ supports_gqa_mqa=True, # NOTE: supports GQA in only by virtue of repeating heads manually
274
+ raise_error=raise_error,
275
+ backend_name="Hopper FMHA",
276
+ ):
277
+ return False
278
+
279
+ if query.dim() != 4:
280
+ target_fn(
281
+ f"Hopper FMHA expects rank-4 input tensors, got {query.shape=}.",
282
+ exception=ValueError,
283
+ )
284
+ return False
285
+
286
+ if not is_cuda(query.device):
287
+ target_fn("Can't run Hopper FMHA; not a CUDA tensor.")
288
+ return False
289
+
290
+ device_cc = get_device_cc(query.device)
291
+
292
+ if device_cc != 90:
293
+ target_fn(
294
+ "Can't run Hopper FMHA; tensor was on CUDA device with "
295
+ f"compute capability {device_cc}, expected 90."
296
+ )
297
+ return False
298
+
299
+ head_dim = query.shape[-1]
300
+ head_dim_v = value.shape[-1]
301
+
302
+ if head_dim != head_dim_v:
303
+ target_fn(
304
+ "Can't run Hopper FMHA; it does not support different head dims for QK and V, "
305
+ f"got {head_dim=}, {head_dim_v=}.",
306
+ exception=ValueError,
307
+ )
308
+ return False
309
+
310
+ requires_grad = query.requires_grad or key.requires_grad or value.requires_grad
311
+ if requires_grad and head_dim not in [32, 64, 128]:
312
+ target_fn(
313
+ f"Can't run Hopper FMHA; it does not support backpropagation for {head_dim=} yet; "
314
+ "only head dims 32, 64, and 128 are allowed.",
315
+ exception=NotImplementedError,
316
+ )
317
+ return False
318
+
319
+ if requires_grad and torch.are_deterministic_algorithms_enabled():
320
+ target_fn(
321
+ "Can't run Hopper FMHA; its backprop does not have a deterministic mode, but "
322
+ "PyTorch's deterministic mode was enabled.",
323
+ exception=NotImplementedError,
324
+ )
325
+ return False
326
+
327
+ if query.dtype not in [torch.float16, torch.bfloat16]:
328
+ target_fn(
329
+ "Can't run Hopper FMHA; it only supports FP16 and BF16 for now.",
330
+ exception=ValueError,
331
+ )
332
+ return False
333
+
334
+ if head_dim not in [32, 64, 128, 256]:
335
+ target_fn(
336
+ "Can't run Hopper FMHA; it only supports head dims 32, 64, 128, and 256 for now.",
337
+ exception=NotImplementedError,
338
+ )
339
+ return False
340
+
341
+ return True
342
+
343
+
344
+ def can_run_cutlass_hopper_fna(
345
+ query: Tensor, key: Tensor, value: Tensor, raise_error: bool = False
346
+ ) -> bool:
347
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
348
+
349
+ if not HAS_LIBNATTEN:
350
+ target_fn("Can't run Hopper FNA; NATTEN was not built with libnatten.")
351
+ return False
352
+
353
+ if not na_tensor_checks(
354
+ query,
355
+ key,
356
+ value,
357
+ must_match_head_dims=True,
358
+ supports_gqa_mqa=True, # NOTE: supports GQA in only by virtue of repeating heads manually
359
+ raise_error=raise_error,
360
+ backend_name="Hopper FNA",
361
+ ):
362
+ return False
363
+
364
+ if query.dim() not in [4, 5, 6]:
365
+ target_fn(
366
+ "Hopper FNA expects 4-D, 5-D, or 6-D tensors as inputs (corresponding to NA1D, NA2D, and NA3D), "
367
+ f"got {query.dim()=}.",
368
+ exception=ValueError,
369
+ )
370
+ return False
371
+
372
+ if not is_cuda(query.device):
373
+ target_fn("Can't run Hopper FNA; not a CUDA tensor.")
374
+ return False
375
+
376
+ device_cc = get_device_cc(query.device)
377
+
378
+ if device_cc != 90:
379
+ target_fn(
380
+ "Can't run Hopper FNA; tensor was on CUDA device with "
381
+ f"compute capability {device_cc}, expected 90."
382
+ )
383
+ return False
384
+
385
+ head_dim = query.shape[-1]
386
+ head_dim_v = value.shape[-1]
387
+
388
+ if head_dim != head_dim_v:
389
+ target_fn(
390
+ "Can't run Hopper FNA; it does not support different head dims for QK and V, "
391
+ f"got {head_dim=}, {head_dim_v=}.",
392
+ exception=ValueError,
393
+ )
394
+ return False
395
+
396
+ requires_grad = query.requires_grad or key.requires_grad or value.requires_grad
397
+ if requires_grad and head_dim not in [32, 64, 128]:
398
+ target_fn(
399
+ f"Can't run Hopper FNA; it does not support backpropagation for {head_dim=} yet; "
400
+ "only head dims 32, 64, and 128 are allowed.",
401
+ exception=NotImplementedError,
402
+ )
403
+ return False
404
+
405
+ if requires_grad and torch.are_deterministic_algorithms_enabled():
406
+ target_fn(
407
+ "Can't run Hopper FNA; its backprop does not have a deterministic mode, but "
408
+ "PyTorch's deterministic mode was enabled.",
409
+ exception=NotImplementedError,
410
+ )
411
+ return False
412
+
413
+ if query.dtype not in [torch.float16, torch.bfloat16]:
414
+ target_fn(
415
+ "Can't run Hopper FNA; it only supports FP16 and BF16 for now.",
416
+ exception=ValueError,
417
+ )
418
+ return False
419
+
420
+ if head_dim not in [32, 64, 128, 256]:
421
+ target_fn(
422
+ "Can't run Hopper FNA; it only supports head dims 32, 64, 128, and 256 for now.",
423
+ exception=NotImplementedError,
424
+ )
425
+ return False
426
+
427
+ return True
428
+
429
+
430
+ ### CUTLASS FMHA/FNA
431
+
432
+
433
+ def can_run_cutlass_fmha(
434
+ query: Tensor,
435
+ key: Tensor,
436
+ value: Tensor,
437
+ is_causal: bool,
438
+ is_varlen: bool,
439
+ raise_error: bool = False,
440
+ ) -> bool:
441
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
442
+
443
+ if not HAS_LIBNATTEN:
444
+ target_fn("Can't run CUTLASS FMHA; NATTEN was not built with libnatten.")
445
+ return False
446
+
447
+ if not fmha_tensor_checks(
448
+ query,
449
+ key,
450
+ value,
451
+ must_match_head_dims=False,
452
+ supports_gqa_mqa=True, # NOTE: supports GQA in only by virtue of repeating heads manually
453
+ raise_error=raise_error,
454
+ backend_name="CUTLASS FMHA",
455
+ ):
456
+ return False
457
+
458
+ if query.dim() != 4:
459
+ target_fn(
460
+ f"FMHA expects rank-4 input tensors, got {query.shape=}.",
461
+ exception=ValueError,
462
+ )
463
+ return False
464
+
465
+ if not is_cuda(query.device):
466
+ target_fn("Can't run CUTLASS FMHA; not a CUDA tensor.")
467
+ return False
468
+
469
+ device_cc = get_device_cc(query.device)
470
+
471
+ if device_cc < 60:
472
+ target_fn(
473
+ "CUTLASS FMHA only supports CUDA devices with compute capability 60 or higher, "
474
+ f"got {device_cc}."
475
+ )
476
+ return False
477
+
478
+ head_dim = query.shape[-1]
479
+ head_dim_v = value.shape[-1]
480
+
481
+ if query.dtype not in [torch.float32, torch.float16, torch.bfloat16]:
482
+ target_fn(
483
+ "Can't run CUTLASS FMHA; it only supports FP32, FP16, and BF16.",
484
+ exception=ValueError,
485
+ )
486
+ return False
487
+
488
+ if head_dim % 8 != 0:
489
+ target_fn(
490
+ "Can't run CUTLASS FMHA; it only supports head dims that are multiples of 8.",
491
+ exception=ValueError,
492
+ )
493
+ return False
494
+
495
+ if head_dim_v % 8 != 0:
496
+ target_fn(
497
+ "Can't run CUTLASS FMHA; it only supports head dims that are multiples of 8, "
498
+ f"got {head_dim_v=}.",
499
+ exception=ValueError,
500
+ )
501
+ return False
502
+
503
+ if max(head_dim, head_dim_v) > 2**16:
504
+ target_fn(
505
+ f"Can't run CUTLASS FMHA; it supports max head dim of {2**16}, "
506
+ f"got {head_dim=}, {head_dim_v=}.",
507
+ exception=ValueError,
508
+ )
509
+ return False
510
+
511
+ return True
512
+
513
+
514
+ def can_run_cutlass_fna(
515
+ query: Tensor, key: Tensor, value: Tensor, raise_error: bool = False
516
+ ) -> bool:
517
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
518
+
519
+ if not HAS_LIBNATTEN:
520
+ target_fn("Can't run CUTLASS FNA; NATTEN was not built with libnatten.")
521
+ return False
522
+
523
+ if not na_tensor_checks(
524
+ query,
525
+ key,
526
+ value,
527
+ must_match_head_dims=False,
528
+ supports_gqa_mqa=True, # NOTE: supports GQA in only by virtue of repeating heads manually
529
+ raise_error=raise_error,
530
+ backend_name="CUTLASS FNA",
531
+ ):
532
+ return False
533
+
534
+ if query.dim() not in [4, 5, 6]:
535
+ target_fn(
536
+ "CUTLASS FNA expects 4-D, 5-D, or 6-D tensors as inputs (corresponding to NA1D, NA2D, and NA3D), "
537
+ f"got {query.dim()=}.",
538
+ exception=ValueError,
539
+ )
540
+ return False
541
+
542
+ if not is_cuda(query.device):
543
+ target_fn("Can't run CUTLASS FNA; not a CUDA tensor.")
544
+ return False
545
+
546
+ device_cc = get_device_cc(query.device)
547
+
548
+ if device_cc < 60:
549
+ target_fn(
550
+ "CUTLASS FNA only supports CUDA devices with compute capability 60 or higher, "
551
+ f"got {device_cc}."
552
+ )
553
+ return False
554
+
555
+ head_dim = query.shape[-1]
556
+ head_dim_v = value.shape[-1]
557
+
558
+ if query.dtype not in [torch.float32, torch.float16, torch.bfloat16]:
559
+ target_fn(
560
+ "Can't run CUTLASS FNA; it only supports FP32, FP16, and BF16.",
561
+ exception=ValueError,
562
+ )
563
+ return False
564
+
565
+ if head_dim % 8 != 0:
566
+ target_fn(
567
+ "Can't run CUTLASS FNA; it only supports head dims that are multiples of 8, "
568
+ f"got {head_dim=}.",
569
+ exception=ValueError,
570
+ )
571
+ return False
572
+
573
+ if head_dim_v % 8 != 0:
574
+ target_fn(
575
+ "Can't run CUTLASS FNA; it only supports head dims that are multiples of 8, "
576
+ f"got {head_dim_v=}.",
577
+ exception=ValueError,
578
+ )
579
+ return False
580
+
581
+ if max(head_dim, head_dim_v) > 2**16:
582
+ target_fn(
583
+ f"Can't run CUTLASS FNA; it supports max head dim of {2**16}, "
584
+ f"got {head_dim=}, {head_dim_v=}.",
585
+ exception=ValueError,
586
+ )
587
+ return False
588
+
589
+ return True
590
+
591
+
592
+ ### Flex FMHA/FNA
593
+
594
+ _FLEX_SUPPORTED = _TORCH_VERSION >= [2, 7]
595
+ _FLEX_COMPILE_SUPPORTED = _TORCH_VERSION >= [2, 7] and _IS_TORCH_COMPILE_SUPPORTED
596
+
597
+
598
+ def can_run_flex_attention(
599
+ query: Tensor,
600
+ key: Tensor,
601
+ value: Tensor,
602
+ torch_compile: bool,
603
+ is_causal: bool = False,
604
+ is_varlen: bool = False,
605
+ raise_error: bool = False,
606
+ ) -> bool:
607
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
608
+
609
+ if is_causal:
610
+ target_fn("Flex FMHA doesn't support causal mask yet.")
611
+ return False
612
+
613
+ if is_varlen:
614
+ target_fn("Flex FMHA doesn't support variable length inputs (varlen).")
615
+ return False
616
+
617
+ if not _FLEX_SUPPORTED:
618
+ target_fn("Can't run NATTEN with Flex Attention with torch < 2.7.")
619
+ return False
620
+
621
+ if torch_compile and not _FLEX_COMPILE_SUPPORTED:
622
+ target_fn("Can't run NATTEN with Flex Attention (compiled).)")
623
+ return False
624
+
625
+ if torch_compile and not is_flex_compile_allowed():
626
+ target_fn(
627
+ "NATTEN does not allow compiling Flex Attention. This is because we cannot verify "
628
+ "Flex's correctness in all scenarios through NATTEN's tests. You can choose to override "
629
+ "this, though it is discouraged, as it may affect your results significantly, "
630
+ "by doing:\n"
631
+ " from ... import allow_flex_compile\n"
632
+ " allow_flex_compile()\n"
633
+ )
634
+ return False
635
+
636
+ requires_grad = query.requires_grad or key.requires_grad or value.requires_grad
637
+ if torch_compile and requires_grad and not is_flex_compile_backprop_allowed():
638
+ target_fn(
639
+ "NATTEN does not allow compiling Flex Attention for backpropagation "
640
+ "({q,k,v}.requires_grad=True). This is because we cannot verify Flex's correctness "
641
+ "in all scenarios through NATTEN's tests. You can choose to override this, though "
642
+ "it is HIGHLY discouraged, as it may affect the results of your training significantly, "
643
+ "by doing:\n"
644
+ " from ... import allow_flex_compile_backprop\n"
645
+ " allow_flex_compile_backprop()\n"
646
+ )
647
+ return False
648
+
649
+ # TODO: can we just have different checks for FMHA vs FNA, like the rest of the backends?
650
+ if query.dim() == 4 and key.dim() == 4 and query.shape[1] != key.shape[1]:
651
+ supported = fmha_tensor_checks(
652
+ query,
653
+ key,
654
+ value,
655
+ must_match_head_dims=True,
656
+ supports_gqa_mqa=True,
657
+ raise_error=raise_error,
658
+ backend_name="Flex FMHA",
659
+ )
660
+ else:
661
+ supported = na_tensor_checks(
662
+ query,
663
+ key,
664
+ value,
665
+ must_match_head_dims=True,
666
+ supports_gqa_mqa=True,
667
+ raise_error=raise_error,
668
+ backend_name="Flex FMHA/FNA",
669
+ )
670
+ if not supported:
671
+ return False
672
+
673
+ if query.dim() not in [4, 5, 6]:
674
+ target_fn(
675
+ "Flex backend expects 4-D, 5-D, or 6-D tensors as inputs (corresponding to FMHA/NA1D, "
676
+ f"NA2D, and NA3D), got {query.dim()=}.",
677
+ exception=ValueError,
678
+ )
679
+ return False
680
+
681
+ if not is_cuda(query.device):
682
+ if not is_cpu(query.device) and not is_rocm(query.device):
683
+ target_fn(
684
+ "Can't run Flex Attention; tensor is not on a CUDA, ROCm, or CPU device: "
685
+ f"{query.device.type}"
686
+ )
687
+
688
+ return False
689
+ # TODO: check if ROCm device supports torch.compile/triton?
690
+
691
+ else:
692
+ device_cc = get_device_cc(query.device)
693
+
694
+ if device_cc < 70:
695
+ target_fn(
696
+ "Flex Attention (compiled) only supports CUDA devices with compute capability "
697
+ f"70 or higher, got {device_cc}."
698
+ )
699
+ return False
700
+
701
+ head_dim = query.shape[-1]
702
+ head_dim_v = value.shape[-1]
703
+
704
+ if head_dim != head_dim_v:
705
+ target_fn(
706
+ "Can't run NATTEN with Flex Attention; we don't support different head dims for QK and "
707
+ f"V in this backend yet, got {head_dim=}, {head_dim_v=}.",
708
+ exception=ValueError,
709
+ )
710
+ return False
711
+
712
+ if not torch_compile and query.dtype not in [
713
+ torch.float32,
714
+ torch.float16,
715
+ torch.bfloat16,
716
+ ]:
717
+ target_fn(
718
+ "Can't run NATTEN with Flex Attention; we only support FP32, FP16, and BF16 for now.",
719
+ exception=ValueError,
720
+ )
721
+ return False
722
+
723
+ if torch_compile and query.dtype not in [torch.float16, torch.bfloat16]:
724
+ target_fn(
725
+ "Can't run NATTEN with Flex Attention (compiled); we only support FP32, FP16, and BF16 for now.",
726
+ exception=ValueError,
727
+ )
728
+ return False
729
+
730
+ if torch_compile and (
731
+ head_dim < 32 or head_dim > 512 or not math.log2(head_dim).is_integer()
732
+ ):
733
+ target_fn(
734
+ "Can't run NATTEN with Flex Attention (compiled); we only allow 32 <= head_dim <= 512 "
735
+ f"and only powers of two, got {head_dim}.",
736
+ exception=ValueError,
737
+ )
738
+ return False
739
+
740
+ if not torch_compile and (
741
+ head_dim < 8 or head_dim > 512 or not math.log2(head_dim).is_integer()
742
+ ):
743
+ target_fn(
744
+ "Can't run NATTEN with Flex Attention (not compiled); we only allow 8 <= head_dim <= 512 "
745
+ f"and only powers of two, got {head_dim}.",
746
+ exception=ValueError,
747
+ )
748
+ return False
749
+
750
+ return True
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/__init__.py ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import List, Optional, Tuple
25
+
26
+ import torch
27
+ from torch import Tensor
28
+
29
+ # FNA/FMHA forward supports 64x64 and 32x128 GEMM configs in all
30
+ # use cases. Some architectures (SM80 and SM90 )have more shared
31
+ # memory so they can handle 64x128 GEMMs.
32
+
33
+ from ....backends.configs.cutlass.fna_backward_128x128 import (
34
+ _FNA_BACKWARD_128x128_TILE_SIZES,
35
+ )
36
+ from ....backends.configs.cutlass.fna_backward_128x64 import (
37
+ _FNA_BACKWARD_128x64_TILE_SIZES,
38
+ )
39
+ from ....backends.configs.cutlass.fna_backward_64x64 import (
40
+ _FNA_BACKWARD_64x64_TILE_SIZES,
41
+ )
42
+
43
+ # FNA/FMHA backward supports 64x64 GEMM configs in all
44
+ # use cases. Some architectures have more shared memory
45
+ # so they can handle 128x64 or 128x128 GEMMs, but that
46
+ # is also dependent on the GEMM K.
47
+
48
+ from ....backends.configs.cutlass.fna_forward_32x128 import (
49
+ _FNA_FORWARD_32x128_TILE_SIZES,
50
+ )
51
+ from ....backends.configs.cutlass.fna_forward_64x128 import (
52
+ _FNA_FORWARD_64x128_TILE_SIZES,
53
+ )
54
+ from ....backends.configs.cutlass.fna_forward_64x64 import (
55
+ _FNA_FORWARD_64x64_TILE_SIZES,
56
+ )
57
+ from ...._types import (
58
+ CutlassFmhaBackwardConfigType,
59
+ CutlassFmhaForwardConfigType,
60
+ CutlassFnaBackwardConfigType,
61
+ CutlassFnaForwardConfigType,
62
+ DimensionType,
63
+ )
64
+ from ....utils.checks import check_tile_shape
65
+ from ....utils.device import get_device_cc, is_cuda
66
+
67
+
68
+ def _get_default_tile_shapes_forward(
69
+ na_dim: int,
70
+ ) -> CutlassFnaForwardConfigType:
71
+ assert na_dim in [1, 2, 3]
72
+
73
+ if na_dim == 1:
74
+ return ((64,), (64,))
75
+ if na_dim == 2:
76
+ return ((8, 8), (8, 8))
77
+ if na_dim == 3:
78
+ return ((4, 4, 4), (4, 4, 4))
79
+
80
+ raise NotImplementedError()
81
+
82
+
83
+ def get_all_tile_shapes_forward(
84
+ input_tensor: Tensor,
85
+ ) -> List[CutlassFnaForwardConfigType]:
86
+ assert input_tensor.dim() in [4, 5, 6]
87
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
88
+ device = input_tensor.device
89
+
90
+ if not is_cuda(device):
91
+ return []
92
+
93
+ # DC-class GPUs have more shared memory
94
+ if get_device_cc(device) in [80, 90, 100, 103]:
95
+ return (
96
+ _FNA_FORWARD_32x128_TILE_SIZES[na_dim]
97
+ + _FNA_FORWARD_64x64_TILE_SIZES[na_dim]
98
+ + _FNA_FORWARD_64x128_TILE_SIZES[na_dim]
99
+ )
100
+
101
+ return (
102
+ _FNA_FORWARD_32x128_TILE_SIZES[na_dim] + _FNA_FORWARD_64x64_TILE_SIZES[na_dim]
103
+ )
104
+
105
+
106
+ # For FMHA
107
+ def get_all_tile_sizes_forward(
108
+ input_tensor: Tensor,
109
+ ) -> List[CutlassFmhaForwardConfigType]:
110
+ if input_tensor.dim() != 4:
111
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
112
+
113
+ tile_shapes = get_all_tile_shapes_forward(input_tensor)
114
+ assert all(len(q_t) == len(kv_t) == 1 for q_t, kv_t in tile_shapes)
115
+
116
+ tile_sizes = [(q_t[0], kv_t[0]) for q_t, kv_t in tile_shapes]
117
+
118
+ return tile_sizes
119
+
120
+
121
+ def get_default_forward_config(
122
+ input_tensor: Tensor, dilation: Optional[DimensionType] = None
123
+ ) -> CutlassFnaForwardConfigType:
124
+ assert input_tensor.dim() in [4, 5, 6]
125
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
126
+ return _get_default_tile_shapes_forward(na_dim)
127
+
128
+
129
+ get_all_forward_configs = get_all_tile_shapes_forward
130
+ get_all_fmha_forward_configs = get_all_tile_sizes_forward
131
+
132
+
133
+ def check_cutlass_fna_forward_config(
134
+ input_tensor: Tensor,
135
+ dilation: Optional[DimensionType] = None,
136
+ q_tile_shape: Optional[DimensionType] = None,
137
+ kv_tile_shape: Optional[DimensionType] = None,
138
+ ) -> CutlassFnaForwardConfigType:
139
+ assert input_tensor.dim() in [4, 5, 6]
140
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
141
+
142
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
143
+ raise ValueError(
144
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
145
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
146
+ )
147
+
148
+ if q_tile_shape is None and kv_tile_shape is None:
149
+ return get_default_forward_config(input_tensor=input_tensor, dilation=dilation)
150
+
151
+ q_tile_shape = check_tile_shape(q_tile_shape)
152
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
153
+
154
+ tile_shapes = get_all_tile_shapes_forward(input_tensor=input_tensor)
155
+
156
+ for q_t, kv_t in tile_shapes:
157
+ if q_t == q_tile_shape and kv_t == kv_tile_shape:
158
+ return (q_t, kv_t) # type: ignore
159
+
160
+ # Fail and make suggestions
161
+ device_cc = get_device_cc(input_tensor.device)
162
+ MAX_EXAMPLES = 3
163
+ examples = ""
164
+ for i, (q_t, kv_t) in enumerate(tile_shapes):
165
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}"
166
+ if i > MAX_EXAMPLES:
167
+ break
168
+
169
+ raise ValueError(
170
+ f"Invalid configuration for CUTLASS FNA-{na_dim}D. "
171
+ f"Q tile shape {q_tile_shape} and KV tile shape {kv_tile_shape} "
172
+ f"are not among the {len(tile_shapes)} configurations implementable "
173
+ f"with CUTLASS 2.X FNA for SM{device_cc}. "
174
+ "Try selecting a combination from: \n"
175
+ " natten.get_configs_for_cutlass_fna(q, k, v)"
176
+ "\n"
177
+ "Here's a few examples of available combinations for your use case:\n"
178
+ f"{examples}"
179
+ )
180
+
181
+
182
+ def check_cutlass_fmha_forward_config(
183
+ input_tensor: Tensor,
184
+ q_tile_size: Optional[int] = None,
185
+ kv_tile_size: Optional[int] = None,
186
+ ) -> CutlassFmhaForwardConfigType:
187
+ assert input_tensor.dim() == 4
188
+
189
+ if (q_tile_size is None) ^ (kv_tile_size is None):
190
+ raise ValueError(
191
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
192
+ f"Got {q_tile_size=}, {kv_tile_size=}."
193
+ )
194
+
195
+ if q_tile_size is None and kv_tile_size is None:
196
+ q_tile_shape, kv_tile_shape = get_default_forward_config(
197
+ input_tensor=input_tensor
198
+ )
199
+ assert len(q_tile_shape) == len(kv_tile_shape) == 1
200
+ return (q_tile_shape[0], kv_tile_shape[0])
201
+
202
+ tile_sizes = get_all_tile_sizes_forward(input_tensor=input_tensor)
203
+
204
+ for q_t, kv_t in tile_sizes:
205
+ if q_t == q_tile_size and kv_t == kv_tile_size:
206
+ return (q_t, kv_t)
207
+
208
+ # Fail and make suggestions
209
+ device_cc = get_device_cc(input_tensor.device)
210
+ MAX_EXAMPLES = 3
211
+ examples = ""
212
+ for i, (q_t, kv_t) in enumerate(tile_sizes):
213
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}"
214
+ if i > MAX_EXAMPLES:
215
+ break
216
+
217
+ raise ValueError(
218
+ f"Invalid configuration for CUTLASS FMHA. "
219
+ f"Q tile size {q_tile_size} and KV tile size {kv_tile_size} "
220
+ f"are not among the {len(tile_sizes)} configurations implementable "
221
+ f"with CUTLASS 2.X FMHA for SM{device_cc}. "
222
+ "Try selecting a combination from: \n"
223
+ " natten.get_configs_for_cutlass_fmha(q, k, v)"
224
+ "\n"
225
+ "Here's a few examples of available combinations for your use case:\n"
226
+ f"{examples}"
227
+ )
228
+
229
+
230
+ ###### Backward
231
+
232
+
233
+ def _get_default_tile_shapes_backward(
234
+ na_dim: int,
235
+ ) -> Tuple[DimensionType, DimensionType]:
236
+ assert na_dim in [1, 2, 3]
237
+
238
+ if na_dim == 1:
239
+ return ((64,), (64,))
240
+ if na_dim == 2:
241
+ return ((8, 8), (8, 8))
242
+ if na_dim == 3:
243
+ return ((4, 4, 4), (4, 4, 4))
244
+
245
+ raise NotImplementedError()
246
+
247
+
248
+ def get_all_tile_shapes_backward(
249
+ input_tensor: Tensor,
250
+ ) -> List[CutlassFnaForwardConfigType]:
251
+ assert input_tensor.dim() in [4, 5, 6]
252
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
253
+ device = input_tensor.device
254
+ dtype = input_tensor.dtype
255
+ dim_per_head = input_tensor.shape[-1]
256
+
257
+ if not is_cuda(device):
258
+ return []
259
+
260
+ compute_cap = get_device_cc(device)
261
+
262
+ assert dtype in [torch.float32, torch.float16, torch.bfloat16]
263
+
264
+ # DC-class cards have extra shmem which allows larger tile sizes
265
+ dc_class_arches = [80, 90, 100, 103]
266
+
267
+ if dtype == torch.float32 and compute_cap not in dc_class_arches:
268
+ return _FNA_BACKWARD_64x64_TILE_SIZES[na_dim]
269
+
270
+ elif dtype == torch.float32:
271
+ return (
272
+ _FNA_BACKWARD_64x64_TILE_SIZES[na_dim]
273
+ + _FNA_BACKWARD_128x64_TILE_SIZES[na_dim]
274
+ )
275
+
276
+ if compute_cap == 70:
277
+ return (
278
+ _FNA_BACKWARD_64x64_TILE_SIZES[na_dim]
279
+ + _FNA_BACKWARD_128x64_TILE_SIZES[na_dim]
280
+ )
281
+
282
+ if compute_cap in dc_class_arches and dim_per_head <= 128:
283
+ return (
284
+ _FNA_BACKWARD_64x64_TILE_SIZES[na_dim]
285
+ + _FNA_BACKWARD_128x128_TILE_SIZES[na_dim]
286
+ )
287
+ elif compute_cap in dc_class_arches:
288
+ return (
289
+ _FNA_BACKWARD_64x64_TILE_SIZES[na_dim]
290
+ + _FNA_BACKWARD_128x64_TILE_SIZES[na_dim]
291
+ )
292
+
293
+ return _FNA_BACKWARD_64x64_TILE_SIZES[na_dim]
294
+
295
+
296
+ # For FMHA
297
+ def get_all_tile_sizes_backward(
298
+ input_tensor: Tensor,
299
+ ) -> List[CutlassFmhaForwardConfigType]:
300
+ if input_tensor.dim() != 4:
301
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
302
+
303
+ tile_shapes = get_all_tile_shapes_backward(input_tensor)
304
+ assert all(len(q_t) == len(kv_t) == 1 for q_t, kv_t in tile_shapes)
305
+
306
+ tile_sizes = [(q_t[0], kv_t[0]) for q_t, kv_t in tile_shapes]
307
+
308
+ return tile_sizes
309
+
310
+
311
+ def get_default_backward_config(
312
+ input_tensor: Tensor,
313
+ ) -> CutlassFnaBackwardConfigType:
314
+ assert input_tensor.dim() in [4, 5, 6]
315
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
316
+
317
+ q_tile_shape, kv_tile_shape = _get_default_tile_shapes_backward(na_dim)
318
+ return (q_tile_shape, kv_tile_shape) # type: ignore
319
+
320
+
321
+ get_all_backward_configs = get_all_tile_shapes_backward
322
+
323
+
324
+ get_all_fmha_backward_configs = get_all_tile_sizes_backward
325
+
326
+
327
+ def check_cutlass_fna_backward_config(
328
+ input_tensor: Tensor,
329
+ q_tile_shape: Optional[DimensionType] = None,
330
+ kv_tile_shape: Optional[DimensionType] = None,
331
+ ) -> CutlassFnaBackwardConfigType:
332
+ assert input_tensor.dim() in [4, 5, 6]
333
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
334
+
335
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
336
+ raise ValueError(
337
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
338
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
339
+ )
340
+
341
+ if q_tile_shape is None and kv_tile_shape is None:
342
+ return get_default_backward_config(
343
+ input_tensor=input_tensor,
344
+ )
345
+
346
+ q_tile_shape = check_tile_shape(q_tile_shape)
347
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
348
+
349
+ tile_shapes = get_all_tile_shapes_backward(input_tensor)
350
+
351
+ for q_t, kv_t in tile_shapes:
352
+ if q_t == q_tile_shape and kv_t == kv_tile_shape:
353
+ return (q_t, kv_t) # type: ignore
354
+
355
+ # Fail and make suggestions
356
+ device_cc = get_device_cc(input_tensor.device)
357
+ MAX_EXAMPLES = 3
358
+ examples = ""
359
+ for i, (q_t, kv_t) in enumerate(tile_shapes):
360
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}"
361
+ if i > MAX_EXAMPLES:
362
+ break
363
+
364
+ raise ValueError(
365
+ f"Invalid configuration for CUTLASS FNA-{na_dim}D. "
366
+ f"Q tile shape {q_tile_shape} and KV tile shape {kv_tile_shape} "
367
+ f"are not among the {len(tile_shapes)} configurations implementable "
368
+ f"with CUTLASS 2.X FNA for SM{device_cc}, with input tensor shape "
369
+ f"{input_tensor.shape}. Try selecting a combination from: \n"
370
+ " natten.get_bwd_configs_for_cutlass_fna(q, k, v)"
371
+ "\n"
372
+ "Here's a few examples of available combinations for your use case:\n"
373
+ f"{examples}"
374
+ )
375
+
376
+
377
+ def check_cutlass_fmha_backward_config(
378
+ input_tensor: Tensor,
379
+ q_tile_size: Optional[int] = None,
380
+ kv_tile_size: Optional[int] = None,
381
+ ) -> CutlassFmhaBackwardConfigType:
382
+ assert input_tensor.dim() == 4
383
+
384
+ if (q_tile_size is None) ^ (kv_tile_size is None):
385
+ raise ValueError(
386
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
387
+ f"Got {q_tile_size=}, {kv_tile_size=}."
388
+ )
389
+
390
+ if q_tile_size is None and kv_tile_size is None:
391
+ q_tile_shape, kv_tile_shape = get_default_backward_config(
392
+ input_tensor=input_tensor
393
+ )
394
+ assert len(q_tile_shape) == len(kv_tile_shape) == 1
395
+ q_tile_size, kv_tile_size = q_tile_shape[0], kv_tile_shape[0]
396
+
397
+ tile_sizes = get_all_tile_sizes_backward(input_tensor)
398
+
399
+ for q_t, kv_t in tile_sizes:
400
+ if q_t == q_tile_size and kv_t == kv_tile_size:
401
+ return (q_t, kv_t)
402
+
403
+ # Fail and make suggestions
404
+ device_cc = get_device_cc(input_tensor.device)
405
+ MAX_EXAMPLES = 3
406
+ examples = ""
407
+ for i, (q_t, kv_t) in enumerate(tile_sizes):
408
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}"
409
+ if i > MAX_EXAMPLES:
410
+ break
411
+
412
+ raise ValueError(
413
+ f"Invalid configuration for CUTLASS FMHA. "
414
+ f"Q tile size {q_tile_size} and KV tile size {kv_tile_size} "
415
+ f"are not among the {len(tile_sizes)} configurations implementable "
416
+ f"with CUTLASS 2.X FNA for SM{device_cc}, with input tensor shape "
417
+ f"{input_tensor.shape}. Try selecting a combination from: \n"
418
+ " natten.get_bwd_configs_for_cutlass_fmha(q, k, v)"
419
+ "\n"
420
+ "Here's a few examples of available combinations for your use case:\n"
421
+ f"{examples}"
422
+ )
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/backward_knobs.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ # Backward pass "knobs" for CUTLASS FNA/FMHA:
25
+ # - kv_splits: number of KV splits for parallelism
26
+ # - use_pt_reduction: whether to use PyTorch for delta computation
27
+ #
28
+ # These are independent of tile shape selection and are validated/defaulted
29
+ # in the torch ops (torch_wrappers.py), not in the config selection logic.
30
+
31
+ import itertools
32
+ import math
33
+ from typing import Optional
34
+
35
+ from torch import Tensor
36
+
37
+ from ....context import (
38
+ is_kv_parallelism_in_fused_na_enabled,
39
+ is_memory_usage_strict,
40
+ is_memory_usage_unrestricted,
41
+ )
42
+ from ...._types import DimensionType
43
+ from ....utils.checks import check_dilation_arg, check_input_size_arg
44
+ from ....utils.tuples import ceil_div_int, ceil_div_tuple
45
+
46
+
47
+ def _get_max_grid_size_allowed() -> int:
48
+ if is_memory_usage_unrestricted():
49
+ return 65535
50
+ if is_memory_usage_strict():
51
+ return 1024
52
+
53
+ return 4096
54
+
55
+
56
+ def get_min_splits(na_dim: int) -> DimensionType:
57
+ assert na_dim in [1, 2, 3]
58
+ return tuple(1 for _ in range(na_dim)) # type: ignore
59
+
60
+
61
+ def get_max_splits(
62
+ input_shape: DimensionType, dilation: DimensionType, kv_tile_shape: DimensionType
63
+ ) -> DimensionType:
64
+ extent_per_dilation_group = ceil_div_tuple(input_shape, dilation)
65
+ return tuple(
66
+ ceil_div_int(x, t) for x, t in zip(extent_per_dilation_group, kv_tile_shape)
67
+ ) # type: ignore
68
+
69
+
70
+ def _reduce_max_kv_splits(
71
+ na_dim: int,
72
+ kv_splits: DimensionType,
73
+ max_splits: int,
74
+ ) -> DimensionType:
75
+ assert isinstance(kv_splits, tuple)
76
+ assert na_dim in [1, 2, 3]
77
+
78
+ if na_dim == 1:
79
+ assert len(kv_splits) == 1
80
+ return (min(kv_splits[0], max_splits),)
81
+
82
+ if na_dim == 2:
83
+ assert len(kv_splits) == 2
84
+ splits_x = max(min(max_splits // 2, kv_splits[0]), 1)
85
+ splits_y = max(min(max_splits // splits_x, kv_splits[1]), 1)
86
+ assert (
87
+ 0 < splits_x * splits_y <= max_splits
88
+ ), f"{splits_x=} * {splits_y=} does not fall in range [0, {max_splits}]"
89
+ return (splits_x, splits_y)
90
+
91
+ if na_dim == 3:
92
+ assert len(kv_splits) == 3
93
+ splits_x = max(min(max_splits // 3, kv_splits[0]), 1)
94
+ splits_y = max(min(max_splits // splits_x, kv_splits[1]), 1)
95
+ splits_z = max(min(max_splits // (splits_x * splits_y), kv_splits[2]), 1)
96
+ assert (
97
+ 0 < splits_x * splits_y * splits_z <= max_splits
98
+ ), f"{splits_x=} * {splits_y=} * {splits_z=} does not fall in range [0, {max_splits}]"
99
+ return (splits_x, splits_y, splits_z)
100
+
101
+ raise NotImplementedError()
102
+
103
+
104
+ def _get_possible_kv_splits(
105
+ min_splits: DimensionType,
106
+ max_splits: DimensionType,
107
+ ):
108
+ assert 0 < len(min_splits) == len(max_splits) < 4
109
+ na_dim = len(max_splits)
110
+ if na_dim == 1:
111
+ return itertools.product(
112
+ range(min_splits[0], max_splits[0] + 1),
113
+ )
114
+ if na_dim == 2:
115
+ assert len(min_splits) == len(max_splits) == 2
116
+ return itertools.product(
117
+ range(min_splits[0], max_splits[0] + 1),
118
+ range(min_splits[1], max_splits[1] + 1),
119
+ )
120
+ if na_dim == 3:
121
+ assert len(min_splits) == len(max_splits) == 3
122
+ return itertools.product(
123
+ range(min_splits[0], max_splits[0] + 1),
124
+ range(min_splits[1], max_splits[1] + 1),
125
+ range(min_splits[2], max_splits[2] + 1),
126
+ )
127
+
128
+ raise NotImplementedError()
129
+
130
+
131
+ def get_default_kv_splits_backward(
132
+ input_tensor: Tensor,
133
+ kv_tile_shape: DimensionType,
134
+ deterministic: bool,
135
+ dilation: Optional[DimensionType] = None,
136
+ max_seqlen: Optional[DimensionType] = None,
137
+ ) -> DimensionType:
138
+ assert input_tensor.dim() in [4, 5, 6]
139
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
140
+ dilation = check_dilation_arg(na_dim, dilation)
141
+ input_shape: DimensionType = tuple(int(x) for x in input_tensor.shape[1 : na_dim + 1]) # type: ignore
142
+ if max_seqlen is not None:
143
+ input_shape = check_input_size_arg(na_dim, max_seqlen)
144
+
145
+ assert na_dim in [1, 2, 3]
146
+ if na_dim == 1:
147
+ kv_splits: DimensionType = (1,)
148
+ elif na_dim == 2:
149
+ kv_splits = (1, 1)
150
+
151
+ elif na_dim == 3:
152
+ kv_splits = (1, 1, 1)
153
+
154
+ if is_kv_parallelism_in_fused_na_enabled() and not deterministic:
155
+ kv_splits = get_max_splits(
156
+ input_shape, dilation=dilation, kv_tile_shape=kv_tile_shape
157
+ )
158
+ total_kv_splits = math.prod(kv_splits)
159
+
160
+ batch_size = input_tensor.shape[0]
161
+ num_heads = input_tensor.shape[-2]
162
+ num_dilation_splits = math.prod(dilation)
163
+ max_kv_splits_allowed = max(
164
+ 1,
165
+ _get_max_grid_size_allowed()
166
+ // (batch_size * num_heads * num_dilation_splits),
167
+ )
168
+
169
+ if total_kv_splits > max_kv_splits_allowed:
170
+ kv_splits = _reduce_max_kv_splits(
171
+ na_dim=na_dim, kv_splits=kv_splits, max_splits=max_kv_splits_allowed
172
+ )
173
+
174
+ return kv_splits
175
+
176
+
177
+ def check_fmha_kv_splits(
178
+ kv_splits: Optional[int],
179
+ input_tensor: Tensor,
180
+ kv_tile_size: int,
181
+ deterministic: bool,
182
+ max_seqlen: Optional[int] = None,
183
+ ) -> int:
184
+ if kv_splits is not None and isinstance(kv_splits, int):
185
+ seqlen_kv = input_tensor.shape[1] if max_seqlen is None else max_seqlen
186
+ num_kv_tiles = (seqlen_kv + kv_tile_size - 1) // kv_tile_size
187
+ assert num_kv_tiles > 0
188
+ return min(num_kv_tiles, kv_splits)
189
+
190
+ if kv_splits is None:
191
+ max_seqlen_tuple = None if max_seqlen is None else (max_seqlen,)
192
+ default_kv_splits: DimensionType = get_default_kv_splits_backward(
193
+ input_tensor=input_tensor,
194
+ deterministic=deterministic,
195
+ kv_tile_shape=(kv_tile_size,),
196
+ max_seqlen=max_seqlen_tuple,
197
+ )
198
+ assert len(default_kv_splits) == 1
199
+ return default_kv_splits[0]
200
+
201
+ raise ValueError(f"Invalid type {type(kv_splits)} for kv_splits.")
202
+
203
+
204
+ def check_fna_kv_splits(
205
+ kv_splits: Optional[DimensionType],
206
+ input_tensor: Tensor,
207
+ kv_tile_shape: DimensionType,
208
+ deterministic: bool,
209
+ dilation: Optional[DimensionType] = None,
210
+ ) -> DimensionType:
211
+ if kv_splits is not None and isinstance(kv_splits, tuple):
212
+ na_dim = input_tensor.dim() - 3
213
+ dilation = check_dilation_arg(na_dim, dilation)
214
+ input_shape: DimensionType = tuple(int(x) for x in input_tensor.shape[1 : na_dim + 1]) # type: ignore
215
+ max_kv_splits = get_max_splits(
216
+ input_shape, dilation=dilation, kv_tile_shape=kv_tile_shape
217
+ )
218
+ return tuple(min(s, m) for s, m in zip(kv_splits, max_kv_splits)) # type: ignore
219
+
220
+ if kv_splits is None:
221
+ return get_default_kv_splits_backward(
222
+ deterministic=deterministic,
223
+ input_tensor=input_tensor,
224
+ kv_tile_shape=kv_tile_shape,
225
+ dilation=dilation,
226
+ )
227
+
228
+ raise ValueError(f"Invalid type {type(kv_splits)} for kv_splits.")
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_backward_128x128.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import Dict, List
26
+
27
+ from ...._types import QKTileShapeType
28
+
29
+ # NOTE: we're excluding tile shapes that include 1 just to
30
+ # reduce the giant number of configs down to a reasonable
31
+ # amount; otherwise autotuning would take more than a few
32
+ # seconds per call which is unacceptable. Tile shapes with
33
+ # 1s are rarely selected.
34
+
35
+ _FNA_BACKWARD_128x128_TILE_SIZES: Dict[int, List[QKTileShapeType]] = {
36
+ 1: [
37
+ ((128,), (128,)),
38
+ ],
39
+ 2: [
40
+ ((64, 2), (64, 2)),
41
+ ((64, 2), (32, 4)),
42
+ ((64, 2), (16, 8)),
43
+ ((64, 2), (8, 16)),
44
+ ((64, 2), (4, 32)),
45
+ ((64, 2), (2, 64)),
46
+ ((32, 4), (64, 2)),
47
+ ((32, 4), (32, 4)),
48
+ ((32, 4), (16, 8)),
49
+ ((32, 4), (8, 16)),
50
+ ((32, 4), (4, 32)),
51
+ ((32, 4), (2, 64)),
52
+ ((16, 8), (64, 2)),
53
+ ((16, 8), (32, 4)),
54
+ ((16, 8), (16, 8)),
55
+ ((16, 8), (8, 16)),
56
+ ((16, 8), (4, 32)),
57
+ ((16, 8), (2, 64)),
58
+ ((8, 16), (64, 2)),
59
+ ((8, 16), (32, 4)),
60
+ ((8, 16), (16, 8)),
61
+ ((8, 16), (8, 16)),
62
+ ((8, 16), (4, 32)),
63
+ ((8, 16), (2, 64)),
64
+ ((4, 32), (64, 2)),
65
+ ((4, 32), (32, 4)),
66
+ ((4, 32), (16, 8)),
67
+ ((4, 32), (8, 16)),
68
+ ((4, 32), (4, 32)),
69
+ ((4, 32), (2, 64)),
70
+ ((2, 64), (64, 2)),
71
+ ((2, 64), (32, 4)),
72
+ ((2, 64), (16, 8)),
73
+ ((2, 64), (8, 16)),
74
+ ((2, 64), (4, 32)),
75
+ ((2, 64), (2, 64)),
76
+ ],
77
+ 3: [
78
+ ((32, 2, 2), (32, 2, 2)),
79
+ ((32, 2, 2), (16, 4, 2)),
80
+ ((32, 2, 2), (16, 2, 4)),
81
+ ((32, 2, 2), (8, 8, 2)),
82
+ ((32, 2, 2), (8, 4, 4)),
83
+ ((32, 2, 2), (8, 2, 8)),
84
+ ((32, 2, 2), (4, 16, 2)),
85
+ ((32, 2, 2), (4, 8, 4)),
86
+ ((32, 2, 2), (4, 4, 8)),
87
+ ((32, 2, 2), (4, 2, 16)),
88
+ ((32, 2, 2), (2, 32, 2)),
89
+ ((32, 2, 2), (2, 16, 4)),
90
+ ((32, 2, 2), (2, 8, 8)),
91
+ ((32, 2, 2), (2, 4, 16)),
92
+ ((32, 2, 2), (2, 2, 32)),
93
+ ((16, 4, 2), (32, 2, 2)),
94
+ ((16, 4, 2), (16, 4, 2)),
95
+ ((16, 4, 2), (16, 2, 4)),
96
+ ((16, 4, 2), (8, 8, 2)),
97
+ ((16, 4, 2), (8, 4, 4)),
98
+ ((16, 4, 2), (8, 2, 8)),
99
+ ((16, 4, 2), (4, 16, 2)),
100
+ ((16, 4, 2), (4, 8, 4)),
101
+ ((16, 4, 2), (4, 4, 8)),
102
+ ((16, 4, 2), (4, 2, 16)),
103
+ ((16, 4, 2), (2, 32, 2)),
104
+ ((16, 4, 2), (2, 16, 4)),
105
+ ((16, 4, 2), (2, 8, 8)),
106
+ ((16, 4, 2), (2, 4, 16)),
107
+ ((16, 4, 2), (2, 2, 32)),
108
+ ((16, 2, 4), (32, 2, 2)),
109
+ ((16, 2, 4), (16, 4, 2)),
110
+ ((16, 2, 4), (16, 2, 4)),
111
+ ((16, 2, 4), (8, 8, 2)),
112
+ ((16, 2, 4), (8, 4, 4)),
113
+ ((16, 2, 4), (8, 2, 8)),
114
+ ((16, 2, 4), (4, 16, 2)),
115
+ ((16, 2, 4), (4, 8, 4)),
116
+ ((16, 2, 4), (4, 4, 8)),
117
+ ((16, 2, 4), (4, 2, 16)),
118
+ ((16, 2, 4), (2, 32, 2)),
119
+ ((16, 2, 4), (2, 16, 4)),
120
+ ((16, 2, 4), (2, 8, 8)),
121
+ ((16, 2, 4), (2, 4, 16)),
122
+ ((16, 2, 4), (2, 2, 32)),
123
+ ((8, 8, 2), (32, 2, 2)),
124
+ ((8, 8, 2), (16, 4, 2)),
125
+ ((8, 8, 2), (16, 2, 4)),
126
+ ((8, 8, 2), (8, 8, 2)),
127
+ ((8, 8, 2), (8, 4, 4)),
128
+ ((8, 8, 2), (8, 2, 8)),
129
+ ((8, 8, 2), (4, 16, 2)),
130
+ ((8, 8, 2), (4, 8, 4)),
131
+ ((8, 8, 2), (4, 4, 8)),
132
+ ((8, 8, 2), (4, 2, 16)),
133
+ ((8, 8, 2), (2, 32, 2)),
134
+ ((8, 8, 2), (2, 16, 4)),
135
+ ((8, 8, 2), (2, 8, 8)),
136
+ ((8, 8, 2), (2, 4, 16)),
137
+ ((8, 8, 2), (2, 2, 32)),
138
+ ((8, 4, 4), (32, 2, 2)),
139
+ ((8, 4, 4), (16, 4, 2)),
140
+ ((8, 4, 4), (16, 2, 4)),
141
+ ((8, 4, 4), (8, 8, 2)),
142
+ ((8, 4, 4), (8, 4, 4)),
143
+ ((8, 4, 4), (8, 2, 8)),
144
+ ((8, 4, 4), (4, 16, 2)),
145
+ ((8, 4, 4), (4, 8, 4)),
146
+ ((8, 4, 4), (4, 4, 8)),
147
+ ((8, 4, 4), (4, 2, 16)),
148
+ ((8, 4, 4), (2, 32, 2)),
149
+ ((8, 4, 4), (2, 16, 4)),
150
+ ((8, 4, 4), (2, 8, 8)),
151
+ ((8, 4, 4), (2, 4, 16)),
152
+ ((8, 4, 4), (2, 2, 32)),
153
+ ((8, 2, 8), (32, 2, 2)),
154
+ ((8, 2, 8), (16, 4, 2)),
155
+ ((8, 2, 8), (16, 2, 4)),
156
+ ((8, 2, 8), (8, 8, 2)),
157
+ ((8, 2, 8), (8, 4, 4)),
158
+ ((8, 2, 8), (8, 2, 8)),
159
+ ((8, 2, 8), (4, 16, 2)),
160
+ ((8, 2, 8), (4, 8, 4)),
161
+ ((8, 2, 8), (4, 4, 8)),
162
+ ((8, 2, 8), (4, 2, 16)),
163
+ ((8, 2, 8), (2, 32, 2)),
164
+ ((8, 2, 8), (2, 16, 4)),
165
+ ((8, 2, 8), (2, 8, 8)),
166
+ ((8, 2, 8), (2, 4, 16)),
167
+ ((8, 2, 8), (2, 2, 32)),
168
+ ((4, 16, 2), (32, 2, 2)),
169
+ ((4, 16, 2), (16, 4, 2)),
170
+ ((4, 16, 2), (16, 2, 4)),
171
+ ((4, 16, 2), (8, 8, 2)),
172
+ ((4, 16, 2), (8, 4, 4)),
173
+ ((4, 16, 2), (8, 2, 8)),
174
+ ((4, 16, 2), (4, 16, 2)),
175
+ ((4, 16, 2), (4, 8, 4)),
176
+ ((4, 16, 2), (4, 4, 8)),
177
+ ((4, 16, 2), (4, 2, 16)),
178
+ ((4, 16, 2), (2, 32, 2)),
179
+ ((4, 16, 2), (2, 16, 4)),
180
+ ((4, 16, 2), (2, 8, 8)),
181
+ ((4, 16, 2), (2, 4, 16)),
182
+ ((4, 16, 2), (2, 2, 32)),
183
+ ((4, 8, 4), (32, 2, 2)),
184
+ ((4, 8, 4), (16, 4, 2)),
185
+ ((4, 8, 4), (16, 2, 4)),
186
+ ((4, 8, 4), (8, 8, 2)),
187
+ ((4, 8, 4), (8, 4, 4)),
188
+ ((4, 8, 4), (8, 2, 8)),
189
+ ((4, 8, 4), (4, 16, 2)),
190
+ ((4, 8, 4), (4, 8, 4)),
191
+ ((4, 8, 4), (4, 4, 8)),
192
+ ((4, 8, 4), (4, 2, 16)),
193
+ ((4, 8, 4), (2, 32, 2)),
194
+ ((4, 8, 4), (2, 16, 4)),
195
+ ((4, 8, 4), (2, 8, 8)),
196
+ ((4, 8, 4), (2, 4, 16)),
197
+ ((4, 8, 4), (2, 2, 32)),
198
+ ((4, 4, 8), (32, 2, 2)),
199
+ ((4, 4, 8), (16, 4, 2)),
200
+ ((4, 4, 8), (16, 2, 4)),
201
+ ((4, 4, 8), (8, 8, 2)),
202
+ ((4, 4, 8), (8, 4, 4)),
203
+ ((4, 4, 8), (8, 2, 8)),
204
+ ((4, 4, 8), (4, 16, 2)),
205
+ ((4, 4, 8), (4, 8, 4)),
206
+ ((4, 4, 8), (4, 4, 8)),
207
+ ((4, 4, 8), (4, 2, 16)),
208
+ ((4, 4, 8), (2, 32, 2)),
209
+ ((4, 4, 8), (2, 16, 4)),
210
+ ((4, 4, 8), (2, 8, 8)),
211
+ ((4, 4, 8), (2, 4, 16)),
212
+ ((4, 4, 8), (2, 2, 32)),
213
+ ((4, 2, 16), (32, 2, 2)),
214
+ ((4, 2, 16), (16, 4, 2)),
215
+ ((4, 2, 16), (16, 2, 4)),
216
+ ((4, 2, 16), (8, 8, 2)),
217
+ ((4, 2, 16), (8, 4, 4)),
218
+ ((4, 2, 16), (8, 2, 8)),
219
+ ((4, 2, 16), (4, 16, 2)),
220
+ ((4, 2, 16), (4, 8, 4)),
221
+ ((4, 2, 16), (4, 4, 8)),
222
+ ((4, 2, 16), (4, 2, 16)),
223
+ ((4, 2, 16), (2, 32, 2)),
224
+ ((4, 2, 16), (2, 16, 4)),
225
+ ((4, 2, 16), (2, 8, 8)),
226
+ ((4, 2, 16), (2, 4, 16)),
227
+ ((4, 2, 16), (2, 2, 32)),
228
+ ((2, 32, 2), (32, 2, 2)),
229
+ ((2, 32, 2), (16, 4, 2)),
230
+ ((2, 32, 2), (16, 2, 4)),
231
+ ((2, 32, 2), (8, 8, 2)),
232
+ ((2, 32, 2), (8, 4, 4)),
233
+ ((2, 32, 2), (8, 2, 8)),
234
+ ((2, 32, 2), (4, 16, 2)),
235
+ ((2, 32, 2), (4, 8, 4)),
236
+ ((2, 32, 2), (4, 4, 8)),
237
+ ((2, 32, 2), (4, 2, 16)),
238
+ ((2, 32, 2), (2, 32, 2)),
239
+ ((2, 32, 2), (2, 16, 4)),
240
+ ((2, 32, 2), (2, 8, 8)),
241
+ ((2, 32, 2), (2, 4, 16)),
242
+ ((2, 32, 2), (2, 2, 32)),
243
+ ((2, 16, 4), (32, 2, 2)),
244
+ ((2, 16, 4), (16, 4, 2)),
245
+ ((2, 16, 4), (16, 2, 4)),
246
+ ((2, 16, 4), (8, 8, 2)),
247
+ ((2, 16, 4), (8, 4, 4)),
248
+ ((2, 16, 4), (8, 2, 8)),
249
+ ((2, 16, 4), (4, 16, 2)),
250
+ ((2, 16, 4), (4, 8, 4)),
251
+ ((2, 16, 4), (4, 4, 8)),
252
+ ((2, 16, 4), (4, 2, 16)),
253
+ ((2, 16, 4), (2, 32, 2)),
254
+ ((2, 16, 4), (2, 16, 4)),
255
+ ((2, 16, 4), (2, 8, 8)),
256
+ ((2, 16, 4), (2, 4, 16)),
257
+ ((2, 16, 4), (2, 2, 32)),
258
+ ((2, 8, 8), (32, 2, 2)),
259
+ ((2, 8, 8), (16, 4, 2)),
260
+ ((2, 8, 8), (16, 2, 4)),
261
+ ((2, 8, 8), (8, 8, 2)),
262
+ ((2, 8, 8), (8, 4, 4)),
263
+ ((2, 8, 8), (8, 2, 8)),
264
+ ((2, 8, 8), (4, 16, 2)),
265
+ ((2, 8, 8), (4, 8, 4)),
266
+ ((2, 8, 8), (4, 4, 8)),
267
+ ((2, 8, 8), (4, 2, 16)),
268
+ ((2, 8, 8), (2, 32, 2)),
269
+ ((2, 8, 8), (2, 16, 4)),
270
+ ((2, 8, 8), (2, 8, 8)),
271
+ ((2, 8, 8), (2, 4, 16)),
272
+ ((2, 8, 8), (2, 2, 32)),
273
+ ((2, 4, 16), (32, 2, 2)),
274
+ ((2, 4, 16), (16, 4, 2)),
275
+ ((2, 4, 16), (16, 2, 4)),
276
+ ((2, 4, 16), (8, 8, 2)),
277
+ ((2, 4, 16), (8, 4, 4)),
278
+ ((2, 4, 16), (8, 2, 8)),
279
+ ((2, 4, 16), (4, 16, 2)),
280
+ ((2, 4, 16), (4, 8, 4)),
281
+ ((2, 4, 16), (4, 4, 8)),
282
+ ((2, 4, 16), (4, 2, 16)),
283
+ ((2, 4, 16), (2, 32, 2)),
284
+ ((2, 4, 16), (2, 16, 4)),
285
+ ((2, 4, 16), (2, 8, 8)),
286
+ ((2, 4, 16), (2, 4, 16)),
287
+ ((2, 4, 16), (2, 2, 32)),
288
+ ((2, 2, 32), (32, 2, 2)),
289
+ ((2, 2, 32), (16, 4, 2)),
290
+ ((2, 2, 32), (16, 2, 4)),
291
+ ((2, 2, 32), (8, 8, 2)),
292
+ ((2, 2, 32), (8, 4, 4)),
293
+ ((2, 2, 32), (8, 2, 8)),
294
+ ((2, 2, 32), (4, 16, 2)),
295
+ ((2, 2, 32), (4, 8, 4)),
296
+ ((2, 2, 32), (4, 4, 8)),
297
+ ((2, 2, 32), (4, 2, 16)),
298
+ ((2, 2, 32), (2, 32, 2)),
299
+ ((2, 2, 32), (2, 16, 4)),
300
+ ((2, 2, 32), (2, 8, 8)),
301
+ ((2, 2, 32), (2, 4, 16)),
302
+ ((2, 2, 32), (2, 2, 32)),
303
+ ],
304
+ }
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_backward_128x64.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import Dict, List
26
+
27
+ from ...._types import CutlassFnaForwardConfigType
28
+
29
+ # NOTE: we're excluding tile shapes that include 1 just to
30
+ # reduce the giant number of configs down to a reasonable
31
+ # amount; otherwise autotuning would take more than a few
32
+ # seconds per call which is unacceptable. Tile shapes with
33
+ # 1s are rarely selected.
34
+
35
+ _FNA_BACKWARD_128x64_TILE_SIZES: Dict[int, List[CutlassFnaForwardConfigType]] = {
36
+ 1: [
37
+ ((128,), (64,)),
38
+ ],
39
+ 2: [
40
+ ((64, 2), (32, 2)),
41
+ ((64, 2), (16, 4)),
42
+ ((64, 2), (8, 8)),
43
+ ((64, 2), (4, 16)),
44
+ ((64, 2), (2, 32)),
45
+ ((32, 4), (32, 2)),
46
+ ((32, 4), (16, 4)),
47
+ ((32, 4), (8, 8)),
48
+ ((32, 4), (4, 16)),
49
+ ((32, 4), (2, 32)),
50
+ ((16, 8), (32, 2)),
51
+ ((16, 8), (16, 4)),
52
+ ((16, 8), (8, 8)),
53
+ ((16, 8), (4, 16)),
54
+ ((16, 8), (2, 32)),
55
+ ((8, 16), (32, 2)),
56
+ ((8, 16), (16, 4)),
57
+ ((8, 16), (8, 8)),
58
+ ((8, 16), (4, 16)),
59
+ ((8, 16), (2, 32)),
60
+ ((4, 32), (32, 2)),
61
+ ((4, 32), (16, 4)),
62
+ ((4, 32), (8, 8)),
63
+ ((4, 32), (4, 16)),
64
+ ((4, 32), (2, 32)),
65
+ ((2, 64), (32, 2)),
66
+ ((2, 64), (16, 4)),
67
+ ((2, 64), (8, 8)),
68
+ ((2, 64), (4, 16)),
69
+ ((2, 64), (2, 32)),
70
+ ],
71
+ 3: [
72
+ ((32, 2, 2), (16, 2, 2)),
73
+ ((32, 2, 2), (8, 4, 2)),
74
+ ((32, 2, 2), (8, 2, 4)),
75
+ ((32, 2, 2), (4, 8, 2)),
76
+ ((32, 2, 2), (4, 4, 4)),
77
+ ((32, 2, 2), (4, 2, 8)),
78
+ ((32, 2, 2), (2, 16, 2)),
79
+ ((32, 2, 2), (2, 8, 4)),
80
+ ((32, 2, 2), (2, 4, 8)),
81
+ ((32, 2, 2), (2, 2, 16)),
82
+ ((16, 4, 2), (16, 2, 2)),
83
+ ((16, 4, 2), (8, 4, 2)),
84
+ ((16, 4, 2), (8, 2, 4)),
85
+ ((16, 4, 2), (4, 8, 2)),
86
+ ((16, 4, 2), (4, 4, 4)),
87
+ ((16, 4, 2), (4, 2, 8)),
88
+ ((16, 4, 2), (2, 16, 2)),
89
+ ((16, 4, 2), (2, 8, 4)),
90
+ ((16, 4, 2), (2, 4, 8)),
91
+ ((16, 4, 2), (2, 2, 16)),
92
+ ((16, 2, 4), (16, 2, 2)),
93
+ ((16, 2, 4), (8, 4, 2)),
94
+ ((16, 2, 4), (8, 2, 4)),
95
+ ((16, 2, 4), (4, 8, 2)),
96
+ ((16, 2, 4), (4, 4, 4)),
97
+ ((16, 2, 4), (4, 2, 8)),
98
+ ((16, 2, 4), (2, 16, 2)),
99
+ ((16, 2, 4), (2, 8, 4)),
100
+ ((16, 2, 4), (2, 4, 8)),
101
+ ((16, 2, 4), (2, 2, 16)),
102
+ ((8, 8, 2), (16, 2, 2)),
103
+ ((8, 8, 2), (8, 4, 2)),
104
+ ((8, 8, 2), (8, 2, 4)),
105
+ ((8, 8, 2), (4, 8, 2)),
106
+ ((8, 8, 2), (4, 4, 4)),
107
+ ((8, 8, 2), (4, 2, 8)),
108
+ ((8, 8, 2), (2, 16, 2)),
109
+ ((8, 8, 2), (2, 8, 4)),
110
+ ((8, 8, 2), (2, 4, 8)),
111
+ ((8, 8, 2), (2, 2, 16)),
112
+ ((8, 4, 4), (16, 2, 2)),
113
+ ((8, 4, 4), (8, 4, 2)),
114
+ ((8, 4, 4), (8, 2, 4)),
115
+ ((8, 4, 4), (4, 8, 2)),
116
+ ((8, 4, 4), (4, 4, 4)),
117
+ ((8, 4, 4), (4, 2, 8)),
118
+ ((8, 4, 4), (2, 16, 2)),
119
+ ((8, 4, 4), (2, 8, 4)),
120
+ ((8, 4, 4), (2, 4, 8)),
121
+ ((8, 4, 4), (2, 2, 16)),
122
+ ((8, 2, 8), (16, 2, 2)),
123
+ ((8, 2, 8), (8, 4, 2)),
124
+ ((8, 2, 8), (8, 2, 4)),
125
+ ((8, 2, 8), (4, 8, 2)),
126
+ ((8, 2, 8), (4, 4, 4)),
127
+ ((8, 2, 8), (4, 2, 8)),
128
+ ((8, 2, 8), (2, 16, 2)),
129
+ ((8, 2, 8), (2, 8, 4)),
130
+ ((8, 2, 8), (2, 4, 8)),
131
+ ((8, 2, 8), (2, 2, 16)),
132
+ ((4, 16, 2), (16, 2, 2)),
133
+ ((4, 16, 2), (8, 4, 2)),
134
+ ((4, 16, 2), (8, 2, 4)),
135
+ ((4, 16, 2), (4, 8, 2)),
136
+ ((4, 16, 2), (4, 4, 4)),
137
+ ((4, 16, 2), (4, 2, 8)),
138
+ ((4, 16, 2), (2, 16, 2)),
139
+ ((4, 16, 2), (2, 8, 4)),
140
+ ((4, 16, 2), (2, 4, 8)),
141
+ ((4, 16, 2), (2, 2, 16)),
142
+ ((4, 8, 4), (16, 2, 2)),
143
+ ((4, 8, 4), (8, 4, 2)),
144
+ ((4, 8, 4), (8, 2, 4)),
145
+ ((4, 8, 4), (4, 8, 2)),
146
+ ((4, 8, 4), (4, 4, 4)),
147
+ ((4, 8, 4), (4, 2, 8)),
148
+ ((4, 8, 4), (2, 16, 2)),
149
+ ((4, 8, 4), (2, 8, 4)),
150
+ ((4, 8, 4), (2, 4, 8)),
151
+ ((4, 8, 4), (2, 2, 16)),
152
+ ((4, 4, 8), (16, 2, 2)),
153
+ ((4, 4, 8), (8, 4, 2)),
154
+ ((4, 4, 8), (8, 2, 4)),
155
+ ((4, 4, 8), (4, 8, 2)),
156
+ ((4, 4, 8), (4, 4, 4)),
157
+ ((4, 4, 8), (4, 2, 8)),
158
+ ((4, 4, 8), (2, 16, 2)),
159
+ ((4, 4, 8), (2, 8, 4)),
160
+ ((4, 4, 8), (2, 4, 8)),
161
+ ((4, 4, 8), (2, 2, 16)),
162
+ ((4, 2, 16), (16, 2, 2)),
163
+ ((4, 2, 16), (8, 4, 2)),
164
+ ((4, 2, 16), (8, 2, 4)),
165
+ ((4, 2, 16), (4, 8, 2)),
166
+ ((4, 2, 16), (4, 4, 4)),
167
+ ((4, 2, 16), (4, 2, 8)),
168
+ ((4, 2, 16), (2, 16, 2)),
169
+ ((4, 2, 16), (2, 8, 4)),
170
+ ((4, 2, 16), (2, 4, 8)),
171
+ ((4, 2, 16), (2, 2, 16)),
172
+ ((2, 32, 2), (16, 2, 2)),
173
+ ((2, 32, 2), (8, 4, 2)),
174
+ ((2, 32, 2), (8, 2, 4)),
175
+ ((2, 32, 2), (4, 8, 2)),
176
+ ((2, 32, 2), (4, 4, 4)),
177
+ ((2, 32, 2), (4, 2, 8)),
178
+ ((2, 32, 2), (2, 16, 2)),
179
+ ((2, 32, 2), (2, 8, 4)),
180
+ ((2, 32, 2), (2, 4, 8)),
181
+ ((2, 32, 2), (2, 2, 16)),
182
+ ((2, 16, 4), (16, 2, 2)),
183
+ ((2, 16, 4), (8, 4, 2)),
184
+ ((2, 16, 4), (8, 2, 4)),
185
+ ((2, 16, 4), (4, 8, 2)),
186
+ ((2, 16, 4), (4, 4, 4)),
187
+ ((2, 16, 4), (4, 2, 8)),
188
+ ((2, 16, 4), (2, 16, 2)),
189
+ ((2, 16, 4), (2, 8, 4)),
190
+ ((2, 16, 4), (2, 4, 8)),
191
+ ((2, 16, 4), (2, 2, 16)),
192
+ ((2, 8, 8), (16, 2, 2)),
193
+ ((2, 8, 8), (8, 4, 2)),
194
+ ((2, 8, 8), (8, 2, 4)),
195
+ ((2, 8, 8), (4, 8, 2)),
196
+ ((2, 8, 8), (4, 4, 4)),
197
+ ((2, 8, 8), (4, 2, 8)),
198
+ ((2, 8, 8), (2, 16, 2)),
199
+ ((2, 8, 8), (2, 8, 4)),
200
+ ((2, 8, 8), (2, 4, 8)),
201
+ ((2, 8, 8), (2, 2, 16)),
202
+ ((2, 4, 16), (16, 2, 2)),
203
+ ((2, 4, 16), (8, 4, 2)),
204
+ ((2, 4, 16), (8, 2, 4)),
205
+ ((2, 4, 16), (4, 8, 2)),
206
+ ((2, 4, 16), (4, 4, 4)),
207
+ ((2, 4, 16), (4, 2, 8)),
208
+ ((2, 4, 16), (2, 16, 2)),
209
+ ((2, 4, 16), (2, 8, 4)),
210
+ ((2, 4, 16), (2, 4, 8)),
211
+ ((2, 4, 16), (2, 2, 16)),
212
+ ((2, 2, 32), (16, 2, 2)),
213
+ ((2, 2, 32), (8, 4, 2)),
214
+ ((2, 2, 32), (8, 2, 4)),
215
+ ((2, 2, 32), (4, 8, 2)),
216
+ ((2, 2, 32), (4, 4, 4)),
217
+ ((2, 2, 32), (4, 2, 8)),
218
+ ((2, 2, 32), (2, 16, 2)),
219
+ ((2, 2, 32), (2, 8, 4)),
220
+ ((2, 2, 32), (2, 4, 8)),
221
+ ((2, 2, 32), (2, 2, 16)),
222
+ ],
223
+ }
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_backward_64x64.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import Dict, List
26
+
27
+ from ...._types import QKTileShapeType
28
+
29
+ # NOTE: we're excluding tile shapes that include 1 just to
30
+ # reduce the giant number of configs down to a reasonable
31
+ # amount; otherwise autotuning would take more than a few
32
+ # seconds per call which is unacceptable. Tile shapes with
33
+ # 1s are rarely selected.
34
+
35
+ _FNA_BACKWARD_64x64_TILE_SIZES: Dict[int, List[QKTileShapeType]] = {
36
+ 1: [
37
+ ((64,), (64,)),
38
+ ],
39
+ 2: [
40
+ ((32, 2), (32, 2)),
41
+ ((32, 2), (16, 4)),
42
+ ((32, 2), (8, 8)),
43
+ ((32, 2), (4, 16)),
44
+ ((32, 2), (2, 32)),
45
+ ((16, 4), (32, 2)),
46
+ ((16, 4), (16, 4)),
47
+ ((16, 4), (8, 8)),
48
+ ((16, 4), (4, 16)),
49
+ ((16, 4), (2, 32)),
50
+ ((8, 8), (32, 2)),
51
+ ((8, 8), (16, 4)),
52
+ ((8, 8), (8, 8)),
53
+ ((8, 8), (4, 16)),
54
+ ((8, 8), (2, 32)),
55
+ ((4, 16), (32, 2)),
56
+ ((4, 16), (16, 4)),
57
+ ((4, 16), (8, 8)),
58
+ ((4, 16), (4, 16)),
59
+ ((4, 16), (2, 32)),
60
+ ((2, 32), (32, 2)),
61
+ ((2, 32), (16, 4)),
62
+ ((2, 32), (8, 8)),
63
+ ((2, 32), (4, 16)),
64
+ ((2, 32), (2, 32)),
65
+ ],
66
+ 3: [
67
+ ((16, 2, 2), (16, 2, 2)),
68
+ ((16, 2, 2), (8, 4, 2)),
69
+ ((16, 2, 2), (8, 2, 4)),
70
+ ((16, 2, 2), (4, 8, 2)),
71
+ ((16, 2, 2), (4, 4, 4)),
72
+ ((16, 2, 2), (4, 2, 8)),
73
+ ((16, 2, 2), (2, 16, 2)),
74
+ ((16, 2, 2), (2, 8, 4)),
75
+ ((16, 2, 2), (2, 4, 8)),
76
+ ((16, 2, 2), (2, 2, 16)),
77
+ ((8, 4, 2), (16, 2, 2)),
78
+ ((8, 4, 2), (8, 4, 2)),
79
+ ((8, 4, 2), (8, 2, 4)),
80
+ ((8, 4, 2), (4, 8, 2)),
81
+ ((8, 4, 2), (4, 4, 4)),
82
+ ((8, 4, 2), (4, 2, 8)),
83
+ ((8, 4, 2), (2, 16, 2)),
84
+ ((8, 4, 2), (2, 8, 4)),
85
+ ((8, 4, 2), (2, 4, 8)),
86
+ ((8, 4, 2), (2, 2, 16)),
87
+ ((8, 2, 4), (16, 2, 2)),
88
+ ((8, 2, 4), (8, 4, 2)),
89
+ ((8, 2, 4), (8, 2, 4)),
90
+ ((8, 2, 4), (4, 8, 2)),
91
+ ((8, 2, 4), (4, 4, 4)),
92
+ ((8, 2, 4), (4, 2, 8)),
93
+ ((8, 2, 4), (2, 16, 2)),
94
+ ((8, 2, 4), (2, 8, 4)),
95
+ ((8, 2, 4), (2, 4, 8)),
96
+ ((8, 2, 4), (2, 2, 16)),
97
+ ((4, 8, 2), (16, 2, 2)),
98
+ ((4, 8, 2), (8, 4, 2)),
99
+ ((4, 8, 2), (8, 2, 4)),
100
+ ((4, 8, 2), (4, 8, 2)),
101
+ ((4, 8, 2), (4, 4, 4)),
102
+ ((4, 8, 2), (4, 2, 8)),
103
+ ((4, 8, 2), (2, 16, 2)),
104
+ ((4, 8, 2), (2, 8, 4)),
105
+ ((4, 8, 2), (2, 4, 8)),
106
+ ((4, 8, 2), (2, 2, 16)),
107
+ ((4, 4, 4), (16, 2, 2)),
108
+ ((4, 4, 4), (8, 4, 2)),
109
+ ((4, 4, 4), (8, 2, 4)),
110
+ ((4, 4, 4), (4, 8, 2)),
111
+ ((4, 4, 4), (4, 4, 4)),
112
+ ((4, 4, 4), (4, 2, 8)),
113
+ ((4, 4, 4), (2, 16, 2)),
114
+ ((4, 4, 4), (2, 8, 4)),
115
+ ((4, 4, 4), (2, 4, 8)),
116
+ ((4, 4, 4), (2, 2, 16)),
117
+ ((4, 2, 8), (16, 2, 2)),
118
+ ((4, 2, 8), (8, 4, 2)),
119
+ ((4, 2, 8), (8, 2, 4)),
120
+ ((4, 2, 8), (4, 8, 2)),
121
+ ((4, 2, 8), (4, 4, 4)),
122
+ ((4, 2, 8), (4, 2, 8)),
123
+ ((4, 2, 8), (2, 16, 2)),
124
+ ((4, 2, 8), (2, 8, 4)),
125
+ ((4, 2, 8), (2, 4, 8)),
126
+ ((4, 2, 8), (2, 2, 16)),
127
+ ((2, 16, 2), (16, 2, 2)),
128
+ ((2, 16, 2), (8, 4, 2)),
129
+ ((2, 16, 2), (8, 2, 4)),
130
+ ((2, 16, 2), (4, 8, 2)),
131
+ ((2, 16, 2), (4, 4, 4)),
132
+ ((2, 16, 2), (4, 2, 8)),
133
+ ((2, 16, 2), (2, 16, 2)),
134
+ ((2, 16, 2), (2, 8, 4)),
135
+ ((2, 16, 2), (2, 4, 8)),
136
+ ((2, 16, 2), (2, 2, 16)),
137
+ ((2, 8, 4), (16, 2, 2)),
138
+ ((2, 8, 4), (8, 4, 2)),
139
+ ((2, 8, 4), (8, 2, 4)),
140
+ ((2, 8, 4), (4, 8, 2)),
141
+ ((2, 8, 4), (4, 4, 4)),
142
+ ((2, 8, 4), (4, 2, 8)),
143
+ ((2, 8, 4), (2, 16, 2)),
144
+ ((2, 8, 4), (2, 8, 4)),
145
+ ((2, 8, 4), (2, 4, 8)),
146
+ ((2, 8, 4), (2, 2, 16)),
147
+ ((2, 4, 8), (16, 2, 2)),
148
+ ((2, 4, 8), (8, 4, 2)),
149
+ ((2, 4, 8), (8, 2, 4)),
150
+ ((2, 4, 8), (4, 8, 2)),
151
+ ((2, 4, 8), (4, 4, 4)),
152
+ ((2, 4, 8), (4, 2, 8)),
153
+ ((2, 4, 8), (2, 16, 2)),
154
+ ((2, 4, 8), (2, 8, 4)),
155
+ ((2, 4, 8), (2, 4, 8)),
156
+ ((2, 4, 8), (2, 2, 16)),
157
+ ((2, 2, 16), (16, 2, 2)),
158
+ ((2, 2, 16), (8, 4, 2)),
159
+ ((2, 2, 16), (8, 2, 4)),
160
+ ((2, 2, 16), (4, 8, 2)),
161
+ ((2, 2, 16), (4, 4, 4)),
162
+ ((2, 2, 16), (4, 2, 8)),
163
+ ((2, 2, 16), (2, 16, 2)),
164
+ ((2, 2, 16), (2, 8, 4)),
165
+ ((2, 2, 16), (2, 4, 8)),
166
+ ((2, 2, 16), (2, 2, 16)),
167
+ ],
168
+ }
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_forward_32x128.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import Dict, List
26
+
27
+ from ...._types import QKTileShapeType
28
+
29
+ # TODO: More combinations are possible for
30
+ # 2D and 3D (query tile does not have to be smaller
31
+ # than KV tile); but that behavior is untested,
32
+ # and IIRC was unstable.
33
+
34
+ _FNA_FORWARD_32x128_TILE_SIZES: Dict[int, List[QKTileShapeType]] = {
35
+ 1: [
36
+ ((32,), (128,)),
37
+ ],
38
+ 2: [
39
+ ((16, 2), (64, 2)),
40
+ ((16, 2), (32, 4)),
41
+ ((16, 2), (16, 8)),
42
+ ((8, 4), (32, 4)),
43
+ ((8, 4), (16, 8)),
44
+ ((8, 4), (8, 16)),
45
+ ((4, 8), (16, 8)),
46
+ ((4, 8), (8, 16)),
47
+ ((4, 8), (4, 32)),
48
+ ((2, 16), (8, 16)),
49
+ ((2, 16), (4, 32)),
50
+ ((2, 16), (2, 64)),
51
+ ],
52
+ 3: [
53
+ ((8, 2, 2), (32, 2, 2)),
54
+ ((8, 2, 2), (16, 4, 2)),
55
+ ((8, 2, 2), (16, 2, 4)),
56
+ ((8, 2, 2), (8, 8, 2)),
57
+ ((8, 2, 2), (8, 4, 4)),
58
+ ((8, 2, 2), (8, 2, 8)),
59
+ ((4, 4, 2), (16, 4, 2)),
60
+ ((4, 4, 2), (8, 8, 2)),
61
+ ((4, 4, 2), (8, 4, 4)),
62
+ ((4, 4, 2), (4, 16, 2)),
63
+ ((4, 4, 2), (4, 8, 4)),
64
+ ((4, 4, 2), (4, 4, 8)),
65
+ ((4, 2, 4), (16, 2, 4)),
66
+ ((4, 2, 4), (8, 4, 4)),
67
+ ((4, 2, 4), (8, 2, 8)),
68
+ ((4, 2, 4), (4, 8, 4)),
69
+ ((4, 2, 4), (4, 4, 8)),
70
+ ((4, 2, 4), (4, 2, 16)),
71
+ ((2, 8, 2), (8, 8, 2)),
72
+ ((2, 8, 2), (4, 16, 2)),
73
+ ((2, 8, 2), (4, 8, 4)),
74
+ ((2, 8, 2), (2, 32, 2)),
75
+ ((2, 8, 2), (2, 16, 4)),
76
+ ((2, 8, 2), (2, 8, 8)),
77
+ ((2, 4, 4), (8, 4, 4)),
78
+ ((2, 4, 4), (4, 8, 4)),
79
+ ((2, 4, 4), (4, 4, 8)),
80
+ ((2, 4, 4), (2, 16, 4)),
81
+ ((2, 4, 4), (2, 8, 8)),
82
+ ((2, 4, 4), (2, 4, 16)),
83
+ ((2, 2, 8), (8, 2, 8)),
84
+ ((2, 2, 8), (4, 4, 8)),
85
+ ((2, 2, 8), (4, 2, 16)),
86
+ ((2, 2, 8), (2, 8, 8)),
87
+ ((2, 2, 8), (2, 4, 16)),
88
+ ((2, 2, 8), (2, 2, 32)),
89
+ ],
90
+ }
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_forward_64x128.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import Dict, List
26
+
27
+ from ...._types import QKTileShapeType
28
+
29
+ # TODO: More combinations are possible for
30
+ # 2D and 3D (query tile does not have to be smaller
31
+ # than KV tile); but that behavior is untested,
32
+ # and IIRC was unstable.
33
+
34
+ _FNA_FORWARD_64x128_TILE_SIZES: Dict[int, List[QKTileShapeType]] = {
35
+ 1: [
36
+ ((64,), (128,)),
37
+ ],
38
+ 2: [
39
+ ((32, 2), (64, 2)),
40
+ ((32, 2), (32, 4)),
41
+ ((16, 4), (32, 4)),
42
+ ((16, 4), (16, 8)),
43
+ ((8, 8), (16, 8)),
44
+ ((8, 8), (8, 16)),
45
+ ((4, 16), (8, 16)),
46
+ ((4, 16), (4, 32)),
47
+ ((2, 32), (4, 32)),
48
+ ((2, 32), (2, 64)),
49
+ ],
50
+ 3: [
51
+ ((16, 2, 2), (32, 2, 2)),
52
+ ((16, 2, 2), (16, 4, 2)),
53
+ ((16, 2, 2), (16, 2, 4)),
54
+ ((8, 4, 2), (16, 4, 2)),
55
+ ((8, 4, 2), (8, 8, 2)),
56
+ ((8, 4, 2), (8, 4, 4)),
57
+ ((8, 2, 4), (16, 2, 4)),
58
+ ((8, 2, 4), (8, 4, 4)),
59
+ ((8, 2, 4), (8, 2, 8)),
60
+ ((4, 8, 2), (8, 8, 2)),
61
+ ((4, 8, 2), (4, 16, 2)),
62
+ ((4, 8, 2), (4, 8, 4)),
63
+ ((4, 4, 4), (8, 4, 4)),
64
+ ((4, 4, 4), (4, 8, 4)),
65
+ ((4, 4, 4), (4, 4, 8)),
66
+ ((4, 2, 8), (8, 2, 8)),
67
+ ((4, 2, 8), (4, 4, 8)),
68
+ ((4, 2, 8), (4, 2, 16)),
69
+ ((2, 16, 2), (4, 16, 2)),
70
+ ((2, 16, 2), (2, 32, 2)),
71
+ ((2, 16, 2), (2, 16, 4)),
72
+ ((2, 8, 4), (4, 8, 4)),
73
+ ((2, 8, 4), (2, 16, 4)),
74
+ ((2, 8, 4), (2, 8, 8)),
75
+ ((2, 4, 8), (4, 4, 8)),
76
+ ((2, 4, 8), (2, 8, 8)),
77
+ ((2, 4, 8), (2, 4, 16)),
78
+ ((2, 2, 16), (4, 2, 16)),
79
+ ((2, 2, 16), (2, 4, 16)),
80
+ ((2, 2, 16), (2, 2, 32)),
81
+ ],
82
+ }
build/torch210-cu126-aarch64-linux/backends/configs/cutlass/fna_forward_64x64.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+
25
+ from typing import Dict, List
26
+
27
+ from ...._types import QKTileShapeType
28
+
29
+ # TODO: More combinations are possible for
30
+ # 2D and 3D (query tile does not have to be smaller
31
+ # than KV tile); but that behavior is untested,
32
+ # and IIRC was unstable.
33
+
34
+ # NOTE: we're excluding tile shapes that include 1 just to
35
+ # reduce the giant number of configs down to a reasonable
36
+ # amount; otherwise autotuning would take more than a few
37
+ # seconds per call which is unacceptable. Tile shapes with
38
+ # 1s are rarely selected.
39
+
40
+ _FNA_FORWARD_64x64_TILE_SIZES: Dict[int, List[QKTileShapeType]] = {
41
+ 1: [
42
+ ((64,), (64,)),
43
+ ],
44
+ 2: [
45
+ ((32, 2), (32, 2)),
46
+ ((16, 4), (16, 4)),
47
+ ((8, 8), (8, 8)),
48
+ ((4, 16), (4, 16)),
49
+ ((2, 32), (2, 32)),
50
+ ],
51
+ 3: [
52
+ ((16, 2, 2), (16, 2, 2)),
53
+ ((8, 4, 2), (8, 4, 2)),
54
+ ((8, 2, 4), (8, 2, 4)),
55
+ ((4, 8, 2), (4, 8, 2)),
56
+ ((4, 4, 4), (4, 4, 4)),
57
+ ((4, 2, 8), (4, 2, 8)),
58
+ ((2, 16, 2), (2, 16, 2)),
59
+ ((2, 8, 4), (2, 8, 4)),
60
+ ((2, 4, 8), (2, 4, 8)),
61
+ ((2, 2, 16), (2, 2, 16)),
62
+ ],
63
+ }
build/torch210-cu126-aarch64-linux/backends/configs/cutlass_blackwell/__init__.py ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import List, Optional
25
+
26
+ import torch # noqa: F401
27
+ from torch import Tensor
28
+
29
+ from ...._types import (
30
+ CutlassBlackwellFmhaBackwardConfigType,
31
+ CutlassBlackwellFmhaForwardConfigType,
32
+ CutlassBlackwellFnaBackwardConfigType,
33
+ CutlassBlackwellFnaForwardConfigType,
34
+ DimensionType,
35
+ )
36
+ from ....utils.checks import check_tile_shape
37
+ from ....utils.device import get_device_cc
38
+
39
+ # The current CUTLASS FMHA forward kernel can only do Q tile size 256, KV tile size 128.
40
+ # This limits 1D tile shapes to just the one, but for 2-D and 3-D we can have many more shapes,
41
+ # only some of which we compile. Adding new ones requires adding them to autogen, regenerating
42
+ # the instantiations, and recompiling libnatten. Unlike CUTLASS 2.X FNA, multi-dim tile shapes are
43
+ # static in Blackwell FNA, and not dynamic.
44
+
45
+ BLACKWELL_FORWARD_TILE_SHAPES = {
46
+ 1: [
47
+ ((256,), (128,)),
48
+ ],
49
+ 2: [
50
+ ((16, 16), (16, 8)),
51
+ ((16, 16), (8, 16)),
52
+ ((8, 32), (8, 16)),
53
+ ((8, 32), (4, 32)),
54
+ ],
55
+ 3: [
56
+ ((8, 4, 8), (4, 4, 8)),
57
+ ((8, 4, 8), (2, 8, 8)),
58
+ ((2, 8, 16), (4, 4, 8)),
59
+ ((2, 8, 16), (2, 8, 8)),
60
+ ((4, 4, 16), (2, 4, 16)),
61
+ ((2, 16, 8), (2, 8, 8)),
62
+ ((4, 8, 8), (2, 8, 8)),
63
+ ],
64
+ }
65
+
66
+ BLACKWELL_BACKWARD_TILE_SHAPES = {
67
+ 1: [
68
+ ((128,), (128,)),
69
+ ],
70
+ 2: [
71
+ ((16, 8), (16, 8)),
72
+ ((16, 8), (8, 16)),
73
+ ((8, 16), (16, 8)),
74
+ ((8, 16), (8, 16)),
75
+ ],
76
+ 3: [
77
+ ((4, 4, 8), (4, 4, 8)),
78
+ ((4, 4, 8), (2, 8, 8)),
79
+ ((1, 8, 16), (4, 4, 8)),
80
+ ((2, 8, 8), (4, 4, 8)),
81
+ ((1, 8, 16), (2, 8, 8)),
82
+ ((2, 4, 16), (2, 4, 16)),
83
+ ((4, 2, 16), (2, 4, 16)),
84
+ ((4, 4, 8), (2, 4, 16)),
85
+ ((2, 8, 8), (2, 8, 8)),
86
+ ],
87
+ }
88
+
89
+
90
+ def _get_default_tile_shapes_forward(
91
+ na_dim: int,
92
+ ) -> CutlassBlackwellFnaForwardConfigType:
93
+ assert na_dim in [1, 2, 3]
94
+
95
+ if na_dim == 1:
96
+ return ((256,), (128,))
97
+ if na_dim == 2:
98
+ return ((16, 16), (16, 8))
99
+ if na_dim == 3:
100
+ return ((8, 4, 8), (4, 4, 8))
101
+
102
+ raise NotImplementedError()
103
+
104
+
105
+ def get_all_forward_configs(
106
+ input_tensor: Tensor,
107
+ ) -> List[CutlassBlackwellFnaForwardConfigType]:
108
+ assert input_tensor.dim() in [4, 5, 6]
109
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
110
+
111
+ device_cc = get_device_cc(input_tensor.device)
112
+ if device_cc not in [100, 103]:
113
+ return []
114
+
115
+ return BLACKWELL_FORWARD_TILE_SHAPES[na_dim] # type: ignore
116
+
117
+
118
+ def get_all_backward_configs(
119
+ input_tensor: Tensor,
120
+ ) -> List[CutlassBlackwellFnaBackwardConfigType]:
121
+ assert input_tensor.dim() in [4, 5, 6]
122
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
123
+
124
+ device_cc = get_device_cc(input_tensor.device)
125
+ if device_cc not in [100, 103]:
126
+ return []
127
+
128
+ return BLACKWELL_BACKWARD_TILE_SHAPES[na_dim] # type: ignore
129
+
130
+
131
+ # For FMHA
132
+ def get_all_fmha_forward_configs(
133
+ input_tensor: Tensor,
134
+ ) -> List[CutlassBlackwellFmhaForwardConfigType]:
135
+ if input_tensor.dim() != 4:
136
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
137
+
138
+ tile_shapes = get_all_forward_configs(input_tensor)
139
+ assert all(len(q_t) == len(kv_t) == 1 for q_t, kv_t in tile_shapes)
140
+
141
+ tile_sizes = [(q_t[0], kv_t[0]) for q_t, kv_t in tile_shapes]
142
+
143
+ return tile_sizes
144
+
145
+
146
+ def get_all_fmha_backward_configs(
147
+ input_tensor: Tensor,
148
+ ) -> List[CutlassBlackwellFmhaBackwardConfigType]:
149
+ if input_tensor.dim() != 4:
150
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
151
+
152
+ tile_shapes = get_all_backward_configs(input_tensor)
153
+ assert all(len(q_t) == len(kv_t) == 1 for q_t, kv_t in tile_shapes)
154
+
155
+ tile_sizes = [(q_t[0], kv_t[0]) for q_t, kv_t in tile_shapes]
156
+
157
+ return tile_sizes
158
+
159
+
160
+ def get_default_forward_tile_shapes(
161
+ input_tensor: Tensor,
162
+ ) -> CutlassBlackwellFnaForwardConfigType:
163
+ assert input_tensor.dim() in [4, 5, 6]
164
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
165
+
166
+ return _get_default_tile_shapes_forward(na_dim)
167
+
168
+
169
+ def get_default_forward_tile_sizes(
170
+ input_tensor: Tensor,
171
+ ) -> CutlassBlackwellFmhaForwardConfigType:
172
+ if input_tensor.dim() != 4:
173
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
174
+
175
+ q_t, kv_t = get_default_forward_tile_shapes(input_tensor)
176
+ assert len(q_t) == len(kv_t) == 1
177
+
178
+ return (q_t[0], kv_t[0])
179
+
180
+
181
+ def get_default_backward_tile_shapes(
182
+ input_tensor: Tensor,
183
+ ) -> CutlassBlackwellFnaBackwardConfigType:
184
+ all_configs = get_all_backward_configs(input_tensor)
185
+
186
+ if len(all_configs) < 1:
187
+ device_cc = get_device_cc(input_tensor.device)
188
+ raise ValueError(
189
+ "No configs exist for this use case; Blackwell FMHA/FNA does not support it: "
190
+ f"{input_tensor.shape=}, {input_tensor.dtype=}, {device_cc=}."
191
+ )
192
+
193
+ return all_configs[0]
194
+
195
+
196
+ def get_default_backward_tile_sizes(
197
+ input_tensor: Tensor,
198
+ ) -> CutlassBlackwellFmhaBackwardConfigType:
199
+ if input_tensor.dim() != 4:
200
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
201
+
202
+ q_t, kv_t = get_default_backward_tile_shapes(input_tensor)
203
+ assert len(q_t) == len(kv_t) == 1
204
+
205
+ return (q_t[0], kv_t[0])
206
+
207
+
208
+ def check_cutlass_blackwell_fna_forward_config(
209
+ input_tensor: Tensor,
210
+ q_tile_shape: Optional[DimensionType] = None,
211
+ kv_tile_shape: Optional[DimensionType] = None,
212
+ ) -> CutlassBlackwellFnaForwardConfigType:
213
+ assert input_tensor.dim() in [4, 5, 6]
214
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
215
+
216
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
217
+ raise ValueError(
218
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
219
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
220
+ )
221
+
222
+ if q_tile_shape is None and kv_tile_shape is None:
223
+ return get_default_forward_tile_shapes(input_tensor=input_tensor)
224
+
225
+ q_tile_shape = check_tile_shape(q_tile_shape)
226
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
227
+
228
+ tile_shapes = get_all_forward_configs(input_tensor=input_tensor)
229
+
230
+ for q_t, kv_t in tile_shapes:
231
+ if q_t == q_tile_shape and kv_t == kv_tile_shape:
232
+ return (q_t, kv_t) # type: ignore
233
+
234
+ # Fail and make suggestions
235
+ MAX_EXAMPLES = 3
236
+ examples = ""
237
+ for i, (q_t, kv_t) in enumerate(tile_shapes):
238
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}"
239
+ if i > MAX_EXAMPLES:
240
+ break
241
+
242
+ raise ValueError(
243
+ f"Invalid configuration for CUTLASS Blackwell FNA-{na_dim}D. "
244
+ f"Q tile shape {q_tile_shape} and KV tile shape {kv_tile_shape} "
245
+ f"are not among the {len(tile_shapes)} configurations implementable "
246
+ f"with CUTLASS Blackwell FNA. "
247
+ "Try selecting a combination from: \n"
248
+ " natten.get_configs_for_blackwell_fna(q, k, v)"
249
+ "\n"
250
+ "Here's a few examples of available combinations for your use case:\n"
251
+ f"{examples}"
252
+ )
253
+
254
+
255
+ def check_cutlass_blackwell_fmha_forward_config(
256
+ input_tensor: Tensor,
257
+ q_tile_size: Optional[int] = None,
258
+ kv_tile_size: Optional[int] = None,
259
+ ) -> CutlassBlackwellFmhaForwardConfigType:
260
+ assert input_tensor.dim() == 4
261
+
262
+ if (q_tile_size is None) ^ (kv_tile_size is None):
263
+ raise ValueError(
264
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
265
+ f"Got {q_tile_size=}, {kv_tile_size=}."
266
+ )
267
+
268
+ if q_tile_size is None and kv_tile_size is None:
269
+ q_tile_size, kv_tile_size = get_default_forward_tile_sizes(
270
+ input_tensor=input_tensor
271
+ )
272
+ return (q_tile_size, kv_tile_size)
273
+
274
+ tile_sizes = get_all_fmha_forward_configs(input_tensor=input_tensor)
275
+
276
+ for q_t, kv_t in tile_sizes:
277
+ if q_t == q_tile_size and kv_t == kv_tile_size:
278
+ return (q_t, kv_t)
279
+
280
+ # Fail and make suggestions
281
+ MAX_EXAMPLES = 3
282
+ examples = ""
283
+ for i, (q_t, kv_t) in enumerate(tile_sizes):
284
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}"
285
+ if i > MAX_EXAMPLES:
286
+ break
287
+
288
+ raise ValueError(
289
+ f"Invalid configuration for CUTLASS Blackwell FMHA. "
290
+ f"Q tile size {q_tile_size} and KV tile size {kv_tile_size} "
291
+ f"are not among the {len(tile_sizes)} configurations implementable "
292
+ f"with CUTLASS Blackwell FMHA. "
293
+ "Try selecting a combination from: \n"
294
+ " natten.get_configs_for_blackwell_fmha(q, k, v)"
295
+ "\n"
296
+ "Here's a few examples of available combinations for your use case:\n"
297
+ f"{examples}"
298
+ )
299
+
300
+
301
+ def check_cutlass_blackwell_fna_backward_config(
302
+ input_tensor: Tensor,
303
+ q_tile_shape: Optional[DimensionType] = None,
304
+ kv_tile_shape: Optional[DimensionType] = None,
305
+ ) -> CutlassBlackwellFnaBackwardConfigType:
306
+ assert input_tensor.dim() in [4, 5, 6]
307
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
308
+
309
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
310
+ raise ValueError(
311
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
312
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
313
+ )
314
+
315
+ if q_tile_shape is None and kv_tile_shape is None:
316
+ return get_default_backward_tile_shapes(input_tensor=input_tensor)
317
+
318
+ q_tile_shape = check_tile_shape(q_tile_shape)
319
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
320
+
321
+ tile_shapes = get_all_backward_configs(input_tensor=input_tensor)
322
+
323
+ for q_t, kv_t in tile_shapes:
324
+ if q_t == q_tile_shape and kv_t == kv_tile_shape:
325
+ return (q_t, kv_t) # type: ignore
326
+
327
+ # Fail and make suggestions
328
+ MAX_EXAMPLES = 3
329
+ examples = ""
330
+ for i, (q_t, kv_t) in enumerate(tile_shapes):
331
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}"
332
+ if i > MAX_EXAMPLES:
333
+ break
334
+
335
+ raise ValueError(
336
+ f"Invalid configuration for CUTLASS Blackwell FNA-{na_dim}D Backward. "
337
+ f"Q tile shape {q_tile_shape} and KV tile shape {kv_tile_shape} "
338
+ f"are not among the {len(tile_shapes)} configurations implementable "
339
+ f"with CUTLASS Blackwell FNA Backward. "
340
+ "Try selecting a combination from: \n"
341
+ " natten.get_bwd_configs_for_blackwell_fna(q, k, v)"
342
+ "\n"
343
+ "Here's a few examples of available combinations for your use case:\n"
344
+ f"{examples}"
345
+ )
346
+
347
+
348
+ def check_cutlass_blackwell_fmha_backward_config(
349
+ input_tensor: Tensor,
350
+ q_tile_size: Optional[int] = None,
351
+ kv_tile_size: Optional[int] = None,
352
+ ) -> CutlassBlackwellFmhaBackwardConfigType:
353
+ assert input_tensor.dim() == 4
354
+
355
+ if (q_tile_size is None) ^ (kv_tile_size is None):
356
+ raise ValueError(
357
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
358
+ f"Got {q_tile_size=}, {kv_tile_size=}."
359
+ )
360
+
361
+ if q_tile_size is None and kv_tile_size is None:
362
+ q_tile_size, kv_tile_size = get_default_backward_tile_sizes(
363
+ input_tensor=input_tensor
364
+ )
365
+ return (q_tile_size, kv_tile_size)
366
+
367
+ tile_sizes = get_all_fmha_backward_configs(input_tensor=input_tensor)
368
+
369
+ for q_t, kv_t in tile_sizes:
370
+ if q_t == q_tile_size and kv_t == kv_tile_size:
371
+ return (q_t, kv_t)
372
+
373
+ # Fail and make suggestions
374
+ MAX_EXAMPLES = 3
375
+ examples = ""
376
+ for i, (q_t, kv_t) in enumerate(tile_sizes):
377
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}"
378
+ if i > MAX_EXAMPLES:
379
+ break
380
+
381
+ raise ValueError(
382
+ f"Invalid configuration for CUTLASS Blackwell FMHA Backward. "
383
+ f"Q tile size {q_tile_size} and KV tile size {kv_tile_size} "
384
+ f"are not among the {len(tile_sizes)} configurations implementable "
385
+ f"with CUTLASS Blackwell FMHA Backward. "
386
+ "Try selecting a combination from: \n"
387
+ " natten.get_bwd_configs_for_blackwell_fmha(q, k, v)"
388
+ "\n"
389
+ "Here's a few examples of available combinations for your use case:\n"
390
+ f"{examples}"
391
+ )
build/torch210-cu126-aarch64-linux/backends/configs/cutlass_hopper/__init__.py ADDED
@@ -0,0 +1,522 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import List, Optional
25
+
26
+ import torch # noqa: F401
27
+ from torch import Tensor
28
+
29
+ from ...._types import (
30
+ CutlassHopperFmhaBackwardConfigType,
31
+ CutlassHopperFmhaForwardConfigType,
32
+ CutlassHopperFnaBackwardConfigType,
33
+ CutlassHopperFnaForwardConfigType,
34
+ DimensionType,
35
+ KernelSchedule,
36
+ )
37
+ from ....utils.checks import check_tile_shape
38
+ from ....utils.device import get_device_cc
39
+
40
+ DTYPE_TO_BITS = {
41
+ torch.float16: 16,
42
+ torch.bfloat16: 16,
43
+ }
44
+
45
+ # TODO: notes
46
+
47
+ HOPPER_FORWARD_CONFIGS = {
48
+ 1: {
49
+ 16: {
50
+ 32: [
51
+ (((64,), (128,)), KernelSchedule.NonPersistent),
52
+ ],
53
+ 64: [
54
+ (((64,), (128,)), KernelSchedule.NonPersistent),
55
+ ],
56
+ 128: [
57
+ (((128,), (128,)), KernelSchedule.WarpSpecializedCooperative),
58
+ (((128,), (128,)), KernelSchedule.WarpSpecializedPingpong),
59
+ ],
60
+ 256: [
61
+ (((128,), (64,)), KernelSchedule.WarpSpecializedCooperative),
62
+ ],
63
+ },
64
+ },
65
+ 2: {
66
+ 16: {
67
+ 32: [
68
+ (((8, 8), (16, 8)), KernelSchedule.NonPersistent),
69
+ (((8, 8), (8, 16)), KernelSchedule.NonPersistent),
70
+ ],
71
+ 64: [
72
+ (((8, 8), (16, 8)), KernelSchedule.NonPersistent),
73
+ (((8, 8), (8, 16)), KernelSchedule.NonPersistent),
74
+ ],
75
+ 128: [
76
+ (((16, 8), (16, 8)), KernelSchedule.WarpSpecializedCooperative),
77
+ (((16, 8), (16, 8)), KernelSchedule.WarpSpecializedPingpong),
78
+ ],
79
+ 256: [
80
+ (((16, 8), (8, 8)), KernelSchedule.WarpSpecializedCooperative),
81
+ (((8, 16), (8, 8)), KernelSchedule.WarpSpecializedCooperative),
82
+ ],
83
+ },
84
+ },
85
+ 3: {
86
+ 16: {
87
+ 32: [
88
+ (((4, 4, 4), (4, 4, 8)), KernelSchedule.NonPersistent),
89
+ (((4, 4, 4), (2, 8, 8)), KernelSchedule.NonPersistent),
90
+ ],
91
+ 64: [
92
+ (((4, 4, 4), (4, 4, 8)), KernelSchedule.NonPersistent),
93
+ (((4, 4, 4), (2, 8, 8)), KernelSchedule.NonPersistent),
94
+ ],
95
+ 128: [
96
+ (((4, 4, 8), (4, 4, 8)), KernelSchedule.WarpSpecializedCooperative),
97
+ (((4, 4, 8), (4, 4, 8)), KernelSchedule.WarpSpecializedPingpong),
98
+ (((2, 8, 8), (2, 8, 8)), KernelSchedule.WarpSpecializedCooperative),
99
+ (((2, 8, 8), (2, 8, 8)), KernelSchedule.WarpSpecializedPingpong),
100
+ ],
101
+ 256: [
102
+ (((4, 4, 8), (4, 4, 4)), KernelSchedule.WarpSpecializedCooperative),
103
+ (((2, 8, 8), (4, 4, 4)), KernelSchedule.WarpSpecializedCooperative),
104
+ ],
105
+ },
106
+ },
107
+ }
108
+
109
+ HOPPER_BACKWARD_CONFIGS = {
110
+ 1: {
111
+ 16: {
112
+ 32: [
113
+ ((64,), (128,)),
114
+ ((128,), (128,)),
115
+ ],
116
+ 64: [
117
+ ((64,), (128,)),
118
+ ((128,), (128,)),
119
+ ],
120
+ 128: [
121
+ ((64,), (128,)),
122
+ ],
123
+ },
124
+ },
125
+ 2: {
126
+ 16: {
127
+ 32: [
128
+ ((8, 8), (16, 8)),
129
+ ((8, 8), (8, 16)),
130
+ ((16, 8), (16, 8)),
131
+ ((16, 8), (8, 16)),
132
+ ],
133
+ 64: [
134
+ ((8, 8), (16, 8)),
135
+ ((8, 8), (8, 16)),
136
+ ((16, 8), (16, 8)),
137
+ ((16, 8), (8, 16)),
138
+ ],
139
+ 128: [
140
+ ((8, 8), (16, 8)),
141
+ ((8, 8), (8, 16)),
142
+ ],
143
+ },
144
+ },
145
+ 3: {
146
+ 16: {
147
+ 32: [
148
+ ((4, 4, 4), (4, 4, 8)),
149
+ ((4, 4, 4), (2, 8, 8)),
150
+ ((4, 4, 8), (4, 4, 8)),
151
+ ((4, 4, 8), (2, 8, 8)),
152
+ ],
153
+ 64: [
154
+ ((4, 4, 4), (4, 4, 8)),
155
+ ((4, 4, 4), (2, 8, 8)),
156
+ ((4, 4, 8), (4, 4, 8)),
157
+ ((4, 4, 8), (2, 8, 8)),
158
+ ],
159
+ 128: [
160
+ ((4, 4, 4), (4, 4, 8)),
161
+ ((4, 4, 4), (2, 8, 8)),
162
+ ((2, 4, 8), (2, 8, 8)),
163
+ ((1, 8, 8), (2, 8, 8)),
164
+ ],
165
+ },
166
+ },
167
+ }
168
+
169
+
170
+ def get_all_forward_configs(
171
+ input_tensor: Tensor,
172
+ ) -> List[CutlassHopperFnaForwardConfigType]:
173
+ assert input_tensor.dim() in [4, 5, 6]
174
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
175
+
176
+ dtype = input_tensor.dtype
177
+ dtype_bits = DTYPE_TO_BITS[dtype]
178
+
179
+ head_dim = input_tensor.shape[-1]
180
+
181
+ # if dtype not in [torch.float16, torch.bfloat16]:
182
+ if dtype_bits not in HOPPER_FORWARD_CONFIGS[na_dim]: # type: ignore
183
+ return []
184
+
185
+ # if head_dim not in [32, 64, 128, 256]:
186
+ if head_dim not in HOPPER_FORWARD_CONFIGS[na_dim][dtype_bits]: # type: ignore
187
+ return []
188
+
189
+ device_cc = get_device_cc(input_tensor.device)
190
+ if device_cc != 90:
191
+ return []
192
+
193
+ return HOPPER_FORWARD_CONFIGS[na_dim][dtype_bits][head_dim] # type: ignore
194
+
195
+
196
+ def get_all_backward_configs(
197
+ input_tensor: Tensor,
198
+ ) -> List[CutlassHopperFnaBackwardConfigType]:
199
+ assert input_tensor.dim() in [4, 5, 6]
200
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
201
+
202
+ dtype = input_tensor.dtype
203
+ dtype_bits = DTYPE_TO_BITS[dtype]
204
+
205
+ head_dim = input_tensor.shape[-1]
206
+
207
+ # if dtype not in [torch.float16, torch.bfloat16]:
208
+ if dtype_bits not in HOPPER_BACKWARD_CONFIGS[na_dim]: # type: ignore
209
+ return []
210
+
211
+ # if head_dim not in [32, 64, 128, 256]:
212
+ if head_dim not in HOPPER_BACKWARD_CONFIGS[na_dim][dtype_bits]: # type: ignore
213
+ return []
214
+
215
+ device_cc = get_device_cc(input_tensor.device)
216
+ if device_cc != 90:
217
+ return []
218
+
219
+ return HOPPER_BACKWARD_CONFIGS[na_dim][dtype_bits][head_dim] # type: ignore
220
+
221
+
222
+ # For FMHA
223
+ def get_all_fmha_forward_configs(
224
+ input_tensor: Tensor,
225
+ ) -> List[CutlassHopperFmhaForwardConfigType]:
226
+ if input_tensor.dim() != 4:
227
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
228
+
229
+ configs_multi_dim = get_all_forward_configs(input_tensor)
230
+ assert all(len(q_t) == len(kv_t) == 1 for (q_t, kv_t), _ in configs_multi_dim)
231
+
232
+ configs_fmha = [
233
+ ((q_t[0], kv_t[0]), sched) for (q_t, kv_t), sched in configs_multi_dim
234
+ ]
235
+
236
+ return configs_fmha
237
+
238
+
239
+ def get_all_fmha_backward_configs(
240
+ input_tensor: Tensor,
241
+ ) -> List[CutlassHopperFmhaBackwardConfigType]:
242
+ if input_tensor.dim() != 4:
243
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
244
+
245
+ configs_multi_dim = get_all_backward_configs(input_tensor)
246
+ assert all(len(q_t) == len(kv_t) == 1 for q_t, kv_t in configs_multi_dim)
247
+
248
+ configs_fmha = [(q_t[0], kv_t[0]) for q_t, kv_t in configs_multi_dim]
249
+
250
+ return configs_fmha
251
+
252
+
253
+ def get_default_forward_config(
254
+ input_tensor: Tensor,
255
+ ) -> CutlassHopperFnaForwardConfigType:
256
+ all_configs = get_all_forward_configs(input_tensor)
257
+
258
+ if len(all_configs) < 1:
259
+ device_cc = get_device_cc(input_tensor.device)
260
+ raise ValueError(
261
+ "No configs exist for this use case; Hopper FMHA/FNA does not support it: "
262
+ f"{input_tensor.shape=}, {input_tensor.dtype=}, {device_cc=}."
263
+ )
264
+
265
+ return all_configs[0]
266
+
267
+
268
+ def get_default_backward_config(
269
+ input_tensor: Tensor,
270
+ ) -> CutlassHopperFnaBackwardConfigType:
271
+ all_configs = get_all_backward_configs(input_tensor)
272
+
273
+ if len(all_configs) < 1:
274
+ device_cc = get_device_cc(input_tensor.device)
275
+ raise ValueError(
276
+ "No configs exist for this use case; Hopper FMHA/FNA does not support it: "
277
+ f"{input_tensor.shape=}, {input_tensor.dtype=}, {device_cc=}."
278
+ )
279
+
280
+ return all_configs[0]
281
+
282
+
283
+ def get_default_fmha_forward_config(
284
+ input_tensor: Tensor,
285
+ ) -> CutlassHopperFmhaForwardConfigType:
286
+ if input_tensor.dim() != 4:
287
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
288
+
289
+ (q_t, kv_t), sched = get_default_forward_config(input_tensor)
290
+ assert len(q_t) == len(kv_t) == 1
291
+
292
+ return (q_t[0], kv_t[0]), sched
293
+
294
+
295
+ def get_default_fmha_backward_config(
296
+ input_tensor: Tensor,
297
+ ) -> CutlassHopperFmhaBackwardConfigType:
298
+ if input_tensor.dim() != 4:
299
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
300
+
301
+ q_t, kv_t = get_default_backward_config(input_tensor)
302
+ assert len(q_t) == len(kv_t) == 1
303
+
304
+ return q_t[0], kv_t[0]
305
+
306
+
307
+ def check_cutlass_hopper_fna_forward_config(
308
+ input_tensor: Tensor,
309
+ q_tile_shape: Optional[DimensionType] = None,
310
+ kv_tile_shape: Optional[DimensionType] = None,
311
+ kernel_schedule: Optional[KernelSchedule] = None,
312
+ ) -> CutlassHopperFnaForwardConfigType:
313
+ assert input_tensor.dim() in [4, 5, 6]
314
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
315
+
316
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
317
+ raise ValueError(
318
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
319
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
320
+ )
321
+
322
+ (default_q_tile_shape, default_kv_tile_shape), default_sched = (
323
+ get_default_forward_config(input_tensor=input_tensor)
324
+ )
325
+ if q_tile_shape is None and kv_tile_shape is None and kernel_schedule is None:
326
+ return (default_q_tile_shape, default_kv_tile_shape), default_sched # type: ignore[return-value]
327
+
328
+ elif q_tile_shape is None and kv_tile_shape is None:
329
+ q_tile_shape = default_q_tile_shape
330
+ kv_tile_shape = default_kv_tile_shape
331
+
332
+ q_tile_shape = check_tile_shape(q_tile_shape)
333
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
334
+
335
+ configs = get_all_forward_configs(input_tensor=input_tensor)
336
+
337
+ for (q_t, kv_t), sched in configs:
338
+ if (
339
+ q_t == q_tile_shape
340
+ and kv_t == kv_tile_shape
341
+ and (kernel_schedule is None or sched == kernel_schedule)
342
+ ):
343
+ return (q_t, kv_t), sched # type: ignore
344
+
345
+ # Fail and make suggestions
346
+ MAX_EXAMPLES = 3
347
+ examples = ""
348
+ for i, ((q_t, kv_t), sched) in enumerate(configs):
349
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}, schedule={sched}"
350
+ if i > MAX_EXAMPLES:
351
+ break
352
+
353
+ raise ValueError(
354
+ f"Invalid configuration for CUTLASS Hopper FNA-{na_dim}D. "
355
+ f"Q tile shape {q_tile_shape}, KV tile shape {kv_tile_shape}, and schedule {kernel_schedule} "
356
+ f"are not among the {len(configs)} configurations implementable "
357
+ f"with CUTLASS Hopper FNA. "
358
+ "Try selecting a combination from: \n"
359
+ " natten.get_configs_for_hopper_fna(q, k, v)"
360
+ "\n"
361
+ "Here's a few examples of available combinations for your use case:\n"
362
+ f"{examples}"
363
+ )
364
+
365
+
366
+ def check_cutlass_hopper_fna_backward_config(
367
+ input_tensor: Tensor,
368
+ q_tile_shape: Optional[DimensionType] = None,
369
+ kv_tile_shape: Optional[DimensionType] = None,
370
+ ) -> CutlassHopperFnaBackwardConfigType:
371
+ assert input_tensor.dim() in [4, 5, 6]
372
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
373
+
374
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
375
+ raise ValueError(
376
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
377
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
378
+ )
379
+
380
+ default_q_tile_shape, default_kv_tile_shape = get_default_backward_config(
381
+ input_tensor=input_tensor
382
+ )
383
+ if q_tile_shape is None and kv_tile_shape is None:
384
+ return default_q_tile_shape, default_kv_tile_shape # type: ignore[return-value]
385
+
386
+ elif q_tile_shape is None and kv_tile_shape is None:
387
+ q_tile_shape = default_q_tile_shape
388
+ kv_tile_shape = default_kv_tile_shape
389
+
390
+ q_tile_shape = check_tile_shape(q_tile_shape)
391
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
392
+
393
+ configs = get_all_backward_configs(input_tensor=input_tensor)
394
+
395
+ for q_t, kv_t in configs:
396
+ if q_t == q_tile_shape and kv_t == kv_tile_shape:
397
+ return q_t, kv_t # type: ignore
398
+
399
+ # Fail and make suggestions
400
+ MAX_EXAMPLES = 3
401
+ examples = ""
402
+ for i, (q_t, kv_t) in enumerate(configs):
403
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}"
404
+ if i > MAX_EXAMPLES:
405
+ break
406
+
407
+ raise ValueError(
408
+ f"Invalid configuration for CUTLASS Hopper FNA-{na_dim}D Backward. "
409
+ f"Q tile shape {q_tile_shape} and KV tile shape {kv_tile_shape} "
410
+ f"are not among the {len(configs)} configurations implementable "
411
+ f"with CUTLASS Hopper FNA Backward. "
412
+ "Try selecting a combination from: \n"
413
+ " natten.get_bwd_configs_for_hopper_fna(q, k, v)"
414
+ "\n"
415
+ "Here's a few examples of available combinations for your use case:\n"
416
+ f"{examples}"
417
+ )
418
+
419
+
420
+ def check_cutlass_hopper_fmha_forward_config(
421
+ input_tensor: Tensor,
422
+ q_tile_size: Optional[int] = None,
423
+ kv_tile_size: Optional[int] = None,
424
+ kernel_schedule: Optional[KernelSchedule] = None,
425
+ ) -> CutlassHopperFmhaForwardConfigType:
426
+ assert input_tensor.dim() == 4
427
+
428
+ if (q_tile_size is None) ^ (kv_tile_size is None):
429
+ raise ValueError(
430
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
431
+ f"Got {q_tile_size=}, {kv_tile_size=}."
432
+ )
433
+
434
+ (default_q_tile_size, default_kv_tile_size), default_sched = (
435
+ get_default_fmha_forward_config(input_tensor=input_tensor)
436
+ )
437
+ if q_tile_size is None and kv_tile_size is None and kernel_schedule is None:
438
+ return (default_q_tile_size, default_kv_tile_size), default_sched
439
+
440
+ elif q_tile_size is None and kv_tile_size is None:
441
+ q_tile_size = default_q_tile_size
442
+ kv_tile_size = default_kv_tile_size
443
+
444
+ configs = get_all_fmha_forward_configs(input_tensor=input_tensor)
445
+
446
+ for (q_t, kv_t), sched in configs:
447
+ if (
448
+ q_t == q_tile_size
449
+ and kv_t == kv_tile_size
450
+ and (kernel_schedule is None or sched == kernel_schedule)
451
+ ):
452
+ return (q_t, kv_t), sched # type: ignore
453
+
454
+ # Fail and make suggestions
455
+ MAX_EXAMPLES = 3
456
+ examples = ""
457
+ for i, ((q_t, kv_t), sched) in enumerate(configs):
458
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}, schedule={sched}"
459
+ if i > MAX_EXAMPLES:
460
+ break
461
+
462
+ raise ValueError(
463
+ f"Invalid configuration for CUTLASS Hopper FMHA. "
464
+ f"Q tile size {q_tile_size}, KV tile size {kv_tile_size}, and schedule {kernel_schedule} "
465
+ f"are not among the {len(configs)} configurations implementable "
466
+ f"with CUTLASS Hopper FMHA. "
467
+ "Try selecting a combination from: \n"
468
+ " natten.get_configs_for_hopper_fmha(q, k, v)"
469
+ "\n"
470
+ "Here's a few examples of available combinations for your use case:\n"
471
+ f"{examples}"
472
+ )
473
+
474
+
475
+ def check_cutlass_hopper_fmha_backward_config(
476
+ input_tensor: Tensor,
477
+ q_tile_size: Optional[int] = None,
478
+ kv_tile_size: Optional[int] = None,
479
+ ) -> CutlassHopperFmhaBackwardConfigType:
480
+ assert input_tensor.dim() == 4
481
+
482
+ if (q_tile_size is None) ^ (kv_tile_size is None):
483
+ raise ValueError(
484
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
485
+ f"Got {q_tile_size=}, {kv_tile_size=}."
486
+ )
487
+
488
+ default_q_tile_size, default_kv_tile_size = get_default_fmha_backward_config(
489
+ input_tensor=input_tensor
490
+ )
491
+ if q_tile_size is None and kv_tile_size is None:
492
+ return default_q_tile_size, default_kv_tile_size
493
+
494
+ elif q_tile_size is None and kv_tile_size is None:
495
+ q_tile_size = default_q_tile_size
496
+ kv_tile_size = default_kv_tile_size
497
+
498
+ configs = get_all_fmha_backward_configs(input_tensor=input_tensor)
499
+
500
+ for q_t, kv_t in configs:
501
+ if q_t == q_tile_size and kv_t == kv_tile_size:
502
+ return q_t, kv_t # type: ignore
503
+
504
+ # Fail and make suggestions
505
+ MAX_EXAMPLES = 3
506
+ examples = ""
507
+ for i, (q_t, kv_t) in enumerate(configs):
508
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}"
509
+ if i > MAX_EXAMPLES:
510
+ break
511
+
512
+ raise ValueError(
513
+ f"Invalid configuration for CUTLASS Hopper FMHA Backward. "
514
+ f"Q tile size {q_tile_size} and KV tile size {kv_tile_size} "
515
+ f"are not among the {len(configs)} configurations implementable "
516
+ f"with CUTLASS Hopper FMHA Backward. "
517
+ "Try selecting a combination from: \n"
518
+ " natten.get_bwd_configs_for_hopper_fmha(q, k, v)"
519
+ "\n"
520
+ "Here's a few examples of available combinations for your use case:\n"
521
+ f"{examples}"
522
+ )
build/torch210-cu126-aarch64-linux/backends/configs/flex/__init__.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import List, Optional
25
+
26
+ import torch # noqa: F401
27
+ from torch import Tensor
28
+
29
+ from ...._types import (
30
+ DimensionType,
31
+ FlexFmhaForwardConfigType,
32
+ FlexFnaForwardConfigType,
33
+ )
34
+ from ....utils.checks import check_tile_shape
35
+ from ....utils.device import get_device_cc
36
+
37
+ # TODO: add more tile sizes/shapes
38
+ # TODO: add backprop tile sizes/shapes
39
+ # Only doing 64 x 64 for now, since it's the one that successfully compiles across devices and
40
+ # use cases without running into compile errors (i.e. shmem over-subscription)
41
+ # Once Flex with compilation actually starts working as expected and is out of prototype, we can
42
+ # add in more tile sizes/shapes and condition them on arch / use case, like we do for CUTLASS FNA.
43
+
44
+ FLEX_FORWARD_TILE_SHAPES = {
45
+ 1: [
46
+ # ((128, ), (128, )),
47
+ ((64,), (64,)),
48
+ ],
49
+ 2: [
50
+ # ((8, 16), (8, 16)),
51
+ ((8, 8), (8, 8)),
52
+ ((4, 16), (4, 16)),
53
+ ((4, 16), (8, 8)),
54
+ ],
55
+ 3: [
56
+ # ((4, 4, 8), (4, 4, 8)),
57
+ ((4, 4, 4), (4, 4, 4)),
58
+ ((2, 4, 8), (2, 4, 8)),
59
+ ((2, 4, 8), (4, 4, 4)),
60
+ ],
61
+ }
62
+
63
+
64
+ def _get_default_tile_shapes_forward(
65
+ na_dim: int,
66
+ ) -> FlexFnaForwardConfigType:
67
+ assert na_dim in [1, 2, 3]
68
+
69
+ if na_dim == 1:
70
+ return ((64,), (64,))
71
+ if na_dim == 2:
72
+ return ((8, 8), (8, 8))
73
+ if na_dim == 3:
74
+ return ((4, 4, 4), (4, 4, 4))
75
+
76
+ raise NotImplementedError()
77
+
78
+
79
+ def get_all_tile_shapes_forward(
80
+ input_tensor: Tensor,
81
+ ) -> List[FlexFnaForwardConfigType]:
82
+ assert input_tensor.dim() in [4, 5, 6]
83
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
84
+
85
+ return FLEX_FORWARD_TILE_SHAPES[na_dim] # type: ignore
86
+
87
+
88
+ # For FMHA
89
+ def get_all_tile_sizes_forward(input_tensor: Tensor) -> List[FlexFmhaForwardConfigType]:
90
+ if input_tensor.dim() != 4:
91
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
92
+
93
+ tile_shapes = get_all_tile_shapes_forward(input_tensor)
94
+ assert all(len(q_t) == len(kv_t) == 1 for q_t, kv_t in tile_shapes)
95
+
96
+ tile_sizes = [(q_t[0], kv_t[0]) for q_t, kv_t in tile_shapes]
97
+
98
+ return tile_sizes
99
+
100
+
101
+ def get_default_forward_tile_shapes(input_tensor: Tensor) -> FlexFnaForwardConfigType:
102
+ assert input_tensor.dim() in [4, 5, 6]
103
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
104
+
105
+ return _get_default_tile_shapes_forward(na_dim)
106
+
107
+
108
+ def get_default_forward_tile_sizes(input_tensor: Tensor) -> FlexFmhaForwardConfigType:
109
+ if input_tensor.dim() != 4:
110
+ raise ValueError("Only 4-D tensors are supported in FMHA.")
111
+
112
+ q_t, kv_t = get_default_forward_tile_shapes(input_tensor)
113
+ assert len(q_t) == len(kv_t) == 1
114
+
115
+ return (q_t[0], kv_t[0])
116
+
117
+
118
+ def check_flex_fna_forward_config(
119
+ input_tensor: Tensor,
120
+ q_tile_shape: Optional[DimensionType] = None,
121
+ kv_tile_shape: Optional[DimensionType] = None,
122
+ ) -> FlexFnaForwardConfigType:
123
+ assert input_tensor.dim() in [4, 5, 6]
124
+ na_dim = input_tensor.dim() - 3 # batch, heads, head_dim
125
+
126
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
127
+ raise ValueError(
128
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
129
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
130
+ )
131
+
132
+ if q_tile_shape is None and kv_tile_shape is None:
133
+ return get_default_forward_tile_shapes(input_tensor=input_tensor)
134
+
135
+ q_tile_shape = check_tile_shape(q_tile_shape)
136
+ kv_tile_shape = check_tile_shape(kv_tile_shape)
137
+
138
+ tile_shapes = get_all_tile_shapes_forward(input_tensor=input_tensor)
139
+
140
+ for q_t, kv_t in tile_shapes:
141
+ if q_t == q_tile_shape and kv_t == kv_tile_shape:
142
+ return (q_t, kv_t) # type: ignore
143
+
144
+ # Fail and make suggestions
145
+ device_cc = get_device_cc(input_tensor.device)
146
+ MAX_EXAMPLES = 3
147
+ examples = ""
148
+ for i, (q_t, kv_t) in enumerate(tile_shapes):
149
+ examples += f"\n q_tile_shape={q_t}, kv_tile_shape={kv_t}"
150
+ if i > MAX_EXAMPLES:
151
+ break
152
+
153
+ raise ValueError(
154
+ f"Invalid configuration for Flex FNA-{na_dim}D. "
155
+ f"Q tile shape {q_tile_shape} and KV tile shape {kv_tile_shape} "
156
+ f"are not among the {len(tile_shapes)} configurations implementable "
157
+ f"with Flex FNA for SM{device_cc}. "
158
+ "Try selecting a combination from: \n"
159
+ " natten.get_configs_for_flex_fna(q, k, v)"
160
+ "\n"
161
+ "Here's a few examples of available combinations for your use case:\n"
162
+ f"{examples}"
163
+ )
164
+
165
+
166
+ def check_flex_fmha_forward_config(
167
+ input_tensor: Tensor,
168
+ q_tile_size: Optional[int] = None,
169
+ kv_tile_size: Optional[int] = None,
170
+ ) -> FlexFmhaForwardConfigType:
171
+ assert input_tensor.dim() == 4
172
+
173
+ if (q_tile_size is None) ^ (kv_tile_size is None):
174
+ raise ValueError(
175
+ "Please specify both q_tile_size and kv_tile_size, or neither one. "
176
+ f"Got {q_tile_size=}, {kv_tile_size=}."
177
+ )
178
+
179
+ if q_tile_size is None and kv_tile_size is None:
180
+ q_tile_size, kv_tile_size = get_default_forward_tile_sizes(
181
+ input_tensor=input_tensor
182
+ )
183
+ return (q_tile_size, kv_tile_size)
184
+
185
+ tile_sizes = get_all_tile_sizes_forward(input_tensor=input_tensor)
186
+
187
+ for q_t, kv_t in tile_sizes:
188
+ if q_t == q_tile_size and kv_t == kv_tile_size:
189
+ return (q_t, kv_t)
190
+
191
+ # Fail and make suggestions
192
+ device_cc = get_device_cc(input_tensor.device)
193
+ MAX_EXAMPLES = 3
194
+ examples = ""
195
+ for i, (q_t, kv_t) in enumerate(tile_sizes):
196
+ examples += f"\n q_tile_size={q_t}, kv_tile_size={kv_t}"
197
+ if i > MAX_EXAMPLES:
198
+ break
199
+
200
+ raise ValueError(
201
+ f"Invalid configuration for Flex FMHA. "
202
+ f"Q tile size {q_tile_size} and KV tile size {kv_tile_size} "
203
+ f"are not among the {len(tile_sizes)} configurations implementable "
204
+ f"with Flex FMHA for SM{device_cc}. "
205
+ "Try selecting a combination from: \n"
206
+ " natten.get_configs_for_flex_fmha(q, k, v)\n"
207
+ "\n"
208
+ "Here's a few examples of available combinations for your use case:\n"
209
+ f"{examples}"
210
+ )
build/torch210-cu126-aarch64-linux/backends/flex.py ADDED
@@ -0,0 +1,799 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import functools
25
+ import math
26
+ import time
27
+ from typing import Callable, Optional, Tuple, Union
28
+
29
+ import torch
30
+ from torch import BoolTensor, IntTensor, Tensor
31
+ from torch.nn.attention.flex_attention import (
32
+ BlockMask,
33
+ create_block_mask,
34
+ flex_attention,
35
+ )
36
+
37
+ from ..backends.configs.checks import ( # noqa: F401
38
+ _FLEX_COMPILE_SUPPORTED,
39
+ _FLEX_SUPPORTED,
40
+ can_run_flex_attention,
41
+ )
42
+ from ..backends.configs.flex import (
43
+ check_flex_fmha_forward_config,
44
+ check_flex_fna_forward_config,
45
+ )
46
+ from ..token_permute import token_permute_operation, token_unpermute_operation
47
+ from .._types import (
48
+ CausalArg1DTypeOrDed,
49
+ CausalArg2DTypeOrDed,
50
+ CausalArg3DTypeOrDed,
51
+ CausalArgType,
52
+ CausalArgTypeOrDed,
53
+ Dimension1DType,
54
+ Dimension1DTypeOrDed,
55
+ Dimension2DType,
56
+ Dimension2DTypeOrDed,
57
+ Dimension3DType,
58
+ Dimension3DTypeOrDed,
59
+ DimensionType,
60
+ DimensionTypeOrDed,
61
+ )
62
+ from ..utils import log
63
+ from ..utils.checks import (
64
+ check_all_args,
65
+ check_args_against_input,
66
+ check_input_size_arg,
67
+ fmha_tensor_checks,
68
+ na_tensor_checks,
69
+ varlen_tensor_checks,
70
+ )
71
+ from ..utils.environment import is_torch_compiling
72
+
73
+ logger = log.get_logger(__name__)
74
+
75
+
76
+ def get_flex_attention_fn(
77
+ torch_compile: bool, torch_compile_args: Optional[dict] = None
78
+ ) -> Callable:
79
+ if not torch_compile:
80
+ return flex_attention
81
+
82
+ additional_args = torch_compile_args or {}
83
+ additional_args["dynamic"] = False
84
+
85
+ return torch.compile(flex_attention, **additional_args)
86
+
87
+
88
+ def _run_flex_attn(
89
+ q: Tensor,
90
+ k: Tensor,
91
+ v: Tensor,
92
+ block_mask: BlockMask,
93
+ scale: float,
94
+ torch_compile: bool,
95
+ q_tile_size: Optional[int] = None,
96
+ kv_tile_size: Optional[int] = None,
97
+ torch_compile_args: Optional[dict] = None,
98
+ ) -> Tuple[Tensor, Tensor]:
99
+
100
+ # We may need to override the default flex config.
101
+ # Default ones are not guaranteed to work out of the box across architectures.
102
+ # Some oversubscribe shmem even on the B200!
103
+ torch_compile_args = {}
104
+
105
+ # Disable flex decoding path
106
+ kernel_options = {
107
+ "FORCE_USE_FLEX_ATTENTION": True,
108
+ }
109
+
110
+ if q_tile_size is not None and torch_compile:
111
+ kv_tile_size = kv_tile_size or q_tile_size
112
+
113
+ # Have to auto-tune, otherwise torch will only allow the default config.
114
+ torch_compile_args["mode"] = "max-autotune-no-cudagraphs"
115
+
116
+ kernel_options["SPARSE_Q_BLOCK_SIZE"] = q_tile_size # type: ignore[assignment]
117
+ kernel_options["SPARSE_KV_BLOCK_SIZE"] = kv_tile_size # type: ignore[assignment]
118
+ kernel_options["BLOCK_M"] = q_tile_size # type: ignore[assignment]
119
+ kernel_options["BLOCK_N"] = kv_tile_size # type: ignore[assignment]
120
+
121
+ flex_fn = get_flex_attention_fn(
122
+ torch_compile=torch_compile, torch_compile_args=torch_compile_args
123
+ )
124
+
125
+ # tensors are BHSD here
126
+ is_gqa = q.shape[1] != k.shape[1]
127
+ return flex_fn(
128
+ q,
129
+ k,
130
+ v,
131
+ block_mask=block_mask,
132
+ return_lse=True,
133
+ scale=scale,
134
+ kernel_options=kernel_options,
135
+ enable_gqa=is_gqa,
136
+ )
137
+
138
+
139
+ def run_flex_attn(
140
+ q: Tensor,
141
+ k: Tensor,
142
+ v: Tensor,
143
+ block_mask: BlockMask,
144
+ scale: float,
145
+ torch_compile: bool,
146
+ torch_compile_args: Optional[dict] = None,
147
+ q_tile_size: Optional[int] = None,
148
+ kv_tile_size: Optional[int] = None,
149
+ ) -> Tuple[Tensor, Tensor]:
150
+
151
+ if q_tile_size is not None and kv_tile_size is not None:
152
+ return _run_flex_attn(
153
+ q,
154
+ k,
155
+ v,
156
+ block_mask=block_mask,
157
+ scale=scale,
158
+ torch_compile=torch_compile,
159
+ q_tile_size=q_tile_size,
160
+ kv_tile_size=kv_tile_size,
161
+ torch_compile_args=torch_compile_args,
162
+ )
163
+
164
+ # Use smallest tile size combo to try and evade shmem oversubscription
165
+ # The defaults just fail very frequently.
166
+ return _run_flex_attn(
167
+ q,
168
+ k,
169
+ v,
170
+ block_mask=block_mask,
171
+ scale=scale,
172
+ torch_compile=torch_compile,
173
+ q_tile_size=64,
174
+ kv_tile_size=64,
175
+ torch_compile_args=torch_compile_args,
176
+ )
177
+
178
+
179
+ def flex_fmha(
180
+ query: Tensor,
181
+ key: Tensor,
182
+ value: Tensor,
183
+ is_causal: bool = False,
184
+ scale: Optional[float] = None,
185
+ q_tile_size: Optional[int] = None,
186
+ kv_tile_size: Optional[int] = None,
187
+ torch_compile: bool = False,
188
+ return_lse: bool = False,
189
+ # varlen parameters
190
+ cumulative_seqlen_Q: Optional[Tensor] = None,
191
+ cumulative_seqlen_KV: Optional[Tensor] = None,
192
+ max_seqlen_Q: int = 0,
193
+ max_seqlen_KV: int = 0,
194
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
195
+
196
+ fmha_tensor_checks(
197
+ query,
198
+ key,
199
+ value,
200
+ must_match_head_dims=True,
201
+ supports_gqa_mqa=True,
202
+ backend_name="Flex FMHA",
203
+ )
204
+
205
+ (
206
+ cumulative_seqlen_Q,
207
+ cumulative_seqlen_KV,
208
+ max_seqlen_Q,
209
+ max_seqlen_KV,
210
+ ) = varlen_tensor_checks(
211
+ query=query,
212
+ key=key,
213
+ value=value,
214
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
215
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
216
+ max_seqlen_Q=max_seqlen_Q,
217
+ max_seqlen_KV=max_seqlen_KV,
218
+ )
219
+ is_varlen = cumulative_seqlen_Q is not None
220
+
221
+ assert can_run_flex_attention(
222
+ query,
223
+ key,
224
+ value,
225
+ is_causal=is_causal,
226
+ is_varlen=is_varlen,
227
+ torch_compile=torch_compile,
228
+ raise_error=True,
229
+ )
230
+
231
+ q_tile_size, kv_tile_size = check_flex_fmha_forward_config(
232
+ input_tensor=query,
233
+ q_tile_size=q_tile_size,
234
+ kv_tile_size=kv_tile_size,
235
+ )
236
+
237
+ scale = scale or query.shape[-1] ** -0.5
238
+
239
+ batch_size, seqlen_q, num_heads, head_dim = query.shape
240
+ _, seqlen_kv, num_heads_kv, head_dim_v = value.shape
241
+
242
+ # Flex and torch attention use heads first layout
243
+ query_ = query.reshape(batch_size, seqlen_q, num_heads, head_dim).transpose(1, 2)
244
+ key_ = key.reshape(batch_size, seqlen_kv, num_heads_kv, head_dim_v).transpose(1, 2)
245
+ value_ = value.reshape(batch_size, seqlen_kv, num_heads_kv, head_dim_v).transpose(
246
+ 1, 2
247
+ )
248
+
249
+ out_, lse_ = run_flex_attn(
250
+ query_,
251
+ key_,
252
+ value_,
253
+ block_mask=None, # type: ignore[arg-type]
254
+ scale=scale,
255
+ torch_compile=torch_compile,
256
+ q_tile_size=q_tile_size,
257
+ kv_tile_size=kv_tile_size,
258
+ )
259
+
260
+ out = out_.transpose(1, 2).reshape(batch_size, seqlen_q, num_heads, head_dim_v)
261
+ lse = lse_.transpose(1, 2).reshape(batch_size, seqlen_q, num_heads)
262
+
263
+ if return_lse:
264
+ return out, lse
265
+
266
+ return out
267
+
268
+
269
+ # TODO: move me elsewhere?
270
+ def idx2crd(index, shape) -> tuple:
271
+ rank = len(shape)
272
+ coord = []
273
+ residual = index
274
+ for i in range(rank - 1, -1, -1):
275
+ coord.append(residual % shape[i])
276
+ residual = residual // shape[i]
277
+
278
+ # assert residual == 0
279
+ return tuple(coord[::-1])
280
+
281
+
282
+ def get_na_flex_mask(
283
+ device: str,
284
+ na_dim: int,
285
+ qkv_shape: DimensionType,
286
+ kernel_size: DimensionType,
287
+ stride: DimensionType,
288
+ dilation: DimensionType,
289
+ is_causal: CausalArgType,
290
+ q_tile_shape: Optional[DimensionType] = None,
291
+ kv_tile_shape: Optional[DimensionType] = None,
292
+ q_shape: Optional[DimensionType] = None,
293
+ kv_shape: Optional[DimensionType] = None,
294
+ torch_compile: bool = False,
295
+ ):
296
+ num_dilation_groups = math.prod(dilation)
297
+ if not is_torch_compiling():
298
+ flex_mask_start_time = time.perf_counter()
299
+ do_token_permute = q_tile_shape is not None and kv_tile_shape is not None
300
+ if do_token_permute:
301
+ if q_tile_shape is None or kv_tile_shape is None:
302
+ raise ValueError(
303
+ "Please specify Q and KV tile shapes for multi dimensional tiling. "
304
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
305
+ )
306
+
307
+ if q_shape is None or kv_shape is None:
308
+ raise ValueError(
309
+ "Please specify q_shape and kv_shape for multi dimensional tiling."
310
+ )
311
+
312
+ if len(q_tile_shape) != na_dim or len(kv_tile_shape) != na_dim:
313
+ raise ValueError(
314
+ "Q and KV tile shapes must match the number of dimensions in the "
315
+ f"token layout ({na_dim}, got {q_tile_shape=}, {kv_tile_shape=}."
316
+ )
317
+
318
+ if any(x % t != 0 for x, t in zip(q_shape, q_tile_shape)):
319
+ raise ValueError(
320
+ "Input must be divisible by Q tile shape, but got "
321
+ f"{q_shape=}, {q_tile_shape=}."
322
+ )
323
+
324
+ if any(x % t != 0 for x, t in zip(kv_shape, kv_tile_shape)):
325
+ raise ValueError(
326
+ "Input must be divisible by KV tile shape, but got "
327
+ f"{kv_shape=}, {kv_tile_shape=}."
328
+ )
329
+
330
+ q_rest_shape = tuple(x // t for x, t in zip(q_shape, q_tile_shape))
331
+ kv_rest_shape = tuple(x // t for x, t in zip(kv_shape, kv_tile_shape))
332
+
333
+ def single_dim_tiling_mask(
334
+ b: IntTensor,
335
+ h: IntTensor,
336
+ q_idx: IntTensor,
337
+ kv_idx: IntTensor,
338
+ qkv_shape,
339
+ kernel_size,
340
+ stride,
341
+ dilation,
342
+ is_causal,
343
+ ) -> BoolTensor:
344
+
345
+ # Reconstruct global Q and KV coordinates
346
+ q_crd = idx2crd(q_idx, qkv_shape)
347
+ kv_crd = idx2crd(kv_idx, qkv_shape)
348
+
349
+ # Coordinates within dilation group
350
+ q_crd_di = tuple(x // d for x, d in zip(q_crd, dilation))
351
+ kv_crd_di = tuple(x // d for x, d in zip(kv_crd, dilation))
352
+
353
+ # Dilation group coordinates
354
+ q_dilation_group_crd = tuple(x % d for x, d in zip(q_crd, dilation))
355
+ kv_dilation_group_crd = tuple(x % d for x, d in zip(kv_crd, dilation))
356
+
357
+ # Fixup input shape according to dilation group
358
+ dilation_group_padding = tuple(
359
+ 1 - ((dg + (d - (x % d))) // d)
360
+ for dg, d, x in zip(q_dilation_group_crd, dilation, qkv_shape)
361
+ )
362
+ qkv_shape_corrected = tuple(
363
+ (x // d) + p for p, d, x in zip(dilation_group_padding, dilation, qkv_shape)
364
+ )
365
+
366
+ # Window size left and right (non-causal only)
367
+ window_size_left = tuple(w // 2 for w in kernel_size)
368
+ window_size_right = tuple(w // 2 + (w % 2 - 1) for w in kernel_size)
369
+
370
+ masks = []
371
+ for i in range(na_dim):
372
+ if is_causal[i]:
373
+ # Leader is the last (right-most) query in the stride group.
374
+ stride_group_leader = torch.min(
375
+ (q_crd_di[i] // stride[i]) * stride[i] + stride[i] - 1,
376
+ qkv_shape_corrected[i] - 1,
377
+ )
378
+
379
+ mask = (
380
+ (
381
+ q_crd_di[i] - kv_crd_di[i] >= 0
382
+ ) # window still ends at query index
383
+ & (stride_group_leader - kv_crd_di[i] < kernel_size[i])
384
+ & (q_dilation_group_crd[i] == kv_dilation_group_crd[i])
385
+ )
386
+ else:
387
+ # Leader is the center-most query in the stride group.
388
+ # If stride is even, choose the right hand side center query.
389
+ stride_group_leader = torch.min(
390
+ (q_crd_di[i] // stride[i]) * stride[i] + (stride[i] // 2),
391
+ qkv_shape_corrected[i] - 1,
392
+ )
393
+
394
+ window_center = stride_group_leader.clamp(
395
+ window_size_left[i] * torch.ones_like(qkv_shape_corrected[i]),
396
+ qkv_shape_corrected[i] - 1 - window_size_right[i],
397
+ )
398
+ w0 = window_center - kv_crd_di[i]
399
+ w1 = kv_crd_di[i] - window_center
400
+ mask = (
401
+ ((0 <= w0) & (w0 <= window_size_left[i]))
402
+ | ((0 <= w1) & (w1 <= window_size_right[i]))
403
+ ) & (q_dilation_group_crd[i] == kv_dilation_group_crd[i])
404
+
405
+ masks.append(mask)
406
+
407
+ return functools.reduce(lambda x, y: x & y, masks) # type: ignore
408
+
409
+ def multi_dim_tiling_mask(
410
+ b: IntTensor,
411
+ h: IntTensor,
412
+ q_idx: IntTensor,
413
+ kv_idx: IntTensor,
414
+ q_tile_size: int,
415
+ kv_tile_size: int,
416
+ q_tile_shape,
417
+ kv_tile_shape,
418
+ qkv_shape,
419
+ kernel_size,
420
+ stride,
421
+ dilation,
422
+ is_causal,
423
+ ) -> BoolTensor:
424
+
425
+ # Reconstruct global Q and KV coordinates
426
+ q_tile_idx = q_idx // q_tile_size
427
+ kv_tile_idx = kv_idx // kv_tile_size
428
+ q_tile_offset = q_idx % q_tile_size
429
+ kv_tile_offset = kv_idx % q_tile_size
430
+ q_tile_coord = idx2crd(q_tile_idx, q_rest_shape)
431
+ kv_tile_coord = idx2crd(kv_tile_idx, kv_rest_shape)
432
+ q_tile_offset_coord = idx2crd(q_tile_offset, q_tile_shape)
433
+ kv_tile_offset_coord = idx2crd(kv_tile_offset, kv_tile_shape)
434
+
435
+ q_crd = tuple(
436
+ tile_crd * tile_sz + tile_off
437
+ for tile_crd, tile_sz, tile_off in zip(
438
+ q_tile_coord, q_tile_shape, q_tile_offset_coord
439
+ )
440
+ )
441
+ kv_crd = tuple(
442
+ tile_crd * tile_sz + tile_off
443
+ for tile_crd, tile_sz, tile_off in zip(
444
+ kv_tile_coord, kv_tile_shape, kv_tile_offset_coord
445
+ )
446
+ )
447
+
448
+ # Dilation group coordinates
449
+ # b_actual = b // num_dilation_groups
450
+ dilation_group_idx = b % num_dilation_groups
451
+ dilation_group_crd = idx2crd(dilation_group_idx, dilation)
452
+
453
+ # Fixup input shape according to dilation group
454
+ dilation_group_padding = tuple(
455
+ 1 - ((dg + (d - (x % d))) // d)
456
+ for dg, d, x in zip(dilation_group_crd, dilation, qkv_shape)
457
+ )
458
+ qkv_shape_corrected = tuple(
459
+ (x // d) + p for p, d, x in zip(dilation_group_padding, dilation, qkv_shape)
460
+ )
461
+
462
+ # Window size left and right (non-causal only)
463
+ window_size_left = tuple(w // 2 for w in kernel_size)
464
+ window_size_right = tuple(w // 2 + (w % 2 - 1) for w in kernel_size)
465
+
466
+ masks = []
467
+ for i in range(na_dim):
468
+ if is_causal[i]:
469
+ # Leader is the last (right-most) query in the stride group.
470
+ stride_group_leader = torch.min(
471
+ (q_crd[i] // stride[i]) * stride[i] + stride[i] - 1,
472
+ qkv_shape_corrected[i] - 1,
473
+ )
474
+
475
+ mask = (
476
+ q_crd[i] - kv_crd[i] >= 0
477
+ ) & ( # window still ends at query index
478
+ stride_group_leader - kv_crd[i] < kernel_size[i]
479
+ )
480
+ else:
481
+ # Leader is the center-most query in the stride group.
482
+ # If stride is even, choose the right hand side center query.
483
+ stride_group_leader = torch.min(
484
+ (q_crd[i] // stride[i]) * stride[i] + (stride[i] // 2),
485
+ qkv_shape_corrected[i] - 1,
486
+ )
487
+
488
+ window_center = stride_group_leader.clamp(
489
+ window_size_left[i] * torch.ones_like(qkv_shape_corrected[i]),
490
+ qkv_shape_corrected[i] - 1 - window_size_right[i],
491
+ )
492
+ w0 = window_center - kv_crd[i]
493
+ w1 = kv_crd[i] - window_center
494
+
495
+ mask = ((0 <= w0) & (w0 <= window_size_left[i])) | (
496
+ (0 <= w1) & (w1 <= window_size_right[i])
497
+ )
498
+
499
+ masks.append(mask)
500
+
501
+ return functools.reduce(lambda x, y: x & y, masks) # type: ignore
502
+
503
+ mask_mod = None
504
+ seq_length_q = seq_length_kv = math.prod(qkv_shape)
505
+ q_tile_size, kv_tile_size = 64, 64
506
+ if do_token_permute:
507
+ assert q_shape is not None
508
+ assert kv_shape is not None
509
+ assert q_tile_shape is not None
510
+ assert kv_tile_shape is not None
511
+
512
+ seq_length_q = math.prod(q_shape)
513
+ seq_length_kv = math.prod(kv_shape)
514
+ q_tile_size, kv_tile_size = math.prod(q_tile_shape), math.prod(kv_tile_shape)
515
+
516
+ mask_mod = functools.partial(
517
+ multi_dim_tiling_mask,
518
+ q_tile_size=q_tile_size,
519
+ kv_tile_size=kv_tile_size,
520
+ q_tile_shape=q_tile_shape,
521
+ kv_tile_shape=kv_tile_shape,
522
+ qkv_shape=qkv_shape,
523
+ kernel_size=kernel_size,
524
+ stride=stride,
525
+ dilation=dilation,
526
+ is_causal=is_causal,
527
+ )
528
+ else:
529
+ mask_mod = functools.partial(
530
+ single_dim_tiling_mask,
531
+ qkv_shape=qkv_shape,
532
+ kernel_size=kernel_size,
533
+ stride=stride,
534
+ dilation=dilation,
535
+ is_causal=is_causal,
536
+ )
537
+
538
+ block_mask = create_block_mask(
539
+ mask_mod,
540
+ B=None,
541
+ H=None,
542
+ Q_LEN=seq_length_q,
543
+ KV_LEN=seq_length_kv,
544
+ _compile=torch_compile,
545
+ BLOCK_SIZE=(q_tile_size, kv_tile_size),
546
+ device=device,
547
+ )
548
+ if not is_torch_compiling():
549
+ flex_mask_end_time = time.perf_counter()
550
+ flex_mask_time = flex_mask_end_time - flex_mask_start_time
551
+ logger.debug(
552
+ f"Flex Attention block mask ({torch_compile=}) created in {flex_mask_time:.2f} seconds."
553
+ )
554
+ return block_mask
555
+
556
+
557
+ def flex_fna_generic(
558
+ query: Tensor,
559
+ key: Tensor,
560
+ value: Tensor,
561
+ kernel_size: DimensionTypeOrDed,
562
+ stride: DimensionTypeOrDed = 1,
563
+ dilation: DimensionTypeOrDed = 1,
564
+ is_causal: Optional[CausalArgTypeOrDed] = False,
565
+ scale: Optional[float] = None,
566
+ q_tile_shape: Optional[DimensionType] = None,
567
+ kv_tile_shape: Optional[DimensionType] = None,
568
+ torch_compile: bool = False,
569
+ return_lse: bool = False,
570
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
571
+
572
+ na_tensor_checks(
573
+ query, key, value, must_match_head_dims=True, supports_gqa_mqa=True
574
+ )
575
+
576
+ na_dim = query.dim() - 3 # batch, heads, head_dim
577
+
578
+ kernel_size, stride, dilation, is_causal = check_all_args(
579
+ na_dim, kernel_size, stride, dilation, is_causal
580
+ )
581
+
582
+ check_args_against_input(
583
+ query,
584
+ kernel_size=kernel_size,
585
+ stride=stride,
586
+ dilation=dilation,
587
+ is_causal=is_causal,
588
+ )
589
+
590
+ batch_size, *qkv_shape_in, num_heads, head_dim = query.shape
591
+ num_heads_kv, head_dim_v = value.shape[-2:]
592
+ qkv_shape = check_input_size_arg(na_dim, qkv_shape_in)
593
+
594
+ scale = scale or query.shape[-1] ** -0.5
595
+
596
+ assert can_run_flex_attention(
597
+ query, key, value, torch_compile=torch_compile, raise_error=True
598
+ )
599
+
600
+ if (q_tile_shape is None) ^ (kv_tile_shape is None):
601
+ raise ValueError(
602
+ "Please specify both q_tile_shape and kv_tile_shape, or neither one. "
603
+ f"Got {q_tile_shape=}, {kv_tile_shape=}."
604
+ )
605
+
606
+ do_token_permute = q_tile_shape is not None and kv_tile_shape is not None
607
+
608
+ q_shape = kv_shape = qkv_shape
609
+ q_tile_size: Optional[int] = None
610
+ kv_tile_size: Optional[int] = None
611
+ if do_token_permute:
612
+ q_tile_shape, kv_tile_shape = check_flex_fna_forward_config(
613
+ input_tensor=query,
614
+ q_tile_shape=q_tile_shape,
615
+ kv_tile_shape=kv_tile_shape,
616
+ )
617
+
618
+ q_tile_size = math.prod(q_tile_shape)
619
+ kv_tile_size = math.prod(kv_tile_shape)
620
+
621
+ assert q_tile_shape is not None
622
+ assert kv_tile_shape is not None
623
+ query_perm, _, q_shape = token_permute_operation(
624
+ query,
625
+ tile_shape=q_tile_shape,
626
+ dilation=dilation,
627
+ flip_tiled_dims=False,
628
+ )
629
+ key_perm, _, k_shape = token_permute_operation(
630
+ key, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=False
631
+ )
632
+ value_perm, _, v_shape = token_permute_operation(
633
+ value, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=False
634
+ )
635
+
636
+ assert k_shape == v_shape
637
+ kv_shape = k_shape
638
+
639
+ # Token permute already flattens to 1-D
640
+ # Flex uses heads first layout
641
+ query_ = query_perm.transpose(1, 2)
642
+ key_ = key_perm.transpose(1, 2)
643
+ value_ = value_perm.transpose(1, 2)
644
+
645
+ else:
646
+ seqlen = math.prod(qkv_shape)
647
+ # Flex uses heads first layout
648
+ query_ = query.reshape(batch_size, seqlen, num_heads, head_dim).transpose(1, 2)
649
+ key_ = key.reshape(batch_size, seqlen, num_heads_kv, head_dim).transpose(1, 2)
650
+ value_ = value.reshape(batch_size, seqlen, num_heads_kv, head_dim_v).transpose(
651
+ 1, 2
652
+ )
653
+
654
+ na_block_mask = get_na_flex_mask(
655
+ device=query.device.type,
656
+ na_dim=na_dim,
657
+ qkv_shape=qkv_shape,
658
+ kernel_size=kernel_size,
659
+ stride=stride,
660
+ dilation=dilation,
661
+ is_causal=is_causal,
662
+ q_tile_shape=q_tile_shape,
663
+ kv_tile_shape=kv_tile_shape,
664
+ q_shape=q_shape,
665
+ kv_shape=kv_shape,
666
+ torch_compile=torch_compile,
667
+ )
668
+
669
+ out_, lse_ = run_flex_attn(
670
+ query_,
671
+ key_,
672
+ value_,
673
+ na_block_mask,
674
+ scale,
675
+ torch_compile=torch_compile,
676
+ q_tile_size=q_tile_size,
677
+ kv_tile_size=kv_tile_size,
678
+ )
679
+
680
+ if do_token_permute:
681
+ out = out_.transpose(1, 2)
682
+ lse = lse_.transpose(1, 2).unsqueeze(-1)
683
+
684
+ assert q_tile_shape is not None
685
+ assert kv_tile_shape is not None
686
+ out = token_unpermute_operation(
687
+ out,
688
+ token_layout_shape=qkv_shape,
689
+ tile_shape=q_tile_shape,
690
+ dilation=dilation,
691
+ flip_tiled_dims=False,
692
+ )
693
+ lse = token_unpermute_operation(
694
+ lse,
695
+ token_layout_shape=qkv_shape,
696
+ tile_shape=q_tile_shape,
697
+ dilation=dilation,
698
+ flip_tiled_dims=False,
699
+ ).squeeze(-1)
700
+ else:
701
+ out = out_.transpose(1, 2).reshape(
702
+ batch_size, *qkv_shape, num_heads, head_dim_v
703
+ )
704
+ lse = lse_.transpose(1, 2).reshape(batch_size, *qkv_shape, num_heads)
705
+
706
+ if return_lse:
707
+ return out, lse
708
+
709
+ return out
710
+
711
+
712
+ def na1d_flex(
713
+ query: Tensor,
714
+ key: Tensor,
715
+ value: Tensor,
716
+ kernel_size: Dimension1DTypeOrDed,
717
+ stride: Dimension1DTypeOrDed = 1,
718
+ dilation: Dimension1DTypeOrDed = 1,
719
+ is_causal: Optional[CausalArg1DTypeOrDed] = False,
720
+ scale: Optional[float] = None,
721
+ q_tile_shape: Optional[Dimension1DType] = None,
722
+ kv_tile_shape: Optional[Dimension1DType] = None,
723
+ torch_compile: bool = False,
724
+ return_lse: bool = False,
725
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
726
+ return flex_fna_generic(
727
+ query=query,
728
+ key=key,
729
+ value=value,
730
+ kernel_size=kernel_size,
731
+ stride=stride,
732
+ dilation=dilation,
733
+ is_causal=is_causal,
734
+ scale=scale,
735
+ q_tile_shape=q_tile_shape,
736
+ kv_tile_shape=kv_tile_shape,
737
+ torch_compile=torch_compile,
738
+ return_lse=return_lse,
739
+ )
740
+
741
+
742
+ def na2d_flex(
743
+ query: Tensor,
744
+ key: Tensor,
745
+ value: Tensor,
746
+ kernel_size: Dimension2DTypeOrDed,
747
+ stride: Dimension2DTypeOrDed = 1,
748
+ dilation: Dimension2DTypeOrDed = 1,
749
+ is_causal: Optional[CausalArg2DTypeOrDed] = False,
750
+ scale: Optional[float] = None,
751
+ q_tile_shape: Optional[Dimension2DType] = None,
752
+ kv_tile_shape: Optional[Dimension2DType] = None,
753
+ torch_compile: bool = False,
754
+ return_lse: bool = False,
755
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
756
+ return flex_fna_generic(
757
+ query=query,
758
+ key=key,
759
+ value=value,
760
+ kernel_size=kernel_size,
761
+ stride=stride,
762
+ dilation=dilation,
763
+ is_causal=is_causal,
764
+ scale=scale,
765
+ q_tile_shape=q_tile_shape,
766
+ kv_tile_shape=kv_tile_shape,
767
+ torch_compile=torch_compile,
768
+ return_lse=return_lse,
769
+ )
770
+
771
+
772
+ def na3d_flex(
773
+ query: Tensor,
774
+ key: Tensor,
775
+ value: Tensor,
776
+ kernel_size: Dimension3DTypeOrDed,
777
+ stride: Dimension3DTypeOrDed = 1,
778
+ dilation: Dimension3DTypeOrDed = 1,
779
+ is_causal: Optional[CausalArg3DTypeOrDed] = False,
780
+ scale: Optional[float] = None,
781
+ q_tile_shape: Optional[Dimension3DType] = None,
782
+ kv_tile_shape: Optional[Dimension3DType] = None,
783
+ torch_compile: bool = False,
784
+ return_lse: bool = False,
785
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
786
+ return flex_fna_generic(
787
+ query=query,
788
+ key=key,
789
+ value=value,
790
+ kernel_size=kernel_size,
791
+ stride=stride,
792
+ dilation=dilation,
793
+ is_causal=is_causal,
794
+ scale=scale,
795
+ q_tile_shape=q_tile_shape,
796
+ kv_tile_shape=kv_tile_shape,
797
+ torch_compile=torch_compile,
798
+ return_lse=return_lse,
799
+ )
build/torch210-cu126-aarch64-linux/backends/fmha.py ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import fmha_backward, fmha_forward
35
+ from ..backends.configs.checks import can_run_cutlass_fmha
36
+ from ..backends.configs.cutlass import (
37
+ check_cutlass_fmha_backward_config,
38
+ check_cutlass_fmha_forward_config,
39
+ )
40
+ from .._types import (
41
+ CutlassFmhaBackwardConfigType,
42
+ CutlassFmhaForwardConfigType,
43
+ NoneType,
44
+ )
45
+ from ..utils import log
46
+ from ..utils.checks import fmha_tensor_checks, varlen_tensor_checks
47
+
48
+ logger = log.get_logger(__name__)
49
+
50
+
51
+ class CutlassFmhaAutogradFn(Function):
52
+ @staticmethod
53
+ @amp_fwd
54
+ def forward(
55
+ ctx,
56
+ query: Tensor,
57
+ key: Tensor,
58
+ value: Tensor,
59
+ is_causal: bool,
60
+ scale: float,
61
+ forward_config: CutlassFmhaForwardConfigType,
62
+ backward_config: CutlassFmhaBackwardConfigType,
63
+ backward_kv_splits: Optional[int],
64
+ backward_use_pt_reduction: bool,
65
+ cumulative_seqlen_Q: Optional[Tensor],
66
+ cumulative_seqlen_KV: Optional[Tensor],
67
+ max_seqlen_Q: int,
68
+ max_seqlen_KV: int,
69
+ ) -> Tuple[Tensor, Tensor]:
70
+
71
+ query = query.contiguous()
72
+ key = key.contiguous()
73
+ value = value.contiguous()
74
+
75
+ assert query.dim() == value.dim() == 4
76
+ assert query.shape[0] == value.shape[0]
77
+ assert query.shape[2] == value.shape[2]
78
+
79
+ q_tile_size, kv_tile_size = forward_config
80
+ output, logsumexp = fmha_forward(
81
+ query,
82
+ key,
83
+ value,
84
+ is_causal,
85
+ scale,
86
+ q_tile_size,
87
+ kv_tile_size,
88
+ cumulative_seqlen_Q,
89
+ cumulative_seqlen_KV,
90
+ max_seqlen_Q,
91
+ max_seqlen_KV,
92
+ )
93
+
94
+ ctx.save_for_backward(
95
+ query,
96
+ key,
97
+ value,
98
+ logsumexp,
99
+ output,
100
+ cumulative_seqlen_Q,
101
+ cumulative_seqlen_KV,
102
+ )
103
+ ctx.scale = scale
104
+ ctx.is_causal = is_causal
105
+ ctx.max_seqlen_Q = max_seqlen_Q
106
+ ctx.max_seqlen_KV = max_seqlen_KV
107
+ ctx.backward_config = backward_config
108
+ ctx.backward_kv_splits = backward_kv_splits
109
+ ctx.backward_use_pt_reduction = backward_use_pt_reduction
110
+ # Always record determinism behavior during forward pass (forward pass itself is
111
+ # deterministic anyway).
112
+ # Determinism could be limited to part of the program, which means during forward pass
113
+ # it'll be true, but on .backward() call, if it's been turned off, it will stay off when we
114
+ # get to this operation's backward call.
115
+ ctx.deterministic = torch.are_deterministic_algorithms_enabled()
116
+
117
+ return output, logsumexp
118
+
119
+ @staticmethod
120
+ @amp_bwd
121
+ def backward(ctx, grad_out: Tensor, grad_lse: Tensor) -> Tuple[
122
+ Tensor,
123
+ Tensor,
124
+ Tensor,
125
+ NoneType,
126
+ NoneType,
127
+ NoneType,
128
+ NoneType,
129
+ # kv_splits, use_pt_reduction
130
+ NoneType,
131
+ NoneType,
132
+ # varlen
133
+ NoneType,
134
+ NoneType,
135
+ NoneType,
136
+ NoneType,
137
+ ]:
138
+ (
139
+ query,
140
+ key,
141
+ value,
142
+ logsumexp,
143
+ output,
144
+ cumulative_seqlen_Q,
145
+ cumulative_seqlen_KV,
146
+ ) = ctx.saved_tensors
147
+ d_output = grad_out.contiguous()
148
+
149
+ q_tile_size, k_tile_size = ctx.backward_config
150
+
151
+ d_query, d_key, d_value = fmha_backward(
152
+ query,
153
+ key,
154
+ value,
155
+ output,
156
+ d_output,
157
+ logsumexp,
158
+ ctx.is_causal,
159
+ ctx.scale,
160
+ q_tile_size,
161
+ k_tile_size,
162
+ ctx.backward_kv_splits,
163
+ ctx.backward_use_pt_reduction,
164
+ cumulative_seqlen_Q,
165
+ cumulative_seqlen_KV,
166
+ ctx.max_seqlen_Q,
167
+ ctx.max_seqlen_KV,
168
+ ctx.deterministic,
169
+ )
170
+
171
+ return (
172
+ d_query,
173
+ d_key,
174
+ d_value,
175
+ None,
176
+ None,
177
+ None,
178
+ None,
179
+ None,
180
+ None,
181
+ None,
182
+ None,
183
+ None,
184
+ None,
185
+ )
186
+
187
+
188
+ def cutlass_fmha(
189
+ query: Tensor,
190
+ key: Tensor,
191
+ value: Tensor,
192
+ is_causal: bool = False,
193
+ scale: Optional[float] = None,
194
+ q_tile_size: Optional[int] = None,
195
+ kv_tile_size: Optional[int] = None,
196
+ backward_q_tile_size: Optional[int] = None,
197
+ backward_kv_tile_size: Optional[int] = None,
198
+ backward_kv_splits: Optional[int] = None,
199
+ backward_use_pt_reduction: bool = False,
200
+ return_lse: bool = False,
201
+ # varlen parameters
202
+ cumulative_seqlen_Q: Optional[Tensor] = None,
203
+ cumulative_seqlen_KV: Optional[Tensor] = None,
204
+ max_seqlen_Q: int = 0,
205
+ max_seqlen_KV: int = 0,
206
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
207
+
208
+ fmha_tensor_checks(
209
+ query,
210
+ key,
211
+ value,
212
+ must_match_head_dims=False,
213
+ supports_gqa_mqa=True,
214
+ backend_name="CUTLASS FMHA",
215
+ )
216
+
217
+ (
218
+ cumulative_seqlen_Q,
219
+ cumulative_seqlen_KV,
220
+ max_seqlen_Q,
221
+ max_seqlen_KV,
222
+ ) = varlen_tensor_checks(
223
+ query=query,
224
+ key=key,
225
+ value=value,
226
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
227
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
228
+ max_seqlen_Q=max_seqlen_Q,
229
+ max_seqlen_KV=max_seqlen_KV,
230
+ )
231
+ is_varlen = cumulative_seqlen_Q is not None
232
+
233
+ assert can_run_cutlass_fmha(
234
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen, raise_error=True
235
+ )
236
+
237
+ forward_config = check_cutlass_fmha_forward_config(
238
+ input_tensor=query if value.shape[-1] <= query.shape[-1] else value,
239
+ q_tile_size=q_tile_size,
240
+ kv_tile_size=kv_tile_size,
241
+ )
242
+
243
+ backward_config = check_cutlass_fmha_backward_config(
244
+ input_tensor=key if value.shape[-1] <= key.shape[-1] else value,
245
+ q_tile_size=backward_q_tile_size,
246
+ kv_tile_size=backward_kv_tile_size,
247
+ )
248
+
249
+ scale = scale or query.shape[-1] ** -0.5
250
+
251
+ # GQA/MQA is not supported by the kernel; only allowed via graph transform
252
+ is_gqa = query.shape[-2] != key.shape[-2]
253
+ if is_gqa:
254
+ heads = query.shape[-2]
255
+ heads_kv = key.shape[-2]
256
+ assert key.shape[-2] == value.shape[-2]
257
+ assert heads >= heads_kv
258
+ assert heads % heads_kv == 0
259
+ h_k = heads // heads_kv
260
+
261
+ key = torch.repeat_interleave(key, repeats=h_k, dim=-2, output_size=heads)
262
+ value = torch.repeat_interleave(value, repeats=h_k, dim=-2, output_size=heads)
263
+
264
+ output, lse = CutlassFmhaAutogradFn.apply(
265
+ query,
266
+ key,
267
+ value,
268
+ is_causal,
269
+ scale,
270
+ forward_config,
271
+ backward_config,
272
+ backward_kv_splits,
273
+ backward_use_pt_reduction,
274
+ cumulative_seqlen_Q,
275
+ cumulative_seqlen_KV,
276
+ max_seqlen_Q,
277
+ max_seqlen_KV,
278
+ )
279
+
280
+ if return_lse:
281
+ return output, lse
282
+
283
+ return output
build/torch210-cu126-aarch64-linux/backends/fna.py ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import (
35
+ na1d_backward,
36
+ na1d_forward,
37
+ na2d_backward,
38
+ na2d_forward,
39
+ na3d_backward,
40
+ na3d_forward,
41
+ )
42
+ from ..backends.configs.checks import can_run_cutlass_fna
43
+ from ..backends.configs.cutlass import (
44
+ check_cutlass_fna_backward_config,
45
+ check_cutlass_fna_forward_config,
46
+ )
47
+ from .._types import (
48
+ CausalArg1DTypeOrDed,
49
+ CausalArg2DTypeOrDed,
50
+ CausalArg3DTypeOrDed,
51
+ CausalArgType,
52
+ CausalArgTypeOrDed,
53
+ CutlassFnaBackwardConfigType,
54
+ CutlassFnaForwardConfigType,
55
+ Dimension1DType,
56
+ Dimension1DTypeOrDed,
57
+ Dimension2DType,
58
+ Dimension2DTypeOrDed,
59
+ Dimension3DType,
60
+ Dimension3DTypeOrDed,
61
+ DimensionType,
62
+ DimensionTypeOrDed,
63
+ NoneType,
64
+ )
65
+ from ..utils import log
66
+ from ..utils.checks import (
67
+ check_all_args,
68
+ check_args_against_input,
69
+ na_tensor_checks,
70
+ )
71
+
72
+ logger = log.get_logger(__name__)
73
+
74
+
75
+ def make_cutlass_fna_autograd_fn(na_dim):
76
+ assert na_dim in [1, 2, 3]
77
+
78
+ FORWARD_OPS = {
79
+ 1: na1d_forward,
80
+ 2: na2d_forward,
81
+ 3: na3d_forward,
82
+ }
83
+
84
+ BACKWARD_OPS = {
85
+ 1: na1d_backward,
86
+ 2: na2d_backward,
87
+ 3: na3d_backward,
88
+ }
89
+
90
+ class CutlassFnaGenericAutogradFn(Function):
91
+ @staticmethod
92
+ @amp_fwd
93
+ def forward(
94
+ ctx,
95
+ query: Tensor,
96
+ key: Tensor,
97
+ value: Tensor,
98
+ kernel_size: DimensionType,
99
+ stride: DimensionType,
100
+ dilation: DimensionType,
101
+ is_causal: CausalArgType,
102
+ scale: float,
103
+ forward_config: CutlassFnaForwardConfigType,
104
+ backward_config: CutlassFnaBackwardConfigType,
105
+ backward_kv_splits: Optional[DimensionType],
106
+ backward_use_pt_reduction: bool,
107
+ ) -> Tuple[Tensor, Tensor]:
108
+ kernel_size, stride, dilation, is_causal = check_all_args(
109
+ na_dim, kernel_size, stride, dilation, is_causal
110
+ )
111
+
112
+ query = query.contiguous()
113
+ key = key.contiguous()
114
+ value = value.contiguous()
115
+
116
+ assert query.dim() == value.dim() == 3 + na_dim
117
+ assert query.shape[0] == value.shape[0]
118
+ assert query.shape[-2] == value.shape[-2]
119
+
120
+ q_tile_shape, kv_tile_shape = forward_config
121
+
122
+ output, logsumexp = FORWARD_OPS[na_dim](
123
+ query,
124
+ key,
125
+ value,
126
+ kernel_size,
127
+ stride,
128
+ dilation,
129
+ is_causal,
130
+ scale,
131
+ q_tile_shape,
132
+ kv_tile_shape,
133
+ )
134
+
135
+ ctx.save_for_backward(query, key, value, logsumexp, output)
136
+ ctx.kernel_size = kernel_size
137
+ ctx.stride = stride
138
+ ctx.dilation = dilation
139
+ ctx.is_causal = is_causal
140
+ ctx.scale = scale
141
+ ctx.backward_config = backward_config
142
+ ctx.backward_kv_splits = backward_kv_splits
143
+ ctx.backward_use_pt_reduction = backward_use_pt_reduction
144
+ # Always record determinism behavior during forward pass (forward pass itself is
145
+ # deterministic anyway).
146
+ # Determinism could be limited to part of the program, which means during forward pass
147
+ # it'll be true, but on .backward() call, if it's been turned off, it will stay off when we
148
+ # get to this operation's backward call.
149
+ ctx.deterministic = torch.are_deterministic_algorithms_enabled()
150
+
151
+ return output, logsumexp
152
+
153
+ @staticmethod
154
+ @amp_bwd
155
+ def backward(ctx, grad_out: Tensor, grad_lse: Tensor) -> Tuple[
156
+ Tensor,
157
+ Tensor,
158
+ Tensor,
159
+ NoneType,
160
+ NoneType,
161
+ NoneType,
162
+ NoneType,
163
+ NoneType,
164
+ NoneType,
165
+ NoneType,
166
+ NoneType,
167
+ NoneType,
168
+ ]:
169
+ query, key, value, logsumexp, output = ctx.saved_tensors
170
+ d_output = grad_out.contiguous()
171
+
172
+ q_tile_shape, k_tile_shape = ctx.backward_config
173
+
174
+ d_query, d_key, d_value = BACKWARD_OPS[na_dim](
175
+ query,
176
+ key,
177
+ value,
178
+ output,
179
+ d_output,
180
+ logsumexp,
181
+ ctx.kernel_size,
182
+ ctx.stride,
183
+ ctx.dilation,
184
+ ctx.is_causal,
185
+ ctx.scale,
186
+ q_tile_shape,
187
+ k_tile_shape,
188
+ ctx.backward_kv_splits,
189
+ ctx.backward_use_pt_reduction,
190
+ ctx.deterministic,
191
+ )
192
+
193
+ return (
194
+ d_query,
195
+ d_key,
196
+ d_value,
197
+ None,
198
+ None,
199
+ None,
200
+ None,
201
+ None,
202
+ None,
203
+ None,
204
+ None,
205
+ None,
206
+ )
207
+
208
+ return CutlassFnaGenericAutogradFn
209
+
210
+
211
+ CutlassFna1DAutogradFn = make_cutlass_fna_autograd_fn(1)
212
+ CutlassFna2DAutogradFn = make_cutlass_fna_autograd_fn(2)
213
+ CutlassFna3DAutogradFn = make_cutlass_fna_autograd_fn(3)
214
+
215
+
216
+ CutlassFNAAutogradFns = {
217
+ 1: CutlassFna1DAutogradFn,
218
+ 2: CutlassFna2DAutogradFn,
219
+ 3: CutlassFna3DAutogradFn,
220
+ }
221
+
222
+
223
+ def cutlass_fna_generic(
224
+ query: Tensor,
225
+ key: Tensor,
226
+ value: Tensor,
227
+ kernel_size: DimensionTypeOrDed,
228
+ stride: DimensionTypeOrDed = 1,
229
+ dilation: DimensionTypeOrDed = 1,
230
+ is_causal: Optional[CausalArgTypeOrDed] = False,
231
+ scale: Optional[float] = None,
232
+ q_tile_shape: Optional[DimensionType] = None,
233
+ kv_tile_shape: Optional[DimensionType] = None,
234
+ backward_q_tile_shape: Optional[DimensionType] = None,
235
+ backward_kv_tile_shape: Optional[DimensionType] = None,
236
+ backward_kv_splits: Optional[DimensionType] = None,
237
+ backward_use_pt_reduction: bool = False,
238
+ return_lse: bool = False,
239
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
240
+
241
+ na_tensor_checks(
242
+ query, key, value, must_match_head_dims=False, supports_gqa_mqa=True
243
+ )
244
+
245
+ assert can_run_cutlass_fna(query, key, value, raise_error=True)
246
+
247
+ na_dim = query.dim() - 3 # batch, heads, head_dim
248
+
249
+ assert na_dim in [1, 2, 3]
250
+
251
+ kernel_size, stride, dilation, is_causal = check_all_args(
252
+ na_dim, kernel_size, stride, dilation, is_causal
253
+ )
254
+
255
+ check_args_against_input(
256
+ query,
257
+ kernel_size=kernel_size,
258
+ stride=stride,
259
+ dilation=dilation,
260
+ is_causal=is_causal,
261
+ )
262
+
263
+ forward_config = check_cutlass_fna_forward_config(
264
+ input_tensor=query if value.shape[-1] <= query.shape[-1] else value,
265
+ dilation=dilation,
266
+ q_tile_shape=q_tile_shape,
267
+ kv_tile_shape=kv_tile_shape,
268
+ )
269
+
270
+ backward_config = check_cutlass_fna_backward_config(
271
+ input_tensor=key if value.shape[-1] <= key.shape[-1] else value,
272
+ q_tile_shape=backward_q_tile_shape,
273
+ kv_tile_shape=backward_kv_tile_shape,
274
+ )
275
+
276
+ scale = scale or query.shape[-1] ** -0.5
277
+
278
+ # GQA/MQA is not supported by the kernel; only allowed via graph transform
279
+ is_gqa = query.shape[-2] != key.shape[-2]
280
+ if is_gqa:
281
+ heads = query.shape[-2]
282
+ heads_kv = key.shape[-2]
283
+ assert key.shape[-2] == value.shape[-2]
284
+ assert heads >= heads_kv
285
+ assert heads % heads_kv == 0
286
+ h_k = heads // heads_kv
287
+
288
+ key = torch.repeat_interleave(key, repeats=h_k, dim=-2, output_size=heads)
289
+ value = torch.repeat_interleave(value, repeats=h_k, dim=-2, output_size=heads)
290
+
291
+ output, lse = CutlassFNAAutogradFns[na_dim].apply(
292
+ query,
293
+ key,
294
+ value,
295
+ kernel_size,
296
+ stride,
297
+ dilation,
298
+ is_causal,
299
+ scale,
300
+ forward_config,
301
+ backward_config,
302
+ backward_kv_splits,
303
+ backward_use_pt_reduction,
304
+ )
305
+
306
+ if return_lse:
307
+ return output, lse
308
+
309
+ return output
310
+
311
+
312
+ def na1d_cutlass_fna(
313
+ query: Tensor,
314
+ key: Tensor,
315
+ value: Tensor,
316
+ kernel_size: Dimension1DTypeOrDed,
317
+ stride: Dimension1DTypeOrDed = 1,
318
+ dilation: Dimension1DTypeOrDed = 1,
319
+ is_causal: Optional[CausalArg1DTypeOrDed] = False,
320
+ scale: Optional[float] = None,
321
+ q_tile_shape: Optional[Dimension1DType] = None,
322
+ kv_tile_shape: Optional[Dimension1DType] = None,
323
+ backward_q_tile_shape: Optional[Dimension1DType] = None,
324
+ backward_kv_tile_shape: Optional[Dimension1DType] = None,
325
+ backward_kv_splits: Optional[Dimension1DType] = None,
326
+ backward_use_pt_reduction: bool = False,
327
+ return_lse: bool = False,
328
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
329
+ return cutlass_fna_generic(
330
+ query=query,
331
+ key=key,
332
+ value=value,
333
+ kernel_size=kernel_size,
334
+ stride=stride,
335
+ dilation=dilation,
336
+ is_causal=is_causal,
337
+ scale=scale,
338
+ q_tile_shape=q_tile_shape,
339
+ kv_tile_shape=kv_tile_shape,
340
+ backward_q_tile_shape=backward_q_tile_shape,
341
+ backward_kv_tile_shape=backward_kv_tile_shape,
342
+ backward_kv_splits=backward_kv_splits,
343
+ backward_use_pt_reduction=backward_use_pt_reduction,
344
+ return_lse=return_lse,
345
+ )
346
+
347
+
348
+ def na2d_cutlass_fna(
349
+ query: Tensor,
350
+ key: Tensor,
351
+ value: Tensor,
352
+ kernel_size: Dimension2DTypeOrDed,
353
+ stride: Dimension2DTypeOrDed = 1,
354
+ dilation: Dimension2DTypeOrDed = 1,
355
+ is_causal: Optional[CausalArg2DTypeOrDed] = False,
356
+ scale: Optional[float] = None,
357
+ q_tile_shape: Optional[Dimension2DType] = None,
358
+ kv_tile_shape: Optional[Dimension2DType] = None,
359
+ backward_q_tile_shape: Optional[Dimension2DType] = None,
360
+ backward_kv_tile_shape: Optional[Dimension2DType] = None,
361
+ backward_kv_splits: Optional[Dimension2DType] = None,
362
+ backward_use_pt_reduction: bool = False,
363
+ return_lse: bool = False,
364
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
365
+ return cutlass_fna_generic(
366
+ query=query,
367
+ key=key,
368
+ value=value,
369
+ kernel_size=kernel_size,
370
+ stride=stride,
371
+ dilation=dilation,
372
+ is_causal=is_causal,
373
+ scale=scale,
374
+ q_tile_shape=q_tile_shape,
375
+ kv_tile_shape=kv_tile_shape,
376
+ backward_q_tile_shape=backward_q_tile_shape,
377
+ backward_kv_tile_shape=backward_kv_tile_shape,
378
+ backward_kv_splits=backward_kv_splits,
379
+ backward_use_pt_reduction=backward_use_pt_reduction,
380
+ return_lse=return_lse,
381
+ )
382
+
383
+
384
+ def na3d_cutlass_fna(
385
+ query: Tensor,
386
+ key: Tensor,
387
+ value: Tensor,
388
+ kernel_size: Dimension3DTypeOrDed,
389
+ stride: Dimension3DTypeOrDed = 1,
390
+ dilation: Dimension3DTypeOrDed = 1,
391
+ is_causal: Optional[CausalArg3DTypeOrDed] = False,
392
+ scale: Optional[float] = None,
393
+ q_tile_shape: Optional[Dimension3DType] = None,
394
+ kv_tile_shape: Optional[Dimension3DType] = None,
395
+ backward_q_tile_shape: Optional[Dimension3DType] = None,
396
+ backward_kv_tile_shape: Optional[Dimension3DType] = None,
397
+ backward_kv_splits: Optional[Dimension3DType] = None,
398
+ backward_use_pt_reduction: bool = False,
399
+ return_lse: bool = False,
400
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
401
+ return cutlass_fna_generic(
402
+ query=query,
403
+ key=key,
404
+ value=value,
405
+ kernel_size=kernel_size,
406
+ stride=stride,
407
+ dilation=dilation,
408
+ is_causal=is_causal,
409
+ scale=scale,
410
+ q_tile_shape=q_tile_shape,
411
+ kv_tile_shape=kv_tile_shape,
412
+ backward_q_tile_shape=backward_q_tile_shape,
413
+ backward_kv_tile_shape=backward_kv_tile_shape,
414
+ backward_kv_splits=backward_kv_splits,
415
+ backward_use_pt_reduction=backward_use_pt_reduction,
416
+ return_lse=return_lse,
417
+ )
build/torch210-cu126-aarch64-linux/backends/hopper_fmha.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import hopper_fmha_backward, hopper_fmha_forward
35
+ from ..backends.configs.checks import can_run_cutlass_hopper_fmha
36
+ from ..backends.configs.cutlass_hopper import (
37
+ check_cutlass_hopper_fmha_backward_config,
38
+ check_cutlass_hopper_fmha_forward_config,
39
+ )
40
+ from .._types import (
41
+ CutlassHopperFmhaBackwardConfigType,
42
+ CutlassHopperFmhaForwardConfigType,
43
+ KernelSchedule,
44
+ NoneType,
45
+ )
46
+ from ..utils import log
47
+ from ..utils.checks import fmha_tensor_checks, varlen_tensor_checks
48
+
49
+ logger = log.get_logger(__name__)
50
+
51
+
52
+ class CutlassHopperFmhaAutogradFn(Function):
53
+ @staticmethod
54
+ @amp_fwd
55
+ def forward(
56
+ ctx,
57
+ query: Tensor,
58
+ key: Tensor,
59
+ value: Tensor,
60
+ is_causal: bool,
61
+ scale: float,
62
+ forward_config: CutlassHopperFmhaForwardConfigType,
63
+ backward_config: CutlassHopperFmhaBackwardConfigType,
64
+ cumulative_seqlen_Q: Optional[Tensor],
65
+ cumulative_seqlen_KV: Optional[Tensor],
66
+ max_seqlen_Q: int,
67
+ max_seqlen_KV: int,
68
+ ) -> Tuple[Tensor, Tensor]:
69
+ query = query.contiguous()
70
+ key = key.contiguous()
71
+ value = value.contiguous()
72
+
73
+ (q_tile_size, kv_tile_size), kernel_schedule = forward_config
74
+
75
+ output, logsumexp = hopper_fmha_forward(
76
+ query,
77
+ key,
78
+ value,
79
+ is_causal,
80
+ scale,
81
+ q_tile_size,
82
+ kv_tile_size,
83
+ kernel_schedule.value, # TODO: I don't like this -- write a map with checks?
84
+ cumulative_seqlen_Q,
85
+ cumulative_seqlen_KV,
86
+ max_seqlen_Q,
87
+ max_seqlen_KV,
88
+ )
89
+
90
+ ctx.save_for_backward(
91
+ query,
92
+ key,
93
+ value,
94
+ logsumexp,
95
+ output,
96
+ cumulative_seqlen_Q,
97
+ cumulative_seqlen_KV,
98
+ )
99
+ ctx.scale = scale
100
+ ctx.is_causal = is_causal
101
+ ctx.max_seqlen_Q = max_seqlen_Q
102
+ ctx.max_seqlen_KV = max_seqlen_KV
103
+ ctx.backward_config = backward_config
104
+ # Always record determinism behavior during forward pass (forward pass itself is
105
+ # deterministic anyway).
106
+ # Determinism could be limited to part of the program, which means during forward pass
107
+ # it'll be true, but on .backward() call, if it's been turned off, it will stay off when we
108
+ # get to this operation's backward call.
109
+ ctx.deterministic = torch.are_deterministic_algorithms_enabled()
110
+
111
+ return output, logsumexp
112
+
113
+ @staticmethod
114
+ @amp_bwd
115
+ def backward(ctx, grad_out: Tensor, grad_lse: Tensor) -> Tuple[
116
+ Tensor,
117
+ Tensor,
118
+ Tensor,
119
+ NoneType,
120
+ NoneType,
121
+ NoneType,
122
+ NoneType,
123
+ NoneType,
124
+ NoneType,
125
+ NoneType,
126
+ NoneType,
127
+ ]:
128
+ (
129
+ query,
130
+ key,
131
+ value,
132
+ logsumexp,
133
+ output,
134
+ cumulative_seqlen_Q,
135
+ cumulative_seqlen_KV,
136
+ ) = ctx.saved_tensors
137
+ d_output = grad_out.contiguous() # noqa: F841
138
+
139
+ q_tile_size, k_tile_size = ctx.backward_config
140
+
141
+ if ctx.deterministic:
142
+ raise RuntimeError(
143
+ "Hopper FMHA backward pass does not have a deterministic mode, "
144
+ "but PyTorch's deterministic algorithms were enabled. To proceed, "
145
+ "you must either disable torch's deterministic mode, or choose a "
146
+ "different backend."
147
+ )
148
+
149
+ d_query, d_key, d_value = hopper_fmha_backward(
150
+ query,
151
+ key,
152
+ value,
153
+ output,
154
+ d_output,
155
+ logsumexp,
156
+ ctx.is_causal,
157
+ ctx.scale,
158
+ q_tile_size,
159
+ k_tile_size,
160
+ cumulative_seqlen_Q,
161
+ cumulative_seqlen_KV,
162
+ ctx.max_seqlen_Q,
163
+ ctx.max_seqlen_KV,
164
+ )
165
+
166
+ return d_query, d_key, d_value, None, None, None, None, None, None, None, None
167
+
168
+
169
+ def cutlass_hopper_fmha(
170
+ query: Tensor,
171
+ key: Tensor,
172
+ value: Tensor,
173
+ is_causal: bool = False,
174
+ scale: Optional[float] = None,
175
+ q_tile_size: Optional[int] = None,
176
+ kv_tile_size: Optional[int] = None,
177
+ kernel_schedule: Optional[KernelSchedule] = None,
178
+ backward_q_tile_size: Optional[int] = None,
179
+ backward_kv_tile_size: Optional[int] = None,
180
+ return_lse: bool = False,
181
+ # varlen parameters
182
+ cumulative_seqlen_Q: Optional[Tensor] = None,
183
+ cumulative_seqlen_KV: Optional[Tensor] = None,
184
+ max_seqlen_Q: int = 0,
185
+ max_seqlen_KV: int = 0,
186
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
187
+
188
+ fmha_tensor_checks(
189
+ query,
190
+ key,
191
+ value,
192
+ must_match_head_dims=True,
193
+ supports_gqa_mqa=True,
194
+ backend_name="Hopper FMHA",
195
+ )
196
+
197
+ (
198
+ cumulative_seqlen_Q,
199
+ cumulative_seqlen_KV,
200
+ max_seqlen_Q,
201
+ max_seqlen_KV,
202
+ ) = varlen_tensor_checks(
203
+ query=query,
204
+ key=key,
205
+ value=value,
206
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
207
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
208
+ max_seqlen_Q=max_seqlen_Q,
209
+ max_seqlen_KV=max_seqlen_KV,
210
+ )
211
+ is_varlen = cumulative_seqlen_Q is not None
212
+
213
+ assert can_run_cutlass_hopper_fmha(
214
+ query, key, value, is_causal=is_causal, is_varlen=is_varlen, raise_error=True
215
+ )
216
+
217
+ forward_config = check_cutlass_hopper_fmha_forward_config(
218
+ input_tensor=query,
219
+ q_tile_size=q_tile_size,
220
+ kv_tile_size=kv_tile_size,
221
+ kernel_schedule=kernel_schedule,
222
+ )
223
+ backward_config = check_cutlass_hopper_fmha_backward_config(
224
+ input_tensor=query,
225
+ q_tile_size=backward_q_tile_size,
226
+ kv_tile_size=backward_kv_tile_size,
227
+ )
228
+
229
+ scale = scale or query.shape[-1] ** -0.5
230
+
231
+ # GQA/MQA is not supported by the kernel; only allowed via graph transform
232
+ is_gqa = query.shape[-2] != key.shape[-2]
233
+ if is_gqa:
234
+ heads = query.shape[-2]
235
+ heads_kv = key.shape[-2]
236
+ assert key.shape[-2] == value.shape[-2]
237
+ assert heads >= heads_kv
238
+ assert heads % heads_kv == 0
239
+ h_k = heads // heads_kv
240
+
241
+ key = torch.repeat_interleave(key, repeats=h_k, dim=-2, output_size=heads)
242
+ value = torch.repeat_interleave(value, repeats=h_k, dim=-2, output_size=heads)
243
+
244
+ output, lse = CutlassHopperFmhaAutogradFn.apply(
245
+ query,
246
+ key,
247
+ value,
248
+ is_causal,
249
+ scale,
250
+ forward_config,
251
+ backward_config,
252
+ cumulative_seqlen_Q,
253
+ cumulative_seqlen_KV,
254
+ max_seqlen_Q,
255
+ max_seqlen_KV,
256
+ )
257
+
258
+ if return_lse:
259
+ return output, lse
260
+
261
+ return output
build/torch210-cu126-aarch64-linux/backends/hopper_fna.py ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import (
35
+ hopper_na1d_backward,
36
+ hopper_na1d_forward,
37
+ hopper_na2d_backward,
38
+ hopper_na2d_forward,
39
+ hopper_na3d_backward,
40
+ hopper_na3d_forward,
41
+ )
42
+ from ..backends.configs.checks import can_run_cutlass_hopper_fna
43
+ from ..backends.configs.cutlass_hopper import (
44
+ check_cutlass_hopper_fna_backward_config,
45
+ check_cutlass_hopper_fna_forward_config,
46
+ )
47
+ from ..token_permute import token_permute_operation, token_unpermute_operation
48
+ from .._types import (
49
+ CausalArg1DTypeOrDed,
50
+ CausalArg2DTypeOrDed,
51
+ CausalArg3DTypeOrDed,
52
+ CausalArgType,
53
+ CausalArgTypeOrDed,
54
+ CutlassHopperFnaBackwardConfigType,
55
+ CutlassHopperFnaForwardConfigType,
56
+ Dimension1DType,
57
+ Dimension1DTypeOrDed,
58
+ Dimension2DType,
59
+ Dimension2DTypeOrDed,
60
+ Dimension3DType,
61
+ Dimension3DTypeOrDed,
62
+ DimensionType,
63
+ DimensionTypeOrDed,
64
+ KernelSchedule,
65
+ NoneType,
66
+ )
67
+ from ..utils.checks import (
68
+ check_all_args,
69
+ check_args_against_input,
70
+ na_tensor_checks,
71
+ )
72
+
73
+
74
+ def make_cutlass_hopper_fna_autograd_fn(na_dim):
75
+ assert na_dim in [1, 2, 3]
76
+
77
+ FORWARD_OPS = {
78
+ 1: hopper_na1d_forward,
79
+ 2: hopper_na2d_forward,
80
+ 3: hopper_na3d_forward,
81
+ }
82
+
83
+ BACKWARD_OPS = {
84
+ 1: hopper_na1d_backward,
85
+ 2: hopper_na2d_backward,
86
+ 3: hopper_na3d_backward,
87
+ }
88
+
89
+ class CutlassHopperFnaGenericAutogradFn(Function):
90
+ @staticmethod
91
+ @amp_fwd
92
+ def forward(
93
+ ctx,
94
+ query: Tensor,
95
+ key: Tensor,
96
+ value: Tensor,
97
+ kernel_size: DimensionType,
98
+ stride: DimensionType,
99
+ dilation: DimensionType,
100
+ is_causal: CausalArgType,
101
+ scale: float,
102
+ forward_config: CutlassHopperFnaForwardConfigType,
103
+ backward_config: CutlassHopperFnaBackwardConfigType,
104
+ ) -> Tuple[Tensor, Tensor]:
105
+ kernel_size, stride, dilation, is_causal = check_all_args(
106
+ na_dim, kernel_size, stride, dilation, is_causal
107
+ )
108
+
109
+ (q_tile_shape, kv_tile_shape), kernel_schedule = forward_config
110
+
111
+ # Token permute begin
112
+ query_perm, qkv_shape, q_shape = token_permute_operation(
113
+ query,
114
+ q_tile_shape,
115
+ dilation=dilation,
116
+ flip_tiled_dims=True,
117
+ )
118
+ key_perm, _, k_shape = token_permute_operation(
119
+ key, kv_tile_shape, dilation=dilation, flip_tiled_dims=True
120
+ )
121
+ value_perm, _, v_shape = token_permute_operation(
122
+ value, kv_tile_shape, dilation=dilation, flip_tiled_dims=True
123
+ )
124
+
125
+ assert k_shape == v_shape
126
+ kv_shape = k_shape
127
+ # Token permute end
128
+
129
+ query_perm = query_perm.contiguous()
130
+ key_perm = key_perm.contiguous()
131
+ value_perm = value_perm.contiguous()
132
+
133
+ output_perm, logsumexp_perm = FORWARD_OPS[na_dim](
134
+ query_perm,
135
+ key_perm,
136
+ value_perm,
137
+ kernel_size,
138
+ stride,
139
+ dilation,
140
+ is_causal,
141
+ scale,
142
+ q_shape,
143
+ kv_shape,
144
+ qkv_shape,
145
+ q_tile_shape,
146
+ kv_tile_shape,
147
+ kernel_schedule.value, # TODO: I don't like this -- write a map with checks?
148
+ )
149
+
150
+ # Token un-permute begin
151
+ output = token_unpermute_operation(
152
+ output_perm,
153
+ token_layout_shape=qkv_shape,
154
+ tile_shape=q_tile_shape,
155
+ dilation=dilation,
156
+ flip_tiled_dims=True,
157
+ )
158
+ logsumexp = token_unpermute_operation(
159
+ logsumexp_perm.unsqueeze(-1),
160
+ token_layout_shape=qkv_shape,
161
+ tile_shape=q_tile_shape,
162
+ dilation=dilation,
163
+ flip_tiled_dims=True,
164
+ ).squeeze(-1)
165
+ # Token un-permute end
166
+
167
+ ctx.save_for_backward(query, key, value, logsumexp, output)
168
+ ctx.kernel_size = kernel_size
169
+ ctx.stride = stride
170
+ ctx.dilation = dilation
171
+ ctx.is_causal = is_causal
172
+ ctx.scale = scale
173
+ ctx.backward_config = backward_config
174
+ # Always record determinism behavior during forward pass (forward pass itself is
175
+ # deterministic anyway).
176
+ # Determinism could be limited to part of the program, which means during forward pass
177
+ # it'll be true, but on .backward() call, if it's been turned off, it will stay off when we
178
+ # get to this operation's backward call.
179
+ ctx.deterministic = torch.are_deterministic_algorithms_enabled()
180
+
181
+ return output, logsumexp
182
+
183
+ @staticmethod
184
+ @amp_bwd
185
+ def backward(ctx, d_output: Tensor, d_lse: Tensor) -> Tuple[
186
+ Tensor,
187
+ Tensor,
188
+ Tensor,
189
+ NoneType,
190
+ NoneType,
191
+ NoneType,
192
+ NoneType,
193
+ NoneType,
194
+ NoneType,
195
+ NoneType,
196
+ ]:
197
+ query, key, value, logsumexp, output = ctx.saved_tensors
198
+ kernel_size, stride, dilation, is_causal, scale = (
199
+ ctx.kernel_size,
200
+ ctx.stride,
201
+ ctx.dilation,
202
+ ctx.is_causal,
203
+ ctx.scale,
204
+ )
205
+
206
+ q_tile_shape, kv_tile_shape = ctx.backward_config
207
+
208
+ if ctx.deterministic:
209
+ raise RuntimeError(
210
+ "Hopper FNA backward pass does not have a deterministic mode, "
211
+ "but PyTorch's deterministic algorithms were enabled. To proceed, "
212
+ "you must either disable torch's deterministic mode, or choose a "
213
+ "different backend."
214
+ )
215
+
216
+ # Token permute begin
217
+
218
+ query_perm, qkv_shape, q_shape = token_permute_operation(
219
+ query, tile_shape=q_tile_shape, dilation=dilation, flip_tiled_dims=True
220
+ )
221
+ output_perm, _, o_shape = token_permute_operation(
222
+ output, tile_shape=q_tile_shape, dilation=dilation, flip_tiled_dims=True
223
+ )
224
+ d_output_perm, _, d_o_shape = token_permute_operation(
225
+ d_output,
226
+ tile_shape=q_tile_shape,
227
+ dilation=dilation,
228
+ flip_tiled_dims=True,
229
+ )
230
+ logsumexp_perm, _, _ = token_permute_operation(
231
+ logsumexp.unsqueeze(-1),
232
+ tile_shape=q_tile_shape,
233
+ dilation=dilation,
234
+ flip_tiled_dims=True,
235
+ )
236
+ key_perm, _, k_shape = token_permute_operation(
237
+ key, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=True
238
+ )
239
+ value_perm, _, v_shape = token_permute_operation(
240
+ value, tile_shape=kv_tile_shape, dilation=dilation, flip_tiled_dims=True
241
+ )
242
+
243
+ assert q_shape == o_shape == d_o_shape
244
+ assert k_shape == v_shape
245
+ kv_shape = k_shape
246
+ # Token permute end
247
+
248
+ query_perm = query_perm.contiguous()
249
+ key_perm = key_perm.contiguous()
250
+ value_perm = value_perm.contiguous()
251
+ output_perm = output_perm.contiguous()
252
+ d_output_perm = d_output_perm.contiguous()
253
+ logsumexp_perm = logsumexp_perm.squeeze(-1)
254
+
255
+ d_query_perm, d_key_perm, d_value_perm = BACKWARD_OPS[na_dim](
256
+ query_perm,
257
+ key_perm,
258
+ value_perm,
259
+ output_perm,
260
+ d_output_perm,
261
+ logsumexp_perm,
262
+ kernel_size,
263
+ stride,
264
+ dilation,
265
+ is_causal,
266
+ scale,
267
+ q_shape,
268
+ kv_shape,
269
+ qkv_shape,
270
+ q_tile_shape,
271
+ kv_tile_shape,
272
+ )
273
+
274
+ # Token un-permute begin
275
+ d_query = token_unpermute_operation(
276
+ d_query_perm,
277
+ token_layout_shape=qkv_shape,
278
+ tile_shape=q_tile_shape,
279
+ dilation=dilation,
280
+ flip_tiled_dims=True,
281
+ )
282
+ d_key = token_unpermute_operation(
283
+ d_key_perm,
284
+ token_layout_shape=qkv_shape,
285
+ tile_shape=kv_tile_shape,
286
+ dilation=dilation,
287
+ flip_tiled_dims=True,
288
+ )
289
+ d_value = token_unpermute_operation(
290
+ d_value_perm,
291
+ token_layout_shape=qkv_shape,
292
+ tile_shape=kv_tile_shape,
293
+ dilation=dilation,
294
+ flip_tiled_dims=True,
295
+ )
296
+ # Token un-permute end
297
+
298
+ assert d_query.shape == query.shape
299
+ assert d_key.shape == key.shape
300
+ assert d_value.shape == value.shape
301
+
302
+ return (
303
+ d_query,
304
+ d_key,
305
+ d_value,
306
+ None,
307
+ None,
308
+ None,
309
+ None,
310
+ None,
311
+ None,
312
+ None,
313
+ )
314
+
315
+ return CutlassHopperFnaGenericAutogradFn
316
+
317
+
318
+ CutlassHopperFna1DAutogradFn = make_cutlass_hopper_fna_autograd_fn(1)
319
+ CutlassHopperFna2DAutogradFn = make_cutlass_hopper_fna_autograd_fn(2)
320
+ CutlassHopperFna3DAutogradFn = make_cutlass_hopper_fna_autograd_fn(3)
321
+
322
+
323
+ CutlassHopperFNAAutogradFns = {
324
+ 1: CutlassHopperFna1DAutogradFn,
325
+ 2: CutlassHopperFna2DAutogradFn,
326
+ 3: CutlassHopperFna3DAutogradFn,
327
+ }
328
+
329
+
330
+ def cutlass_hopper_fna_generic(
331
+ query: Tensor,
332
+ key: Tensor,
333
+ value: Tensor,
334
+ kernel_size: DimensionTypeOrDed,
335
+ stride: DimensionTypeOrDed = 1,
336
+ dilation: DimensionTypeOrDed = 1,
337
+ is_causal: Optional[CausalArgTypeOrDed] = False,
338
+ scale: Optional[float] = None,
339
+ q_tile_shape: Optional[DimensionType] = None,
340
+ kv_tile_shape: Optional[DimensionType] = None,
341
+ backward_q_tile_shape: Optional[DimensionType] = None,
342
+ backward_kv_tile_shape: Optional[DimensionType] = None,
343
+ kernel_schedule: Optional[KernelSchedule] = None,
344
+ return_lse: bool = False,
345
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
346
+
347
+ na_tensor_checks(
348
+ query, key, value, must_match_head_dims=True, supports_gqa_mqa=True
349
+ )
350
+
351
+ assert can_run_cutlass_hopper_fna(query, key, value, raise_error=True)
352
+
353
+ na_dim = query.dim() - 3 # batch, heads, head_dim
354
+
355
+ kernel_size, stride, dilation, is_causal = check_all_args(
356
+ na_dim, kernel_size, stride, dilation, is_causal
357
+ )
358
+
359
+ check_args_against_input(
360
+ query,
361
+ kernel_size=kernel_size,
362
+ stride=stride,
363
+ dilation=dilation,
364
+ is_causal=is_causal,
365
+ )
366
+
367
+ forward_config = check_cutlass_hopper_fna_forward_config(
368
+ input_tensor=query,
369
+ q_tile_shape=q_tile_shape,
370
+ kv_tile_shape=kv_tile_shape,
371
+ kernel_schedule=kernel_schedule,
372
+ )
373
+ backward_config = check_cutlass_hopper_fna_backward_config(
374
+ input_tensor=query,
375
+ q_tile_shape=backward_q_tile_shape,
376
+ kv_tile_shape=backward_kv_tile_shape,
377
+ )
378
+
379
+ scale = scale or query.shape[-1] ** -0.5
380
+
381
+ # GQA/MQA is not supported by the kernel; only allowed via graph transform
382
+ is_gqa = query.shape[-2] != key.shape[-2]
383
+ if is_gqa:
384
+ heads = query.shape[-2]
385
+ heads_kv = key.shape[-2]
386
+ assert key.shape[-2] == value.shape[-2]
387
+ assert heads >= heads_kv
388
+ assert heads % heads_kv == 0
389
+ h_k = heads // heads_kv
390
+
391
+ key = torch.repeat_interleave(key, repeats=h_k, dim=-2, output_size=heads)
392
+ value = torch.repeat_interleave(value, repeats=h_k, dim=-2, output_size=heads)
393
+
394
+ output, lse = CutlassHopperFNAAutogradFns[na_dim].apply(
395
+ query,
396
+ key,
397
+ value,
398
+ kernel_size,
399
+ stride,
400
+ dilation,
401
+ is_causal,
402
+ scale,
403
+ forward_config,
404
+ backward_config,
405
+ )
406
+
407
+ if return_lse:
408
+ return output, lse
409
+
410
+ return output
411
+
412
+
413
+ def na1d_cutlass_hopper_fna(
414
+ query: Tensor,
415
+ key: Tensor,
416
+ value: Tensor,
417
+ kernel_size: Dimension1DTypeOrDed,
418
+ stride: Dimension1DTypeOrDed = 1,
419
+ dilation: Dimension1DTypeOrDed = 1,
420
+ is_causal: Optional[CausalArg1DTypeOrDed] = False,
421
+ scale: Optional[float] = None,
422
+ q_tile_shape: Optional[Dimension1DType] = None,
423
+ kv_tile_shape: Optional[Dimension1DType] = None,
424
+ backward_q_tile_shape: Optional[Dimension1DType] = None,
425
+ backward_kv_tile_shape: Optional[Dimension1DType] = None,
426
+ kernel_schedule: Optional[KernelSchedule] = None,
427
+ return_lse: bool = False,
428
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
429
+ return cutlass_hopper_fna_generic(
430
+ query=query,
431
+ key=key,
432
+ value=value,
433
+ kernel_size=kernel_size,
434
+ stride=stride,
435
+ dilation=dilation,
436
+ is_causal=is_causal,
437
+ scale=scale,
438
+ q_tile_shape=q_tile_shape,
439
+ kv_tile_shape=kv_tile_shape,
440
+ backward_q_tile_shape=backward_q_tile_shape,
441
+ backward_kv_tile_shape=backward_kv_tile_shape,
442
+ kernel_schedule=kernel_schedule,
443
+ return_lse=return_lse,
444
+ )
445
+
446
+
447
+ def na2d_cutlass_hopper_fna(
448
+ query: Tensor,
449
+ key: Tensor,
450
+ value: Tensor,
451
+ kernel_size: Dimension2DTypeOrDed,
452
+ stride: Dimension2DTypeOrDed = 1,
453
+ dilation: Dimension2DTypeOrDed = 1,
454
+ is_causal: Optional[CausalArg2DTypeOrDed] = False,
455
+ scale: Optional[float] = None,
456
+ q_tile_shape: Optional[Dimension2DType] = None,
457
+ kv_tile_shape: Optional[Dimension2DType] = None,
458
+ backward_q_tile_shape: Optional[Dimension2DType] = None,
459
+ backward_kv_tile_shape: Optional[Dimension2DType] = None,
460
+ kernel_schedule: Optional[KernelSchedule] = None,
461
+ return_lse: bool = False,
462
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
463
+ return cutlass_hopper_fna_generic(
464
+ query=query,
465
+ key=key,
466
+ value=value,
467
+ kernel_size=kernel_size,
468
+ stride=stride,
469
+ dilation=dilation,
470
+ is_causal=is_causal,
471
+ scale=scale,
472
+ q_tile_shape=q_tile_shape,
473
+ kv_tile_shape=kv_tile_shape,
474
+ backward_q_tile_shape=backward_q_tile_shape,
475
+ backward_kv_tile_shape=backward_kv_tile_shape,
476
+ kernel_schedule=kernel_schedule,
477
+ return_lse=return_lse,
478
+ )
479
+
480
+
481
+ def na3d_cutlass_hopper_fna(
482
+ query: Tensor,
483
+ key: Tensor,
484
+ value: Tensor,
485
+ kernel_size: Dimension3DTypeOrDed,
486
+ stride: Dimension3DTypeOrDed = 1,
487
+ dilation: Dimension3DTypeOrDed = 1,
488
+ is_causal: Optional[CausalArg3DTypeOrDed] = False,
489
+ scale: Optional[float] = None,
490
+ q_tile_shape: Optional[Dimension3DType] = None,
491
+ kv_tile_shape: Optional[Dimension3DType] = None,
492
+ backward_q_tile_shape: Optional[Dimension3DType] = None,
493
+ backward_kv_tile_shape: Optional[Dimension3DType] = None,
494
+ kernel_schedule: Optional[KernelSchedule] = None,
495
+ return_lse: bool = False,
496
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
497
+ return cutlass_hopper_fna_generic(
498
+ query=query,
499
+ key=key,
500
+ value=value,
501
+ kernel_size=kernel_size,
502
+ stride=stride,
503
+ dilation=dilation,
504
+ is_causal=is_causal,
505
+ scale=scale,
506
+ q_tile_shape=q_tile_shape,
507
+ kv_tile_shape=kv_tile_shape,
508
+ backward_q_tile_shape=backward_q_tile_shape,
509
+ backward_kv_tile_shape=backward_kv_tile_shape,
510
+ kernel_schedule=kernel_schedule,
511
+ return_lse=return_lse,
512
+ )
build/torch210-cu126-aarch64-linux/backends/reference.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import functools
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ from torch import Tensor
28
+ from torch.amp import custom_bwd, custom_fwd
29
+ from torch.autograd import Function
30
+
31
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
32
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
33
+
34
+ from .._libnatten import (
35
+ reference_na1d_backward,
36
+ reference_na1d_forward,
37
+ reference_na2d_backward,
38
+ reference_na2d_forward,
39
+ reference_na3d_backward,
40
+ reference_na3d_forward,
41
+ )
42
+ from .._types import (
43
+ CausalArg1DTypeOrDed,
44
+ CausalArg2DTypeOrDed,
45
+ CausalArg3DTypeOrDed,
46
+ CausalArgType,
47
+ CausalArgTypeOrDed,
48
+ Dimension1DTypeOrDed,
49
+ Dimension2DTypeOrDed,
50
+ Dimension3DTypeOrDed,
51
+ DimensionType,
52
+ DimensionTypeOrDed,
53
+ NoneType,
54
+ )
55
+ from ..utils import log
56
+ from ..utils.checks import (
57
+ additional_kv_tensor_checks,
58
+ check_all_args,
59
+ check_args_against_input,
60
+ na_tensor_checks,
61
+ )
62
+
63
+ logger = log.get_logger(__name__)
64
+
65
+
66
+ def make_reference_fna_autograd_fn(na_dim):
67
+ assert na_dim in [1, 2, 3]
68
+
69
+ FORWARD_OPS = {
70
+ 1: reference_na1d_forward,
71
+ 2: reference_na2d_forward,
72
+ 3: reference_na3d_forward,
73
+ }
74
+
75
+ BACKWARD_OPS = {
76
+ 1: reference_na1d_backward,
77
+ 2: reference_na2d_backward,
78
+ 3: reference_na3d_backward,
79
+ }
80
+
81
+ class ReferenceFnaGenericAutogradFn(Function):
82
+ @staticmethod
83
+ @amp_fwd
84
+ def forward(
85
+ ctx,
86
+ query: Tensor,
87
+ key: Tensor,
88
+ value: Tensor,
89
+ kernel_size: DimensionType,
90
+ stride: DimensionType,
91
+ dilation: DimensionType,
92
+ is_causal: CausalArgType,
93
+ scale: float,
94
+ qkv_shape: DimensionType,
95
+ num_extra_kv: int,
96
+ ) -> Tuple[Tensor, Tensor]:
97
+ kernel_size, stride, dilation, is_causal = check_all_args(
98
+ na_dim, kernel_size, stride, dilation, is_causal
99
+ )
100
+
101
+ query = query.contiguous()
102
+ key = key.contiguous()
103
+ value = value.contiguous()
104
+
105
+ assert query.dim() == value.dim() == 4
106
+ assert query.shape[0] == value.shape[0]
107
+
108
+ output, logsumexp = FORWARD_OPS[na_dim](
109
+ query,
110
+ key,
111
+ value,
112
+ kernel_size,
113
+ stride,
114
+ dilation,
115
+ is_causal,
116
+ scale,
117
+ qkv_shape,
118
+ num_extra_kv,
119
+ )
120
+
121
+ ctx.save_for_backward(query, key, value, logsumexp, output)
122
+ ctx.kernel_size = kernel_size
123
+ ctx.stride = stride
124
+ ctx.dilation = dilation
125
+ ctx.is_causal = is_causal
126
+ ctx.scale = scale
127
+ ctx.qkv_shape = qkv_shape
128
+ ctx.num_extra_kv = num_extra_kv
129
+
130
+ return output, logsumexp
131
+
132
+ @staticmethod
133
+ @amp_bwd
134
+ def backward(ctx, grad_out: Tensor, grad_lse: Tensor) -> Tuple[
135
+ Tensor,
136
+ Tensor,
137
+ Tensor,
138
+ NoneType,
139
+ NoneType,
140
+ NoneType,
141
+ NoneType,
142
+ NoneType,
143
+ NoneType,
144
+ NoneType,
145
+ ]:
146
+ query, key, value, logsumexp, output = ctx.saved_tensors
147
+ d_output = grad_out.contiguous()
148
+
149
+ d_query, d_key, d_value = BACKWARD_OPS[na_dim](
150
+ query,
151
+ key,
152
+ value,
153
+ output,
154
+ d_output,
155
+ logsumexp,
156
+ ctx.kernel_size,
157
+ ctx.stride,
158
+ ctx.dilation,
159
+ ctx.is_causal,
160
+ ctx.scale,
161
+ ctx.qkv_shape,
162
+ ctx.num_extra_kv,
163
+ )
164
+
165
+ return d_query, d_key, d_value, None, None, None, None, None, None, None
166
+
167
+ return ReferenceFnaGenericAutogradFn
168
+
169
+
170
+ ReferenceFna1DAutogradFn = make_reference_fna_autograd_fn(1)
171
+ ReferenceFna2DAutogradFn = make_reference_fna_autograd_fn(2)
172
+ ReferenceFna3DAutogradFn = make_reference_fna_autograd_fn(3)
173
+
174
+
175
+ ReferenceFnaAutogradFns = {
176
+ 1: ReferenceFna1DAutogradFn,
177
+ 2: ReferenceFna2DAutogradFn,
178
+ 3: ReferenceFna3DAutogradFn,
179
+ }
180
+
181
+
182
+ def reference_fna_generic(
183
+ query: Tensor,
184
+ key: Tensor,
185
+ value: Tensor,
186
+ kernel_size: DimensionTypeOrDed,
187
+ stride: DimensionTypeOrDed = 1,
188
+ dilation: DimensionTypeOrDed = 1,
189
+ is_causal: Optional[CausalArgTypeOrDed] = False,
190
+ scale: Optional[float] = None,
191
+ additional_keys: Optional[Tensor] = None,
192
+ additional_values: Optional[Tensor] = None,
193
+ return_lse: bool = False,
194
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
195
+
196
+ na_tensor_checks(
197
+ query, key, value, must_match_head_dims=False, supports_gqa_mqa=True
198
+ )
199
+ additional_kv_tensor_checks(
200
+ query,
201
+ key,
202
+ value,
203
+ additional_keys,
204
+ additional_values,
205
+ must_match_head_dims=False,
206
+ supports_gqa_mqa=True,
207
+ )
208
+
209
+ na_dim = query.dim() - 3 # batch, heads, head_dim
210
+
211
+ assert na_dim in [1, 2, 3]
212
+
213
+ kernel_size, stride, dilation, is_causal = check_all_args(
214
+ na_dim, kernel_size, stride, dilation, is_causal
215
+ )
216
+
217
+ check_args_against_input(
218
+ query,
219
+ kernel_size=kernel_size,
220
+ stride=stride,
221
+ dilation=dilation,
222
+ is_causal=is_causal,
223
+ )
224
+
225
+ scale = scale or query.shape[-1] ** -0.5
226
+
227
+ qkv_shape = query.shape[1 : 1 + na_dim]
228
+
229
+ query = query.flatten(1, na_dim)
230
+ key = key.flatten(1, na_dim)
231
+ value = value.flatten(1, na_dim)
232
+
233
+ num_extra_kv = 0
234
+ if additional_keys is not None and additional_values is not None:
235
+ num_extra_kv = additional_keys.shape[1]
236
+ key = torch.cat([key, additional_keys], dim=1)
237
+ value = torch.cat([value, additional_values], dim=1)
238
+
239
+ output, lse = ReferenceFnaAutogradFns[na_dim].apply(
240
+ query,
241
+ key,
242
+ value,
243
+ kernel_size,
244
+ stride,
245
+ dilation,
246
+ is_causal,
247
+ scale,
248
+ qkv_shape,
249
+ num_extra_kv,
250
+ )
251
+ output = output.reshape(
252
+ query.shape[0], *qkv_shape, query.shape[-2], value.shape[-1]
253
+ )
254
+ lse = lse.reshape(query.shape[0], *qkv_shape, query.shape[-2])
255
+
256
+ if return_lse:
257
+ return output, lse
258
+
259
+ return output
260
+
261
+
262
+ def na1d_reference(
263
+ query: Tensor,
264
+ key: Tensor,
265
+ value: Tensor,
266
+ kernel_size: Dimension1DTypeOrDed,
267
+ stride: Dimension1DTypeOrDed = 1,
268
+ dilation: Dimension1DTypeOrDed = 1,
269
+ is_causal: Optional[CausalArg1DTypeOrDed] = False,
270
+ scale: Optional[float] = None,
271
+ additional_keys: Optional[Tensor] = None,
272
+ additional_values: Optional[Tensor] = None,
273
+ return_lse: bool = False,
274
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
275
+ return reference_fna_generic(
276
+ query=query,
277
+ key=key,
278
+ value=value,
279
+ kernel_size=kernel_size,
280
+ stride=stride,
281
+ dilation=dilation,
282
+ is_causal=is_causal,
283
+ scale=scale,
284
+ additional_keys=additional_keys,
285
+ additional_values=additional_values,
286
+ return_lse=return_lse,
287
+ )
288
+
289
+
290
+ def na2d_reference(
291
+ query: Tensor,
292
+ key: Tensor,
293
+ value: Tensor,
294
+ kernel_size: Dimension2DTypeOrDed,
295
+ stride: Dimension2DTypeOrDed = 1,
296
+ dilation: Dimension2DTypeOrDed = 1,
297
+ is_causal: Optional[CausalArg2DTypeOrDed] = False,
298
+ scale: Optional[float] = None,
299
+ additional_keys: Optional[Tensor] = None,
300
+ additional_values: Optional[Tensor] = None,
301
+ return_lse: bool = False,
302
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
303
+ return reference_fna_generic(
304
+ query=query,
305
+ key=key,
306
+ value=value,
307
+ kernel_size=kernel_size,
308
+ stride=stride,
309
+ dilation=dilation,
310
+ is_causal=is_causal,
311
+ scale=scale,
312
+ additional_keys=additional_keys,
313
+ additional_values=additional_values,
314
+ return_lse=return_lse,
315
+ )
316
+
317
+
318
+ def na3d_reference(
319
+ query: Tensor,
320
+ key: Tensor,
321
+ value: Tensor,
322
+ kernel_size: Dimension3DTypeOrDed,
323
+ stride: Dimension3DTypeOrDed = 1,
324
+ dilation: Dimension3DTypeOrDed = 1,
325
+ is_causal: Optional[CausalArg3DTypeOrDed] = False,
326
+ scale: Optional[float] = None,
327
+ additional_keys: Optional[Tensor] = None,
328
+ additional_values: Optional[Tensor] = None,
329
+ return_lse: bool = False,
330
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
331
+ return reference_fna_generic(
332
+ query=query,
333
+ key=key,
334
+ value=value,
335
+ kernel_size=kernel_size,
336
+ stride=stride,
337
+ dilation=dilation,
338
+ is_causal=is_causal,
339
+ scale=scale,
340
+ additional_keys=additional_keys,
341
+ additional_values=additional_values,
342
+ return_lse=return_lse,
343
+ )
build/torch210-cu126-aarch64-linux/context.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ from enum import Enum
24
+
25
+ import torch
26
+
27
+ from .utils import log
28
+
29
+ logger = log.get_logger(__name__)
30
+
31
+
32
+ class MemoryUsagePreference(Enum):
33
+ Default = 0
34
+ Strict = 1
35
+ Unrestricted = 2
36
+
37
+
38
+ class NattenContext:
39
+ is_deterministic_mode_enabled: bool = False
40
+ is_kv_parallelism_enabled: bool = True
41
+ training_memory_preference: MemoryUsagePreference = MemoryUsagePreference.Default
42
+ flex_compile_allowed: bool = False
43
+ flex_compile_backprop_allowed: bool = False
44
+
45
+ @staticmethod
46
+ def reset():
47
+ NattenContext.is_deterministic_mode_enabled = False
48
+ NattenContext.is_kv_parallelism_enabled = True
49
+ NattenContext.training_memory_preference = MemoryUsagePreference.Default
50
+ NattenContext.flex_compile_allowed = False
51
+ NattenContext.flex_compile_backprop_allowed = False
52
+
53
+
54
+ def set_memory_usage_preference(pref: str = "default"):
55
+ """Sets memory usage preference for KV parallelism in `"cutlass-fna"` and `"cutlass-fmha"`
56
+ backends.
57
+
58
+ Args:
59
+ pref: Choices are `"default"`, `"strict"`, and `"unrestricted"`.
60
+ """
61
+ if pref == "default":
62
+ NattenContext.training_memory_preference = MemoryUsagePreference.Default
63
+ elif pref == "strict":
64
+ NattenContext.training_memory_preference = MemoryUsagePreference.Strict
65
+ elif pref == "unrestricted":
66
+ NattenContext.training_memory_preference = MemoryUsagePreference.Unrestricted
67
+ else:
68
+ raise ValueError(
69
+ "natten.set_memory_usage_preference allows only one of three settings: "
70
+ "`default`, `strict`, and `unrestricted`."
71
+ )
72
+
73
+
74
+ def get_memory_usage_preference() -> MemoryUsagePreference:
75
+ return NattenContext.training_memory_preference
76
+
77
+
78
+ def is_memory_usage_default() -> bool:
79
+ """Returns whether memory usage preference for KV parallelism in `"cutlass-fna"` and
80
+ `"cutlass-fmha"` backends is the default setting.
81
+ """
82
+ return get_memory_usage_preference() == MemoryUsagePreference.Default
83
+
84
+
85
+ def is_memory_usage_strict() -> bool:
86
+ """Returns whether memory usage preference for KV parallelism in `"cutlass-fna"` and
87
+ `"cutlass-fmha"` backends is the *restricted* setting.
88
+ """
89
+ return get_memory_usage_preference() == MemoryUsagePreference.Strict
90
+
91
+
92
+ def is_memory_usage_unrestricted() -> bool:
93
+ """Returns whether memory usage preference for KV parallelism in `"cutlass-fna"` and
94
+ `"cutlass-fmha"` backends is the *unrestricted* setting.
95
+ """
96
+ return get_memory_usage_preference() == MemoryUsagePreference.Unrestricted
97
+
98
+
99
+ def use_deterministic_algorithms(mode: bool = True):
100
+ NattenContext.is_deterministic_mode_enabled = mode
101
+ if mode:
102
+ logger.warning(
103
+ "You're enabling NATTEN's deterministic mode. This mode does not "
104
+ "support auto-tuning, or training with positional biases. "
105
+ "For more information please refer to https://github.com/SHI-Labs/NATTEN/tree/main/docs"
106
+ )
107
+
108
+
109
+ def are_deterministic_algorithms_enabled() -> bool:
110
+ return NattenContext.is_deterministic_mode_enabled
111
+
112
+
113
+ def use_kv_parallelism_in_fused_na(mode: bool = True):
114
+ """Sets guards for using KV Parallelism in backpropagation in `"cutlass-fna"`/`"cutlass-fmha"`
115
+ backends.
116
+
117
+ Warning:
118
+ Disabling KV parallelism can significantly slow down training, particularly in
119
+ small-batch/head and large-token problems.
120
+
121
+ Args:
122
+ mode: If `True`, allows KV parallelism (default setting), and otherwise disables it.
123
+ """
124
+ if not mode:
125
+ NattenContext.is_kv_parallelism_enabled = False
126
+ return
127
+
128
+ if torch.are_deterministic_algorithms_enabled():
129
+ logger.warning(
130
+ "Attempted to enable KV parallelism in FNA, which is non-deterministic, "
131
+ "but PyTorch's deterministic flag has been enabled. Ignoring..."
132
+ )
133
+ return
134
+
135
+ if are_deterministic_algorithms_enabled():
136
+ raise RuntimeError(
137
+ "You enabled NATTEN's deterministic mode, but attempted to "
138
+ "enable KV parallelism, which results in non-determinism. "
139
+ )
140
+
141
+ NattenContext.is_kv_parallelism_enabled = True
142
+
143
+
144
+ def is_kv_parallelism_in_fused_na_enabled() -> bool:
145
+ """Returns whether KV parallelism in `"cutlass-fna"` and `"cutlass-fmha"` backends is enabled."""
146
+ return NattenContext.is_kv_parallelism_enabled
147
+
148
+
149
+ def is_flex_compile_allowed() -> bool:
150
+ """Returns whether compilation is allowed in `"flex-fna"` and `"flex-fmha"` backends."""
151
+ return NattenContext.flex_compile_allowed
152
+
153
+
154
+ def is_flex_compile_backprop_allowed() -> bool:
155
+ """Returns whether compilation for backpropagation is allowed in `"flex-fna"` and `"flex-fmha"`
156
+ backends.
157
+ """
158
+ return NattenContext.flex_compile_backprop_allowed
159
+
160
+
161
+ def allow_flex_compile(mode: bool = True, backprop: bool = False):
162
+ """Sets guards for Flex Attention + `torch.compile`.
163
+
164
+ Allows using our Flex FNA / Flex FMHA backends with `torch.compile`, meaning you can
165
+ pass `torch_compile=True` to the `na{1,2,3}d` or `attention` operation, along with
166
+ `backend="flex-fna"`/`backend="flex-fmha"`, and NATTEN will compile the block-sparse mask, as
167
+ well as the attention operation using `torch.compile` for you.
168
+
169
+ Warning:
170
+ We have been *unable to verify the correctness* of this setting under all of our use
171
+ cases. We are working on raising this issue with PyTorch directly, but until then we strongly
172
+ recommend exercising caution when using this feature.
173
+
174
+ Danger: backprop=True is strongly discouraged!
175
+ Allowing `torch.compile` for backpropagation (detected by checking
176
+ `tensor.requires_grad`) is guarded separately. We strongly recommend NOT using this setting, as
177
+ it can impact your training results.
178
+
179
+ Args:
180
+ mode: If `True`, enable compilation for forward pass, otherwise disable.
181
+ backprop: If `True`, assuming compilation for forward pass is allowed, enable compilation
182
+ for backward pass, otherwise disable.
183
+ """
184
+ if not mode:
185
+ NattenContext.flex_compile_allowed = False
186
+ NattenContext.flex_compile_backprop_allowed = False
187
+
188
+ if not NattenContext.flex_compile_allowed:
189
+ logger.warning(
190
+ "You are enabling Flex Attention compilation in NATTEN. "
191
+ "NATTEN does not allow this by default, because we cannot verify Flex's correctness in all "
192
+ "scenarios through NATTEN's tests. By choosing to override this, you acknowledge that your "
193
+ "results may be affected significantly. If this was not intended, please call "
194
+ "natten.disable_flex_compile()"
195
+ ""
196
+ )
197
+
198
+ NattenContext.flex_compile_allowed = True
199
+
200
+ if backprop:
201
+ if not NattenContext.flex_compile_backprop_allowed:
202
+ logger.warning(
203
+ "You are enabling using compiled Flex Attention to backpropagate. "
204
+ "NATTEN does not allow this by default, because we cannot verify Flex's correctness in all "
205
+ "scenarios through NATTEN's tests, and it is HIGHLY discouraged. By choosing to override "
206
+ "this, you acknowledge that your results may be heavily impacted significantly. "
207
+ "If this was not intended, please call "
208
+ "natten.disable_flex_compile_backprop()"
209
+ ""
210
+ )
211
+ NattenContext.flex_compile_backprop_allowed = True
212
+
213
+
214
+ def allow_flex_compile_backprop(mode: bool = True):
215
+ """Sets guards for Flex Attention + `torch.compile` for backpropagation only.
216
+
217
+ Args:
218
+ mode: If `True`, enable compilation for backprop (assuming forward compilation is already
219
+ enabled), otherwise disable.
220
+ """
221
+ return allow_flex_compile(is_flex_compile_allowed(), mode)
222
+
223
+
224
+ def disable_flex_compile():
225
+ """Disallow Flex Attention + `torch.compile` entirely."""
226
+ return allow_flex_compile(False)
227
+
228
+
229
+ def disable_flex_compile_backprop():
230
+ """Disallow Flex Attention + `torch.compile` for backpropagation entirely."""
231
+ return allow_flex_compile(is_flex_compile_allowed(), False)
build/torch210-cu126-aarch64-linux/functional.py ADDED
@@ -0,0 +1,1151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ from typing import Dict, Optional, Tuple, Union
24
+
25
+ import torch
26
+ from torch import Tensor
27
+
28
+ from .attn_merge import merge_attentions
29
+ from .backends import (
30
+ choose_backend,
31
+ choose_fmha_backend,
32
+ cutlass_blackwell_fmha,
33
+ cutlass_blackwell_fna_generic,
34
+ cutlass_fmha,
35
+ cutlass_fna_generic,
36
+ cutlass_hopper_fmha,
37
+ cutlass_hopper_fna_generic,
38
+ flex_fmha,
39
+ flex_fna_generic,
40
+ )
41
+ from ._types import (
42
+ CausalArg1DTypeOrDed,
43
+ CausalArg2DTypeOrDed,
44
+ CausalArg3DTypeOrDed,
45
+ CausalArgTypeOrDed,
46
+ Dimension1DType,
47
+ Dimension1DTypeOrDed,
48
+ Dimension2DType,
49
+ Dimension2DTypeOrDed,
50
+ Dimension3DType,
51
+ Dimension3DTypeOrDed,
52
+ DimensionType,
53
+ DimensionTypeOrDed,
54
+ KernelSchedule,
55
+ )
56
+ from .utils import log
57
+ from .utils.checks import (
58
+ additional_kv_tensor_checks,
59
+ check_all_args,
60
+ check_args_against_input,
61
+ check_kernel_schedule,
62
+ fmha_tensor_checks,
63
+ is_self_attention,
64
+ na_tensor_checks,
65
+ varlen_tensor_checks,
66
+ )
67
+
68
+ logger = log.get_logger(__name__)
69
+
70
+
71
+ # Standard Attention
72
+
73
+
74
+ def attention(
75
+ query: Tensor,
76
+ key: Tensor,
77
+ value: Tensor,
78
+ is_causal: bool = False,
79
+ scale: Optional[float] = None,
80
+ # varlen parameters
81
+ seqlens_Q: Optional[Tensor] = None,
82
+ seqlens_KV: Optional[Tensor] = None,
83
+ cumulative_seqlen_Q: Optional[Tensor] = None,
84
+ cumulative_seqlen_KV: Optional[Tensor] = None,
85
+ max_seqlen_Q: Optional[int] = None,
86
+ max_seqlen_KV: Optional[int] = None,
87
+ # backend parameters
88
+ backend: Optional[str] = None,
89
+ q_tile_size: Optional[int] = None,
90
+ kv_tile_size: Optional[int] = None,
91
+ backward_q_tile_size: Optional[int] = None,
92
+ backward_kv_tile_size: Optional[int] = None,
93
+ backward_kv_splits: Optional[int] = None,
94
+ backward_use_pt_reduction: bool = False,
95
+ run_persistent_kernel: bool = True,
96
+ kernel_schedule: Optional[Union[str, KernelSchedule]] = None,
97
+ torch_compile: bool = False,
98
+ return_lse: bool = False,
99
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
100
+ """Runs standard dot product attention.
101
+
102
+ This operation is used to implement neighborhood cross attention, in which we allow every
103
+ token to interact with some additional context (`additional_keys` and `additional_values`
104
+ tensors in [na1d][natten.na1d], [na2d][natten.na2d], and [na3d][natten.na3d]).
105
+ This operator is also used as a fast path for cases where neighborhood attention is equivalent
106
+ to self attention (not causal along any dims, and `kernel_size` is equal to the number of input
107
+ tokens).
108
+
109
+ This operation does not call into PyTorch's SDPA, and only runs one of the NATTEN backends
110
+ (`cutlass-fmha`, `hopper-fmha`, `blackwell-fmha`, `flex-fmha`). Reasons for that include being
111
+ able to control performance-related arguments, return logsumexp, and more.
112
+ For more information refer to [backends](backends.md).
113
+
114
+ Causal mask, and Variable length (varlen) Attention are also supported in some backends
115
+ (`cutlass-fmha` and `blackwell-fmha`).
116
+
117
+ Varlen Attention is only supported for the sequence-packed layout: QKV tensors have batch size
118
+ 1, and tokens from different batches are concatenated without any padding along the sequence
119
+ dimension. Sequence lengths for different batches can be provided in two ways:
120
+ 1. `seqlens_Q` and `seqlens_KV` (less efficient): only provide the sequence lengths as
121
+ integer tensors (must be on the same device as QKV), and NATTEN will compute cumulative
122
+ and maximum sequence lengths on each call.
123
+ This is **incompatible** with full-graph `torch.compile` since it requires a
124
+ synchronization.
125
+ 2. `cumulative_seqlen_{Q,KV}` and `max_seqlen_{Q,KV}` (more efficient):
126
+ compute cumulative and maximum sequence lengths. `cumulative_seqlen_{Q,KV}` are integer
127
+ tensors on the same device as QKV containing the cumulative sum of `seqlens_{Q,KV}`,
128
+ with an additional `0` element in the beginning, therefore sized `batch+1`.
129
+ `max_seqlen_{Q,KV}` are integers (not Tensors) that represent the maximum sequence
130
+ lengths for Q and KV among all sequence batches.
131
+ You can use `natten.utils.varlen.generate_varlen_parameters` to generate these
132
+ parameters:
133
+ ```python3
134
+ from .utils.varlen import generate_varlen_parameters
135
+ (
136
+ cumulative_seqlen_Q,
137
+ cumulative_seqlen_KV,
138
+ max_seqlen_Q,
139
+ max_seqlen_KV,
140
+ ) = generate_varlen_parameters(q, k, v, seqlens_Q, seqlens_KV)
141
+ ```
142
+ As long as `generate_varlen_parameters` is called ahead of torch.compiling the model, it
143
+ is supported without any graph breaks.
144
+
145
+ GQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fmha` and
146
+ `flex-fmha` support GQA/MQA natively, and `cutlass-fmha` and `hopper-fmha` support it with
147
+ explicit repeats (increases memory usage and runtime).
148
+
149
+ Parameters:
150
+ query (Tensor): 4-D query tensor, with the heads last layout
151
+ (`[batch, seqlen, heads, head_dim]`)
152
+
153
+ key (Tensor): 4-D key tensor, with the heads last layout
154
+ (`[batch, seqlen_kv, heads_kv, head_dim]`)
155
+
156
+ value (Tensor): 4-D value tensor, with the heads last layout
157
+ (`[batch, seqlen_kv, heads_kv, head_dim_v]`)
158
+
159
+ is_causal (bool): Toggle causal masking. Defaults to `False` (bi-directional).
160
+
161
+ scale (float): Attention scale. Defaults to `head_dim ** -0.5`.
162
+
163
+ seqlens_Q (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch`
164
+ indicating the number of query tokens in each batch. Must be passed together with
165
+ `seqlens_KV`.
166
+
167
+ seqlens_KV (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch`
168
+ indicating the number of key/value tokens in each batch. Must be passed together with
169
+ `seqlens_Q`.
170
+
171
+ cumulative_seqlen_Q (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch + 1`
172
+ indicating the cumulative sum of number of query tokens in each batch, with an
173
+ additional 0 element in the beginning. Must be passed together with
174
+ `cumulative_seqlen_KV` and `max_seqlen_{Q,KV}`.
175
+
176
+ cumulative_seqlen_KV (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch + 1`
177
+ indicating the cumulative sum of number of key/value tokens in each batch, with an
178
+ additional 0 element in the beginning. Must be passed together with
179
+ `cumulative_seqlen_Q` and `max_seqlen_{Q,KV}`.
180
+
181
+ max_seqlen_Q (Optional[int]): (varlen) Optional integer indicating the maximum query
182
+ sequence length in all batches. Must be passed together with `cumulative_seqlen_{Q,KV}`
183
+ and `max_seqlen_KV`.
184
+
185
+ max_seqlen_KV (Optional[int]): (varlen) Optional integer indicating the maximum key/value
186
+ sequence length in all batches. Must be passed together with `cumulative_seqlen_{Q,KV}`
187
+ and `max_seqlen_Q`.
188
+
189
+ Other Parameters:
190
+ backend (str): Backend implementation to run with. Choices are: `None` (pick the best
191
+ available one), `"cutlass-fmha"`, `"hopper-fmha"`, `"blackwell-fmha"`, `"flex-fmha"`.
192
+ Refer to [backends](backends.md) for more information.
193
+
194
+ q_tile_size (int): Tile size along query sequence length in the forward pass kernel.
195
+ You can use [profiler](profiler.md) to find valid choices for your use case.
196
+
197
+ kv_tile_size (int): Tile size along key/value sequence length in the forward pass kernel.
198
+ You can use [profiler](profiler.md) to find valid choices for your use case.
199
+
200
+ backward_q_tile_size (int): Tile size along query sequence length in the backward pass
201
+ kernel. This is ignored by `"flex-fmha"`.
202
+ You can use [profiler](profiler.md) to find valid choices for your use case.
203
+
204
+ backward_kv_tile_size (int): Tile size along key/value sequence length in the backward pass
205
+ kernel. This is ignored by `"flex-fmha"`.
206
+ You can use [profiler](profiler.md) to find valid choices for your use case.
207
+
208
+ backward_kv_splits (int): Number of key/value tiles allowed to work in parallel in the
209
+ backward pass kernel. This is only respected by the `"cutlass-fmha"` backend, only when
210
+ [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.
211
+
212
+ backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`
213
+ product required by the backward pass, over the CUTLASS kernel. This only applies to
214
+ the `"cutlass-fmha"` backend.
215
+
216
+ run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass
217
+ kernel. This only applies to the `"blackwell-fmha"` backend.
218
+
219
+ kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are
220
+ `None`: pick the default, `"non"` (non-persistent), `"coop"` (warp-specialized
221
+ cooperative), or `"pp"` (warp-specialized ping-ponging). Refer to
222
+ [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.
223
+
224
+ torch_compile (bool): Applies only to the `"flex-fmha"` backend. Whether or not to JIT
225
+ compile the attention kernel. Due to this being an experimental feature in PyTorch, we
226
+ do not recommend it, and it is guarded by context flags. Read more in
227
+ [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).
228
+
229
+ return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used
230
+ in the backward pass, and for [attention merging][natten.merge_attentions].
231
+
232
+ Returns:
233
+ output (Tensor): 4-D output tensor, with the heads last layout
234
+ (`[batch, seqlen, heads, head_dim_v]`).
235
+
236
+ logsumexp (Tensor): only returned when `return_lse=True`. 3-D logsumexp tensor, with the
237
+ heads last layout (`[batch, seqlen, heads]`).
238
+ """
239
+
240
+ fmha_tensor_checks(query, key, value)
241
+
242
+ (
243
+ cumulative_seqlen_Q,
244
+ cumulative_seqlen_KV,
245
+ max_seqlen_Q,
246
+ max_seqlen_KV,
247
+ ) = varlen_tensor_checks(
248
+ query=query,
249
+ key=key,
250
+ value=value,
251
+ seqlens_Q=seqlens_Q,
252
+ seqlens_KV=seqlens_KV,
253
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
254
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
255
+ max_seqlen_Q=max_seqlen_Q,
256
+ max_seqlen_KV=max_seqlen_KV,
257
+ )
258
+ is_varlen = cumulative_seqlen_Q is not None
259
+
260
+ scale = scale or query.shape[-1] ** -0.5
261
+
262
+ kernel_schedule = check_kernel_schedule(kernel_schedule)
263
+
264
+ backend = backend or choose_fmha_backend(
265
+ query,
266
+ key,
267
+ value,
268
+ is_causal=is_causal,
269
+ is_varlen=is_varlen,
270
+ torch_compile=torch_compile,
271
+ )
272
+
273
+ if backend == "blackwell-fmha":
274
+ return cutlass_blackwell_fmha(
275
+ query=query,
276
+ key=key,
277
+ value=value,
278
+ is_causal=is_causal,
279
+ scale=scale,
280
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
281
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
282
+ max_seqlen_Q=max_seqlen_Q,
283
+ max_seqlen_KV=max_seqlen_KV,
284
+ q_tile_size=q_tile_size,
285
+ kv_tile_size=kv_tile_size,
286
+ backward_q_tile_size=backward_q_tile_size,
287
+ backward_kv_tile_size=backward_kv_tile_size,
288
+ run_persistent_kernel=run_persistent_kernel,
289
+ return_lse=return_lse,
290
+ )
291
+
292
+ if backend == "hopper-fmha":
293
+ return cutlass_hopper_fmha(
294
+ query=query,
295
+ key=key,
296
+ value=value,
297
+ is_causal=is_causal,
298
+ scale=scale,
299
+ q_tile_size=q_tile_size,
300
+ kv_tile_size=kv_tile_size,
301
+ backward_q_tile_size=backward_q_tile_size,
302
+ backward_kv_tile_size=backward_kv_tile_size,
303
+ kernel_schedule=kernel_schedule,
304
+ return_lse=return_lse,
305
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
306
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
307
+ max_seqlen_Q=max_seqlen_Q,
308
+ max_seqlen_KV=max_seqlen_KV,
309
+ )
310
+
311
+ elif backend == "cutlass-fmha":
312
+ return cutlass_fmha(
313
+ query=query,
314
+ key=key,
315
+ value=value,
316
+ is_causal=is_causal,
317
+ scale=scale,
318
+ q_tile_size=q_tile_size,
319
+ kv_tile_size=kv_tile_size,
320
+ backward_q_tile_size=backward_q_tile_size,
321
+ backward_kv_tile_size=backward_kv_tile_size,
322
+ backward_kv_splits=backward_kv_splits,
323
+ backward_use_pt_reduction=backward_use_pt_reduction,
324
+ return_lse=return_lse,
325
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
326
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
327
+ max_seqlen_Q=max_seqlen_Q,
328
+ max_seqlen_KV=max_seqlen_KV,
329
+ )
330
+
331
+ elif backend == "flex-fmha":
332
+ return flex_fmha(
333
+ query=query,
334
+ key=key,
335
+ value=value,
336
+ is_causal=is_causal,
337
+ scale=scale,
338
+ q_tile_size=q_tile_size,
339
+ kv_tile_size=kv_tile_size,
340
+ torch_compile=torch_compile,
341
+ return_lse=return_lse,
342
+ cumulative_seqlen_Q=cumulative_seqlen_Q,
343
+ cumulative_seqlen_KV=cumulative_seqlen_KV,
344
+ max_seqlen_Q=max_seqlen_Q,
345
+ max_seqlen_KV=max_seqlen_KV,
346
+ )
347
+
348
+ raise NotImplementedError(f"Unrecognized NATTEN FMHA backend {backend}.")
349
+
350
+
351
+ # Neighborhood Attention
352
+
353
+
354
+ def neighborhood_attention_generic(
355
+ query: Tensor,
356
+ key: Tensor,
357
+ value: Tensor,
358
+ kernel_size: DimensionTypeOrDed,
359
+ stride: DimensionTypeOrDed = 1,
360
+ dilation: DimensionTypeOrDed = 1,
361
+ is_causal: Optional[CausalArgTypeOrDed] = False,
362
+ scale: Optional[float] = None,
363
+ additional_keys: Optional[Tensor] = None,
364
+ additional_values: Optional[Tensor] = None,
365
+ attention_kwargs: Optional[Dict] = None,
366
+ # Perf-related args
367
+ backend: Optional[str] = None,
368
+ q_tile_shape: Optional[DimensionType] = None,
369
+ kv_tile_shape: Optional[DimensionType] = None,
370
+ backward_q_tile_shape: Optional[DimensionType] = None,
371
+ backward_kv_tile_shape: Optional[DimensionType] = None,
372
+ backward_kv_splits: Optional[DimensionType] = None,
373
+ backward_use_pt_reduction: bool = False,
374
+ run_persistent_kernel: bool = True,
375
+ kernel_schedule: Optional[Union[str, KernelSchedule]] = None,
376
+ torch_compile: bool = False,
377
+ return_lse: bool = False,
378
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
379
+
380
+ na_tensor_checks(query, key, value)
381
+ additional_kv_tensor_checks(query, key, value, additional_keys, additional_values)
382
+ kernel_schedule = check_kernel_schedule(kernel_schedule)
383
+
384
+ na_dim = query.dim() - 3 # batch, heads, head_dim
385
+
386
+ assert na_dim in [1, 2, 3]
387
+
388
+ kernel_size, stride, dilation, is_causal = check_all_args(
389
+ na_dim, kernel_size, stride, dilation, is_causal
390
+ )
391
+
392
+ check_args_against_input(
393
+ query,
394
+ kernel_size=kernel_size,
395
+ stride=stride,
396
+ dilation=dilation,
397
+ is_causal=is_causal,
398
+ )
399
+
400
+ has_additional_attention = (
401
+ additional_keys is not None and additional_values is not None
402
+ )
403
+
404
+ if is_self_attention(
405
+ query,
406
+ kernel_size=kernel_size,
407
+ is_causal=is_causal,
408
+ has_additional_attention=has_additional_attention,
409
+ ):
410
+ logger.debug(
411
+ f"{query.shape=} with {kernel_size=}, {has_additional_attention=} and {is_causal=} is "
412
+ "self attention. Calling attention instead of neighborhood attention directly."
413
+ )
414
+
415
+ query_shape = query.shape
416
+ query = query.flatten(1, na_dim)
417
+ key = key.flatten(1, na_dim)
418
+ value = value.flatten(1, na_dim)
419
+
420
+ if has_additional_attention:
421
+ assert additional_keys is not None
422
+ assert additional_values is not None
423
+ key = torch.cat([key, additional_keys], dim=1)
424
+ value = torch.cat([value, additional_values], dim=1)
425
+
426
+ attn_kwargs = attention_kwargs or {}
427
+ out, lse = attention(
428
+ query,
429
+ key,
430
+ value,
431
+ is_causal=is_causal[0], # NOTE: special case
432
+ scale=scale,
433
+ return_lse=True,
434
+ **attn_kwargs,
435
+ )
436
+ lse_shape = [s for s in query_shape[:-1]]
437
+ output_shape = lse_shape + [value.shape[-1]]
438
+ out = out.reshape(*output_shape)
439
+ lse = lse.reshape(*lse_shape)
440
+
441
+ if return_lse:
442
+ return out, lse
443
+
444
+ return out
445
+
446
+ scale = scale or query.shape[-1] ** -0.5
447
+
448
+ backend = backend or choose_backend(query, key, value, torch_compile=torch_compile)
449
+
450
+ if backend == "blackwell-fna":
451
+ output, lse = cutlass_blackwell_fna_generic(
452
+ query=query,
453
+ key=key,
454
+ value=value,
455
+ kernel_size=kernel_size,
456
+ stride=stride,
457
+ dilation=dilation,
458
+ is_causal=is_causal,
459
+ scale=scale,
460
+ q_tile_shape=q_tile_shape,
461
+ kv_tile_shape=kv_tile_shape,
462
+ backward_q_tile_shape=backward_q_tile_shape,
463
+ backward_kv_tile_shape=backward_kv_tile_shape,
464
+ run_persistent_kernel=run_persistent_kernel,
465
+ return_lse=True,
466
+ )
467
+
468
+ elif backend == "hopper-fna":
469
+ output, lse = cutlass_hopper_fna_generic(
470
+ query=query,
471
+ key=key,
472
+ value=value,
473
+ kernel_size=kernel_size,
474
+ stride=stride,
475
+ dilation=dilation,
476
+ is_causal=is_causal,
477
+ scale=scale,
478
+ q_tile_shape=q_tile_shape,
479
+ kv_tile_shape=kv_tile_shape,
480
+ backward_q_tile_shape=backward_q_tile_shape,
481
+ backward_kv_tile_shape=backward_kv_tile_shape,
482
+ kernel_schedule=kernel_schedule,
483
+ return_lse=True,
484
+ )
485
+
486
+ elif backend == "cutlass-fna":
487
+ output, lse = cutlass_fna_generic(
488
+ query=query,
489
+ key=key,
490
+ value=value,
491
+ kernel_size=kernel_size,
492
+ stride=stride,
493
+ dilation=dilation,
494
+ is_causal=is_causal,
495
+ scale=scale,
496
+ q_tile_shape=q_tile_shape,
497
+ kv_tile_shape=kv_tile_shape,
498
+ backward_q_tile_shape=backward_q_tile_shape,
499
+ backward_kv_tile_shape=backward_kv_tile_shape,
500
+ backward_kv_splits=backward_kv_splits,
501
+ backward_use_pt_reduction=backward_use_pt_reduction,
502
+ return_lse=True,
503
+ )
504
+
505
+ elif backend == "flex-fna":
506
+ output, lse = flex_fna_generic(
507
+ query=query,
508
+ key=key,
509
+ value=value,
510
+ kernel_size=kernel_size,
511
+ stride=stride,
512
+ dilation=dilation,
513
+ is_causal=is_causal,
514
+ scale=scale,
515
+ q_tile_shape=q_tile_shape,
516
+ kv_tile_shape=kv_tile_shape,
517
+ torch_compile=torch_compile,
518
+ return_lse=True,
519
+ )
520
+
521
+ else:
522
+ raise NotImplementedError(f"Unrecognized NATTEN backend {backend}.")
523
+
524
+ if has_additional_attention:
525
+ assert additional_keys is not None
526
+ assert additional_values is not None
527
+
528
+ attention_kwargs = attention_kwargs or {}
529
+ if "torch_compile" not in attention_kwargs:
530
+ attention_kwargs["torch_compile"] = torch_compile
531
+
532
+ additional_output, additional_lse = attention(
533
+ query.flatten(1, na_dim),
534
+ additional_keys,
535
+ additional_values,
536
+ is_causal=False,
537
+ scale=scale,
538
+ return_lse=True,
539
+ **attention_kwargs,
540
+ )
541
+
542
+ # NOTE: Flex unfused should not use the autograd fix
543
+ is_flex = backend == "flex-fna" or (
544
+ "backend" in attention_kwargs and attention_kwargs["backend"] == "flex-fmha"
545
+ )
546
+ use_autograd_fix = not is_flex or torch_compile
547
+
548
+ merged_output, merged_lse = merge_attentions(
549
+ [output.flatten(1, na_dim), additional_output],
550
+ [lse.flatten(1, na_dim), additional_lse],
551
+ use_autograd_fix=use_autograd_fix,
552
+ )
553
+ merged_output = merged_output.reshape(output.shape)
554
+ merged_lse = merged_lse.reshape(output.shape[:-1])
555
+
556
+ if return_lse:
557
+ return merged_output, merged_lse
558
+
559
+ return merged_output
560
+
561
+ if return_lse:
562
+ return output, lse
563
+
564
+ return output
565
+
566
+
567
+ def na1d(
568
+ query: Tensor,
569
+ key: Tensor,
570
+ value: Tensor,
571
+ kernel_size: Dimension1DTypeOrDed,
572
+ stride: Dimension1DTypeOrDed = 1,
573
+ dilation: Dimension1DTypeOrDed = 1,
574
+ is_causal: Optional[CausalArg1DTypeOrDed] = False,
575
+ scale: Optional[float] = None,
576
+ additional_keys: Optional[Tensor] = None,
577
+ additional_values: Optional[Tensor] = None,
578
+ attention_kwargs: Optional[Dict] = None,
579
+ backend: Optional[str] = None,
580
+ q_tile_shape: Optional[Dimension1DType] = None,
581
+ kv_tile_shape: Optional[Dimension1DType] = None,
582
+ backward_q_tile_shape: Optional[Dimension1DType] = None,
583
+ backward_kv_tile_shape: Optional[Dimension1DType] = None,
584
+ backward_kv_splits: Optional[Dimension1DType] = None,
585
+ backward_use_pt_reduction: bool = False,
586
+ run_persistent_kernel: bool = True,
587
+ kernel_schedule: Optional[Union[str, KernelSchedule]] = None,
588
+ torch_compile: bool = False,
589
+ return_lse: bool = False,
590
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
591
+ """Computes 1-D neighborhood attention.
592
+
593
+ GQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fna` and
594
+ `flex-fna` support GQA/MQA natively, and `cutlass-fna` and `hopper-fna` support it with
595
+ explicit repeats (increases memory usage and runtime).
596
+
597
+ Parameters:
598
+ query (Tensor): 4-D query tensor, with the heads last layout
599
+ (`[batch, seqlen, heads, head_dim]`)
600
+
601
+ key (Tensor): 4-D key tensor, with the heads last layout
602
+ (`[batch, seqlen, heads_kv, head_dim]`)
603
+
604
+ value (Tensor): 4-D value tensor, with the heads last layout
605
+ (`[batch, seqlen, heads_kv, head_dim_v]`)
606
+
607
+ kernel_size (Tuple[int] | int): Neighborhood window (kernel) size.
608
+
609
+ !!! note
610
+ `kernel_size` must be smaller than or equal to `seqlen`.
611
+
612
+ stride (Tuple[int] | int): Sliding window step size. Defaults to `1` (standard sliding
613
+ window).
614
+
615
+ !!! note
616
+ `stride` must be smaller than or equal to `kernel_size`.
617
+ When `stride == kernel_size`, there will be no overlap between sliding windows,
618
+ which is equivalent to blocked attention (a.k.a.
619
+ [window self attention](https://arxiv.org/abs/2103.14030)).
620
+
621
+ dilation (Tuple[int] | int): Dilation step size. Defaults to `1` (standard sliding window).
622
+
623
+ !!! note
624
+ The product of `dilation` and `kernel_size` must be smaller than or equal to
625
+ `seqlen`.
626
+
627
+ is_causal (Tuple[bool] | bool): Toggle causal masking. Defaults to `False`
628
+ (bi-directional).
629
+
630
+ scale (float): Attention scale. Defaults to `head_dim ** -0.5`.
631
+
632
+ additional_keys: `None` or 4-D key tensor, with the heads last layout
633
+ (`[batch, seqlen_kv, heads_kv, head_dim]`), corresponding to key tokens from some
634
+ additional context. Used when performing neighborhood cross-attention, where `query`
635
+ tokens attend to their neighborhood, as well as some fixed additional set of tokens.
636
+
637
+ additional_values: `None` or 4-D value tensor, with the heads last layout
638
+ (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to value tokens from some
639
+ additional context. Used when performing neighborhood cross-attention, where `query`
640
+ tokens attend to their neighborhood, as well as some fixed additional set of tokens.
641
+
642
+ !!! note
643
+ `additional_keys` and `additional_values` must both either be `Tensor`s, or both
644
+ `None`s, and must match in shape.
645
+
646
+ Other Parameters:
647
+ backend (str): Backend implementation to run with. Choices are: `None` (pick the best
648
+ available one), `"cutlass-fna"`, `"hopper-fna"`, `"blackwell-fna"`, `"flex-fna"`.
649
+ Refer to [backends](backends.md) for more information.
650
+
651
+ q_tile_shape (Tuple[int]): 1-D Tile shape for the query token layout in the forward pass
652
+ kernel. You can use [profiler](profiler.md) to find valid choices for your use case,
653
+ and search for the best combination.
654
+
655
+ kv_tile_shape (Tuple[int]): 1-D Tile shape for the key-value token layout in the forward
656
+ pass kernel. You can use [profiler](profiler.md) to find valid choices for your use
657
+ case, and search for the best combination.
658
+
659
+ backward_q_tile_shape (Tuple[int]): 1-D Tile shape for the query token layout in the
660
+ backward pass kernel. This is ignored by `"flex-fna"`.
661
+ You can use [profiler](profiler.md) to find valid choices for your use case, and
662
+ search for the best combination.
663
+
664
+ backward_kv_tile_shape (Tuple[int]): 1-D Tile shape for the key/value token layout in the
665
+ backward pass kernel. This is ignored by `"flex-fna"`.
666
+ You can use [profiler](profiler.md) to find valid choices for your use case, and
667
+ search for the best combination.
668
+
669
+ backward_kv_splits (Tuple[int]): Number of key/value tiles allowed to work in parallel in
670
+ the backward pass kernel. Like tile shapes, this is a tuple and not an integer for
671
+ neighborhood attention operations, and the size of the tuple corresponds to the number
672
+ of dimensions / rank of the layout of tokens. This is only respected by the
673
+ `"cutlass-fna"` backend, and only when
674
+ [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.
675
+
676
+ backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`
677
+ product required by the backward pass, over the CUTLASS kernel. This only applies to
678
+ the `"cutlass-fna"` backend.
679
+
680
+ run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass
681
+ kernel. This only applies to the `"blackwell-fna"` backend.
682
+
683
+ kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are
684
+ `None`: pick the default, `"non"` (non-persistent), `"coop"` (warp-specialized
685
+ cooperative), or `"pp"` (warp-specialized ping-ponging). Refer to
686
+ [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.
687
+
688
+ torch_compile (bool): Applies only to the `"flex-fna"` backend. Whether or not to JIT
689
+ compile the attention kernel. Due to this being an experimental feature in PyTorch, we
690
+ do not recommend it, and it is guarded by context flags. Read more in
691
+ [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).
692
+
693
+ attention_kwargs: arguments to the [attention][natten.attention] operator, if used to
694
+ implement neighborhood cross-attention, or self attention as a fast path for
695
+ neighborhood attention.
696
+
697
+ If `additional_{keys,values}` are specified, NATTEN usually performs a separate
698
+ cross-attention using our [attention][natten.attention] operator, and
699
+ [merges][natten.merge_attentions] the results.
700
+
701
+ If for a given use case, the neighborhood attention problem is equivalent to self
702
+ attention (not causal, `kernel_size == seqlen`), NATTEN will also attempt to directly
703
+ use [attention][natten.attention].
704
+
705
+ You can override arguments to [attention][natten.attention] by passing a
706
+ dictionary here.
707
+
708
+ !!! example
709
+ ```python
710
+ out = na1d(
711
+ q, k, v, kernel_size=kernel_size,
712
+ ...,
713
+ attention_kwargs={
714
+ "backend": "blackwell-fmha",
715
+ "run_persistent_kernel": True,
716
+ }
717
+ )
718
+ ```
719
+
720
+ return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used
721
+ in the backward pass, and for [attention merging][natten.merge_attentions].
722
+
723
+ Returns:
724
+ output (Tensor): 4-D output tensor, with the heads last layout
725
+ (`[batch, seqlen, heads, head_dim_v]`).
726
+
727
+ logsumexp (Tensor): only returned when `return_lse=True`. 3-D logsumexp tensor, with the
728
+ heads last layout (`[batch, seqlen, heads]`).
729
+ """
730
+ return neighborhood_attention_generic(
731
+ query=query,
732
+ key=key,
733
+ value=value,
734
+ kernel_size=kernel_size,
735
+ stride=stride,
736
+ dilation=dilation,
737
+ is_causal=is_causal,
738
+ scale=scale,
739
+ additional_keys=additional_keys,
740
+ additional_values=additional_values,
741
+ attention_kwargs=attention_kwargs,
742
+ backend=backend,
743
+ q_tile_shape=q_tile_shape,
744
+ kv_tile_shape=kv_tile_shape,
745
+ backward_q_tile_shape=backward_q_tile_shape,
746
+ backward_kv_tile_shape=backward_kv_tile_shape,
747
+ backward_kv_splits=backward_kv_splits,
748
+ backward_use_pt_reduction=backward_use_pt_reduction,
749
+ run_persistent_kernel=run_persistent_kernel,
750
+ kernel_schedule=kernel_schedule,
751
+ torch_compile=torch_compile,
752
+ return_lse=return_lse,
753
+ )
754
+
755
+
756
+ def na2d(
757
+ query: Tensor,
758
+ key: Tensor,
759
+ value: Tensor,
760
+ kernel_size: Dimension2DTypeOrDed,
761
+ stride: Dimension2DTypeOrDed = 1,
762
+ dilation: Dimension2DTypeOrDed = 1,
763
+ is_causal: Optional[CausalArg2DTypeOrDed] = False,
764
+ scale: Optional[float] = None,
765
+ additional_keys: Optional[Tensor] = None,
766
+ additional_values: Optional[Tensor] = None,
767
+ attention_kwargs: Optional[Dict] = None,
768
+ backend: Optional[str] = None,
769
+ q_tile_shape: Optional[Dimension2DType] = None,
770
+ kv_tile_shape: Optional[Dimension2DType] = None,
771
+ backward_q_tile_shape: Optional[Dimension2DType] = None,
772
+ backward_kv_tile_shape: Optional[Dimension2DType] = None,
773
+ backward_kv_splits: Optional[Dimension2DType] = None,
774
+ backward_use_pt_reduction: bool = False,
775
+ run_persistent_kernel: bool = True,
776
+ kernel_schedule: Optional[Union[str, KernelSchedule]] = None,
777
+ torch_compile: bool = False,
778
+ return_lse: bool = False,
779
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
780
+ """Computes 2-D neighborhood attention.
781
+
782
+ GQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fna` and
783
+ `flex-fna` support GQA/MQA natively, and `cutlass-fna` and `hopper-fna` support it with
784
+ explicit repeats (increases memory usage and runtime).
785
+
786
+ Parameters:
787
+ query (Tensor): 2-D query tensor, with the heads last layout:
788
+ `[batch, X, Y, heads, head_dim]`, where token layout shape (feature map shape) is
789
+ `(X, Y)`.
790
+
791
+ key (Tensor): 2-D key tensor, with the heads last layout:
792
+ `[batch, X, Y, heads_kv, head_dim]`, where token layout shape (feature map shape) is
793
+ `(X, Y)`.
794
+
795
+ value (Tensor): 2-D value tensor, with the heads last layout:
796
+ `[batch, X, Y, heads_kv, head_dim_v]`, where token layout shape (feature map shape) is
797
+ `(X, Y)`.
798
+
799
+ kernel_size (Tuple[int, int] | int): Neighborhood window (kernel) size/shape. If an
800
+ integer, it will be repeated for all 2 dimensions. For example `kernel_size=3` is
801
+ reinterpreted as `kernel_size=(3, 3)`.
802
+
803
+ !!! note
804
+ `kernel_size` must be smaller than or equal to token layout shape (`(X, Y)`) along
805
+ every dimension.
806
+
807
+ stride (Tuple[int, int] | int): Sliding window step size/shape. Defaults to `1` (standard
808
+ sliding window). If an integer, it will be repeated for all 2 dimensions. For example
809
+ `stride=2` is reinterpreted as `stride=(2, 2)`.
810
+
811
+ !!! note
812
+ `stride` must be smaller than or equal to `kernel_size` along every dimension.
813
+ When `stride == kernel_size`, there will be no overlap between sliding windows,
814
+ which is equivalent to blocked attention (a.k.a.
815
+ [window self attention](https://arxiv.org/abs/2103.14030)).
816
+
817
+ dilation (Tuple[int, int] | int): Dilation step size/shape. Defaults to `1` (standard
818
+ sliding window). If an integer, it will be repeated for all 2 dimensions. For example
819
+ `dilation=4` is reinterpreted as `dilation=(4, 4)`.
820
+
821
+ !!! note
822
+ The product of `dilation` and `kernel_size` must be smaller than or equal to
823
+ token layout shape (`(X, Y)`) along every dimension.
824
+
825
+ is_causal (Tuple[bool, bool] | bool): Toggle causal masking. Defaults to `False`
826
+ (bi-directional). If a boolean, it will be repeated for all 2 dimensions. For example
827
+ `is_causal=True` is reinterpreted as `is_causal=(True, True)`.
828
+
829
+ scale (float): Attention scale. Defaults to `head_dim ** -0.5`.
830
+
831
+ additional_keys: `None` or 4-D key tensor, with the heads last layout
832
+ (`[batch, seqlen_kv, heads_kv, head_dim]`), corresponding to key tokens from some
833
+ additional context. Used when performing neighborhood cross-attention, where `query`
834
+ tokens attend to their neighborhood, as well as some fixed additional set of tokens.
835
+
836
+ additional_values: `None` or 4-D value tensor, with the heads last layout
837
+ (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to value tokens from some
838
+ additional context. Used when performing neighborhood cross-attention, where `query`
839
+ tokens attend to their neighborhood, as well as some fixed additional set of tokens.
840
+
841
+ !!! note
842
+ `additional_keys` and `additional_values` must both either be `Tensor`s, or both
843
+ `None`s, and must match in shape.
844
+
845
+ Other Parameters:
846
+ backend (str): Backend implementation to run with. Choices are: `None` (pick the best
847
+ available one), `"cutlass-fna"`, `"hopper-fna"`, `"blackwell-fna"`, `"flex-fna"`.
848
+ Refer to [backends](backends.md) for more information.
849
+
850
+ q_tile_shape (Tuple[int, int]): 2-D Tile shape for the query token layout in the forward
851
+ pass kernel. You can use [profiler](profiler.md) to find valid choices for your use
852
+ case, and search for the best combination.
853
+
854
+ kv_tile_shape (Tuple[int, int]): 2-D Tile shape for the key-value token layout in the
855
+ forward pass kernel. You can use [profiler](profiler.md) to find valid choices for your
856
+ use case, and search for the best combination.
857
+
858
+ backward_q_tile_shape (Tuple[int, int]): 2-D Tile shape for the query token layout in the
859
+ backward pass kernel. This is ignored by `"flex-fna"`.
860
+ You can use [profiler](profiler.md) to find valid choices for your use case, and
861
+ search for the best combination.
862
+
863
+ backward_kv_tile_shape (Tuple[int, int]): 2-D Tile shape for the key/value token layout in
864
+ the backward pass kernel. This is ignored by `"flex-fna"`.
865
+ You can use [profiler](profiler.md) to find valid choices for your use case, and
866
+ search for the best combination.
867
+
868
+ backward_kv_splits (Tuple[int, int]): Number of key/value tiles allowed to work in parallel
869
+ in the backward pass kernel. Like tile shapes, this is a tuple and not an integer for
870
+ neighborhood attention operations, and the size of the tuple corresponds to the number
871
+ of dimensions / rank of the layout of tokens. This is only respected by the
872
+ `"cutlass-fna"` backend, and only when
873
+ [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.
874
+
875
+ backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`
876
+ product required by the backward pass, over the CUTLASS kernel. This only applies to
877
+ the `"cutlass-fna"` backend.
878
+
879
+ run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass
880
+ kernel. This only applies to the `"blackwell-fna"` backend.
881
+
882
+ kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are
883
+ `None`: pick the default, `"non"` (non-persistent), `"coop"` (warp-specialized
884
+ cooperative), or `"pp"` (warp-specialized ping-ponging). Refer to
885
+ [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.
886
+
887
+ torch_compile (bool): Applies only to the `"flex-fna"` backend. Whether or not to JIT
888
+ compile the attention kernel. Due to this being an experimental feature in PyTorch, we
889
+ do not recommend it, and it is guarded by context flags. Read more in
890
+ [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).
891
+
892
+ attention_kwargs: arguments to the [attention][natten.attention] operator, if used to
893
+ implement neighborhood cross-attention, or self attention as a fast path for
894
+ neighborhood attention.
895
+
896
+ If `additional_{keys,values}` are specified, NATTEN usually performs a separate
897
+ cross-attention using our [attention][natten.attention] operator, and
898
+ [merges][natten.merge_attentions] the results.
899
+
900
+ If for a given use case, the neighborhood attention problem is equivalent to self
901
+ attention (not causal along any dims, `kernel_size == (X, Y)`), NATTEN will also
902
+ attempt to directly use [attention][natten.attention].
903
+
904
+ You can override arguments to [attention][natten.attention] by passing a
905
+ dictionary here.
906
+
907
+ !!! example
908
+ ```python
909
+ out = na2d(
910
+ q, k, v, kernel_size=kernel_size,
911
+ ...,
912
+ attention_kwargs={
913
+ "backend": "blackwell-fmha",
914
+ "run_persistent_kernel": True,
915
+ }
916
+ )
917
+ ```
918
+
919
+ return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used
920
+ in the backward pass, and for [attention merging][natten.merge_attentions].
921
+
922
+ Returns:
923
+ output (Tensor): 5-D output tensor, with the heads last layout
924
+ (`[batch, X, Y, heads, head_dim_v]`).
925
+
926
+ logsumexp (Tensor): only returned when `return_lse=True`. 4-D logsumexp tensor, with the
927
+ heads last layout (`[batch, X, Y, heads]`).
928
+ """
929
+ return neighborhood_attention_generic(
930
+ query=query,
931
+ key=key,
932
+ value=value,
933
+ kernel_size=kernel_size,
934
+ stride=stride,
935
+ dilation=dilation,
936
+ is_causal=is_causal,
937
+ scale=scale,
938
+ additional_keys=additional_keys,
939
+ additional_values=additional_values,
940
+ attention_kwargs=attention_kwargs,
941
+ backend=backend,
942
+ q_tile_shape=q_tile_shape,
943
+ kv_tile_shape=kv_tile_shape,
944
+ backward_q_tile_shape=backward_q_tile_shape,
945
+ backward_kv_tile_shape=backward_kv_tile_shape,
946
+ backward_kv_splits=backward_kv_splits,
947
+ backward_use_pt_reduction=backward_use_pt_reduction,
948
+ run_persistent_kernel=run_persistent_kernel,
949
+ kernel_schedule=kernel_schedule,
950
+ torch_compile=torch_compile,
951
+ return_lse=return_lse,
952
+ )
953
+
954
+
955
+ def na3d(
956
+ query: Tensor,
957
+ key: Tensor,
958
+ value: Tensor,
959
+ kernel_size: Dimension3DTypeOrDed,
960
+ stride: Dimension3DTypeOrDed = 1,
961
+ dilation: Dimension3DTypeOrDed = 1,
962
+ is_causal: Optional[CausalArg3DTypeOrDed] = False,
963
+ scale: Optional[float] = None,
964
+ additional_keys: Optional[Tensor] = None,
965
+ additional_values: Optional[Tensor] = None,
966
+ attention_kwargs: Optional[Dict] = None,
967
+ backend: Optional[str] = None,
968
+ q_tile_shape: Optional[Dimension3DType] = None,
969
+ kv_tile_shape: Optional[Dimension3DType] = None,
970
+ backward_q_tile_shape: Optional[Dimension3DType] = None,
971
+ backward_kv_tile_shape: Optional[Dimension3DType] = None,
972
+ backward_kv_splits: Optional[Dimension3DType] = None,
973
+ backward_use_pt_reduction: bool = False,
974
+ run_persistent_kernel: bool = True,
975
+ kernel_schedule: Optional[Union[str, KernelSchedule]] = None,
976
+ torch_compile: bool = False,
977
+ return_lse: bool = False,
978
+ ) -> Union[Tensor, Tuple[Tensor, Tensor]]:
979
+ """Computes 3-D neighborhood attention.
980
+
981
+ GQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fna` and
982
+ `flex-fna` support GQA/MQA natively, and `cutlass-fna` and `hopper-fna` support it with
983
+ explicit repeats (increases memory usage and runtime).
984
+
985
+ Parameters:
986
+ query (Tensor): 3-D query tensor, with the heads last layout:
987
+ `[batch, X, Y, Z, heads, head_dim]`, where token layout shape (feature map shape) is
988
+ `(X, Y, Z)`.
989
+
990
+ key (Tensor): 3-D key tensor, with the heads last layout:
991
+ `[batch, X, Y, Z, heads_kv, head_dim]`, where token layout shape (feature map shape) is
992
+ `(X, Y, Z)`.
993
+
994
+ value (Tensor): 3-D value tensor, with the heads last layout:
995
+ `[batch, X, Y, Z, heads_kv, head_dim_V]`, where token layout shape (feature map shape) is
996
+ `(X, Y, Z)`.
997
+
998
+ kernel_size (Tuple[int, int, int] | int): Neighborhood window (kernel) size/shape. If an
999
+ integer, it will be repeated for all 3 dimensions. For example `kernel_size=3` is
1000
+ reinterpreted as `kernel_size=(3, 3, 3)`.
1001
+
1002
+ !!! note
1003
+ `kernel_size` must be smaller than or equal to token layout shape (`(X, Y, Z)`)
1004
+ along every dimension.
1005
+
1006
+ stride (Tuple[int, int, int] | int): Sliding window step size/shape. Defaults to `1`
1007
+ (standard sliding window). If an integer, it will be repeated for all 3 dimensions.
1008
+ For example `stride=2` is reinterpreted as `stride=(2, 2, 2)`.
1009
+
1010
+ !!! note
1011
+ `stride` must be smaller than or equal to `kernel_size` along every dimension.
1012
+ When `stride == kernel_size`, there will be no overlap between sliding windows,
1013
+ which is equivalent to blocked attention (a.k.a.
1014
+ [window self attention](https://arxiv.org/abs/2103.14030)).
1015
+
1016
+ dilation (Tuple[int, int, int] | int): Dilation step size/shape. Defaults to `1` (standard
1017
+ sliding window). If an integer, it will be repeated for all 3 dimensions. For example
1018
+ `dilation=4` is reinterpreted as `dilation=(4, 4, 4)`.
1019
+
1020
+ !!! note
1021
+ The product of `dilation` and `kernel_size` must be smaller than or equal to
1022
+ token layout shape (`(X, Y, Z)`) along every dimension.
1023
+
1024
+ is_causal (Tuple[bool, bool, bool] | bool): Toggle causal masking. Defaults to `False`
1025
+ (bi-directional). If a boolean, it will be repeated for all 3 dimensions. For example
1026
+ `is_causal=True` is reinterpreted as `is_causal=(True, True, True)`.
1027
+
1028
+ scale (float): Attention scale. Defaults to `head_dim ** -0.5`.
1029
+
1030
+ additional_keys: `None` or 4-D key tensor, with the heads last layout
1031
+ (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to key tokens from some
1032
+ additional context. Used when performing neighborhood cross-attention, where `query`
1033
+ tokens attend to their neighborhood, as well as some fixed additional set of tokens.
1034
+
1035
+ additional_values: `None` or 4-D value tensor, with the heads last layout
1036
+ (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to value tokens from some
1037
+ additional context. Used when performing neighborhood cross-attention, where `query`
1038
+ tokens attend to their neighborhood, as well as some fixed additional set of tokens.
1039
+
1040
+ !!! note
1041
+ `additional_keys` and `additional_values` must both either be `Tensor`s, or both
1042
+ `None`s, and must match in shape.
1043
+
1044
+ Other Parameters:
1045
+ backend (str): Backend implementation to run with. Choices are: `None` (pick the best
1046
+ available one), `"cutlass-fna"`, `"hopper-fna"`, `"blackwell-fna"`, `"flex-fna"`.
1047
+ Refer to [backends](backends.md) for more information.
1048
+
1049
+ q_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the query token layout in the
1050
+ forward pass kernel. You can use [profiler](profiler.md) to find valid choices for your
1051
+ use case, and search for the best combination.
1052
+
1053
+ kv_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the key-value token layout in the
1054
+ forward pass kernel. You can use [profiler](profiler.md) to find valid choices for your
1055
+ use case, and search for the best combination.
1056
+
1057
+ backward_q_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the query token layout in
1058
+ the backward pass kernel. This is ignored by `"flex-fna"`.
1059
+ You can use [profiler](profiler.md) to find valid choices for your use case, and
1060
+ search for the best combination.
1061
+
1062
+ backward_kv_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the key/value token
1063
+ layout in the backward pass kernel. This is ignored by `"flex-fna"`.
1064
+ You can use [profiler](profiler.md) to find valid choices for your use case,
1065
+ and search for the best combination.
1066
+
1067
+ backward_kv_splits (Tuple[int, int, int]): Number of key/value tiles allowed to work in
1068
+ parallel in the backward pass kernel. Like tile shapes, this is a tuple and not an
1069
+ integer for neighborhood attention operations, and the size of the tuple corresponds to
1070
+ the number of dimensions / rank of the layout of tokens. This is only respected by the
1071
+ `"cutlass-fna"` backend, and only when
1072
+ [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.
1073
+
1074
+ backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`
1075
+ product required by the backward pass, over the CUTLASS kernel. This only applies to
1076
+ the `"cutlass-fna"` backend.
1077
+
1078
+ run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass
1079
+ kernel. This only applies to the `"blackwell-fna"` backend.
1080
+
1081
+ kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are
1082
+ `None`: pick the default, `"non"` (non-persistent), `"coop"` (warp-specialized
1083
+ cooperative), or `"pp"` (warp-specialized ping-ponging). Refer to
1084
+ [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.
1085
+
1086
+ torch_compile (bool): Applies only to the `"flex-fna"` backend. Whether or not to JIT
1087
+ compile the attention kernel. Due to this being an experimental feature in PyTorch, we
1088
+ do not recommend it, and it is guarded by context flags. Read more in
1089
+ [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).
1090
+
1091
+ attention_kwargs: arguments to the [attention][natten.attention] operator, if used to
1092
+ implement neighborhood cross-attention, or self attention as a fast path for
1093
+ neighborhood attention.
1094
+
1095
+ If `additional_{keys,values}` are specified, NATTEN usually performs a separate
1096
+ cross-attention using our [attention][natten.attention] operator, and
1097
+ [merges][natten.merge_attentions] the results.
1098
+
1099
+ If for a given use case, the neighborhood attention problem is equivalent to self
1100
+ attention (not causal along any dims, `kernel_size == (X, Y, Z)`), NATTEN will also
1101
+ attempt to directly use [attention][natten.attention].
1102
+
1103
+ You can override arguments to [attention][natten.attention] by passing a
1104
+ dictionary here.
1105
+
1106
+ !!! example
1107
+ ```python
1108
+ out = na3d(
1109
+ q, k, v, kernel_size=kernel_size,
1110
+ ...,
1111
+ attention_kwargs={
1112
+ "backend": "blackwell-fmha",
1113
+ "run_persistent_kernel": True,
1114
+ }
1115
+ )
1116
+ ```
1117
+
1118
+ return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used
1119
+ in the backward pass, and for [attention merging][natten.merge_attentions].
1120
+
1121
+ Returns:
1122
+ output (Tensor): 6-D output tensor, with the heads last layout
1123
+ (`[batch, X, Y, Z, heads, head_dim_v]`).
1124
+
1125
+ logsumexp (Tensor): only returned when `return_lse=True`. 5-D logsumexp tensor, with the
1126
+ heads last layout (`[batch, X, Y, Z, heads]`).
1127
+ """
1128
+ return neighborhood_attention_generic(
1129
+ query=query,
1130
+ key=key,
1131
+ value=value,
1132
+ kernel_size=kernel_size,
1133
+ stride=stride,
1134
+ dilation=dilation,
1135
+ is_causal=is_causal,
1136
+ scale=scale,
1137
+ additional_keys=additional_keys,
1138
+ additional_values=additional_values,
1139
+ attention_kwargs=attention_kwargs,
1140
+ backend=backend,
1141
+ q_tile_shape=q_tile_shape,
1142
+ kv_tile_shape=kv_tile_shape,
1143
+ backward_q_tile_shape=backward_q_tile_shape,
1144
+ backward_kv_tile_shape=backward_kv_tile_shape,
1145
+ backward_kv_splits=backward_kv_splits,
1146
+ backward_use_pt_reduction=backward_use_pt_reduction,
1147
+ run_persistent_kernel=run_persistent_kernel,
1148
+ kernel_schedule=kernel_schedule,
1149
+ torch_compile=torch_compile,
1150
+ return_lse=return_lse,
1151
+ )
build/torch210-cu126-aarch64-linux/metadata.json ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "natten",
3
+ "id": "_natten_cuda_5fe9dbb",
4
+ "version": 1,
5
+ "kernels-minver": "0.14.0",
6
+ "license": "MIT",
7
+ "upstream": "https://github.com/SHI-Labs/NATTEN",
8
+ "python-depends": [],
9
+ "kernel-depends": [],
10
+ "backend": {
11
+ "type": "cuda",
12
+ "archs": [
13
+ "8.0",
14
+ "9.0",
15
+ "9.0a"
16
+ ]
17
+ },
18
+ "digest": {
19
+ "algorithm": "sha256",
20
+ "files": {
21
+ "__init__.py": "2xTdlXXAhz/SpvyCABtaEDtbZW55DRw4YDDMSF94Kp4=",
22
+ "_environment.py": "9P189XTk2YMyWuRNI5eCI3UvHRqVR8Zh2hgJsHP7MYI=",
23
+ "_libnatten/__init__.py": "yMW+2Kd5Nxs716jaNq15IleYQ86HR5u7SMEQDqVVunI=",
24
+ "_libnatten/torch_wrappers.py": "v7vkrytkghNz+cJI7l2PXGqWactsSM1ImwCsHfyQ3iA=",
25
+ "_natten_cuda_5fe9dbb.abi3.so": "AvnzzKD+lh9GDOhIAXzHo5GqdONMop5fZDHRE4xDNgA=",
26
+ "_ops.py": "MbEvw83543TKiDVD1FyrMuwUK7cOvCbAaagp4d2oEUk=",
27
+ "_private_for_testing.py": "Ir5abh5fQ1+9aqmmi7RqGhZybEvYV8JEnsrhzj558xw=",
28
+ "_types.py": "OzK1SfxBXbR01LDSzFOpvm5qUCLs3qOc021I31Eibm8=",
29
+ "attn_merge.py": "KDItp+YcD/18PEXqCOOxSBeLYYt50LhYZnyluE5iT7A=",
30
+ "backends/__init__.py": "r8Lco2ESXb2yqPccmIu6kyUD8niprXF7J0WA7urlAbE=",
31
+ "backends/blackwell_fmha.py": "hDQuJ27vACJ46dj4QKaa7CWB2Sy7650ZtHlJCgQQ3Bk=",
32
+ "backends/blackwell_fna.py": "YA6YqgDnTJ/4F5FCe7uf7H0qOMhF4k4UTkKVtYtfYDU=",
33
+ "backends/configs/__init__.py": "NG3ArX78+S0/E3i2R0LTjmr30ZY71qf9Xv3396Wfw34=",
34
+ "backends/configs/checks.py": "d7qCFUqvF9fl0nuRiTr6eDXyaAf0/ksyrkqI8Z2VG+E=",
35
+ "backends/configs/cutlass/__init__.py": "H+6PW4E9/I2UtU0CUB+8rEvSjpT6hqVHEjO5JQD0cao=",
36
+ "backends/configs/cutlass/backward_knobs.py": "s6CakBU+ApSjFfLGLyC42kpmXZBc2Si3Ud+4SKIZ7zM=",
37
+ "backends/configs/cutlass/fna_backward_128x128.py": "ZcUo6u0hDGUXasU52U98XV+KUF475QyP8I5O8T042yM=",
38
+ "backends/configs/cutlass/fna_backward_128x64.py": "3gXgi7PqQpP8A7ClS8GPA1eXwOiLCgPEeOL4oJxYWXY=",
39
+ "backends/configs/cutlass/fna_backward_64x64.py": "i5iRd+TrdtDS8S48cVj6/D7+951L6lPLNuqkLIA1k/c=",
40
+ "backends/configs/cutlass/fna_forward_32x128.py": "1acDc2mpRTKBC3opzjZEEr0R9lPmQ3wLPSOIlJlM5aM=",
41
+ "backends/configs/cutlass/fna_forward_64x128.py": "8NfkwqYqJ8FfdFeXZU1e6fUF8F6dRUZRsJ+c9aVDZAI=",
42
+ "backends/configs/cutlass/fna_forward_64x64.py": "vu1gBAvQLQuO+iJdz4GdidayIywG13b83Lorw/I43ac=",
43
+ "backends/configs/cutlass_blackwell/__init__.py": "RtGGNcP4bvzu1JozMLd16mJtDncxRIAa+W7Lg7I2KHs=",
44
+ "backends/configs/cutlass_hopper/__init__.py": "DjEcfThaXh2JGrA5EW72mmRdh6Kwk/oOmaOkZm9PkNU=",
45
+ "backends/configs/flex/__init__.py": "HFK8hxYrU85UyEZPO6c66DoLvfJcji6jmTNFUGzgGDs=",
46
+ "backends/flex.py": "6xSMb2kuezRaeOcBN4UWY8F6kJG7F6DoZdSv08vvmMg=",
47
+ "backends/fmha.py": "sA52KmY/Dj5Qm5E2eX9IWNVMke0Vn94FAU+tYe1zPLc=",
48
+ "backends/fna.py": "xUzyoiHkFwDyf+Cv0VOhDt+5X5F8X6ckVAo5cJZXGsc=",
49
+ "backends/hopper_fmha.py": "+xWkbfNibNwTtU8d9TmUUedITYnnUQwykYCgBcoyTkE=",
50
+ "backends/hopper_fna.py": "fKfBfO0gNJtdJeghLDa4tQpCdIeOvlkDLf5PaD0P2P8=",
51
+ "backends/reference.py": "SG0s45Zme6O8gSjPAxw3ib9T0pqvDAxCmbGn/DQ/0tc=",
52
+ "context.py": "IzU+TvVnBZ41jc93BjQ7+/Q3Eg+8fDNSK4YCbTc2OU8=",
53
+ "functional.py": "zQq6Et4t+ywceLDcrQmiXYh2xpkRqSk1vBdHE7QsCGE=",
54
+ "modules.py": "1C3KPU4tOHIuRT+rOd6XVqPt98Ho/oehFEKOpwv445Q=",
55
+ "symbols.json": "eyAwy4AHhZ5N1bQLcFp8sj715MIRx60EcLLk/lhcjKg=",
56
+ "token_permute/__init__.py": "NPxL8EMHxAxjHMcHTMFznH0oNQjGUr2divAbtGaWNhs=",
57
+ "token_permute/cutlass_impl.py": "nCXz+b8TJAbU/j0RiB0PMO94nqFZIvNxO9tKhLC/vDs=",
58
+ "token_permute/frontend.py": "mVlwIP2pjFEq/KHEiT9t9qE3Zl+pVpeeVhceclOIVOU=",
59
+ "token_permute/torch_impl.py": "7PKSTynMOiaEaGRxEoyWz6SVrC0Lcx227Cvqdc4kB9E=",
60
+ "utils/__init__.py": "+RppytrGJc5JC8/fPz8XWYdCwAT8JOxpmLDQTlpO6gI=",
61
+ "utils/checks.py": "FP+WlZSIT7sjyzKOjnytCuFbqN2PejuKJ6SyApkZPn0=",
62
+ "utils/device.py": "3UJXsbft+HlnP2+nAnIoE1Hlq5yq9LhLSNFx9I5QHaw=",
63
+ "utils/dtype.py": "p6d+m2q4qj480Kcqg/b7WeV8+n4oRr5vi+dMb9vsxE4=",
64
+ "utils/environment.py": "GyeBUYS5y09z0h50VEL09PWCutXI57kWGxnzX5GUvVM=",
65
+ "utils/log.py": "pvlKMKeLzqrNvjHgygV985sT0TjtnbYcjluTDJ4y2Mc=",
66
+ "utils/tensor.py": "PhXmULtQny1AVmZAMByMKV7xNm/gf6M6VQa3gkjQrTQ=",
67
+ "utils/testing.py": "nl8DQHWiR6AWmecX2mHpkZxZBomc9QRHXc1MqvF9AG8=",
68
+ "utils/tuples.py": "/LrawWeD5LDugY/SD92NNcr9M3BNfs/F+8lSYs+130Y=",
69
+ "utils/varlen.py": "ON5q6wFAyBOY0PscRYht7LdGrwr5p96TcojdrprVaHI=",
70
+ "version.py": "FVIe4O+OfUJ2NXT3Ebezok+DAPBcOkgEU6tlHu0PPg8="
71
+ }
72
+ },
73
+ "provenance": {
74
+ "kernel-builder": {
75
+ "version": "0.18.0-dev0",
76
+ "commit": "21bdf69d97a289b9c581a0c60f63deed131647d5",
77
+ "dirty": false
78
+ },
79
+ "kernel": {
80
+ "commit": "5fe9dbb3d45f6f2652958c1f70e5381891fa7aa0",
81
+ "dirty": false
82
+ }
83
+ }
84
+ }
build/torch210-cu126-aarch64-linux/metadata.json.sigstore ADDED
@@ -0,0 +1 @@
 
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"certificate":{"rawBytes":"MIIHLDCCBrOgAwIBAgIUConfL87twl5y8Cnu/9yMUMstpzEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYxMDMxMDI1NzM3WhcNMjYxMDMxMDMwNzM3WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5OUqvvfMUNlTnXuzgspqC4+nmM9rjRCils9yzXXtR/Raf+lhIphGnxWm1FaoSMVuL9e6NYIxUfud8qpvORjyaaOCBdIwggXOMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSMEGDAWgBTf0+nPViQRlvmo2OkoVaLGLhhkPzBrBgNVHREBAf8EYTBfhl1odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkvLmdpdGh1Yi93b3JrZmxvd3MvYnVpbGQueWFtbEByZWZzL2hlYWRzL21haW4wOQYKKwYBBAGDvzABAQQraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTAfBgorBgEEAYO/MAECBBF3b3JrZmxvd19kaXNwYXRjaDA2BgorBgEEAYO/MAEDBCg1ZmU5ZGJiM2Q0NWY2ZjI2NTI5NThjMWY3MGU1MzgxODkxZmE3YWEwMBMGCisGAQQBg78wAQQEBUJ1aWxkMCsGCisGAQQBg78wAQUEHWh1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MB0GCisGAQQBg78wAQYED3JlZnMvaGVhZHMvbWFpbjA7BgorBgEEAYO/MAEIBC0MK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wbQYKKwYBBAGDvzABCQRfDF1odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkvLmdpdGh1Yi93b3JrZmxvd3MvYnVpbGQueWFtbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABCgQqDCg1ZmU5ZGJiM2Q0NWY2ZjI2NTI5NThjMWY3MGU1MzgxODkxZmE3YWEwMBsGCisGAQQBg78wAQsEDQwLc2VsZi1ob3N0ZWQwQAYKKwYBBAGDvzABDAQyDDBodHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkwOAYKKwYBBAGDvzABDQQqDCg1ZmU5ZGJiM2Q0NWY2ZjI2NTI5NThjMWY3MGU1MzgxODkxZmE3YWEwMB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTA3MTQ3NTUyOTAuBgorBgEEAYO/MAEQBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZTAYBgorBgEEAYO/MAERBAoMCDI1NzIwNzQzMG0GCisGAQQBg78wARIEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoNWZlOWRiYjNkNDVmNmYyNjUyOTU4YzFmNzBlNTM4MTg5MWZhN2FhMDAhBgorBgEEAYO/MAEUBBMMEXdvcmtmbG93X2Rpc3BhdGNoMGQGCisGAQQBg78wARUEVgxUaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5L2FjdGlvbnMvcnVucy80MTA4MzU5MjA4MS9hdHRlbXB0cy8xMBYGCisGAQQBg78wARYECAwGcHVibGljMEYGCisGAQQBg78wARgEOAw2cmVwbzpodWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTpyZWY6cmVmcy9oZWFkcy9tYWluMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGhj+QqDgAABAMASDBGAiEAh4ZF6nzagTaObEPoqfafHCKvVKi8mJ/GqCTKt8o+RU0CIQD68C34HN171/SvQSu9LHuEh9BNb/j2WTE71CgYiHzy3TAKBggqhkjOPQQDAwNnADBkAjBU/vEGqs8gSmAx9y4nh4nOe8z/19Gx5NswnQrF1LmFqUP42Mot+KThfeQvX04zJ/MCMEYxcKQ9BN4KT5M2ppowh5fXPpjTlv11EVBTjXWOUa9+lmgag8eLTy7qERjPA2QwdQ=="},"tlogEntries":[{"logIndex":"3608411523","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"hashedrekord","version":"0.0.1"},"integratedTime":"1793415457","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCUmIxOX/tqdAVOw5F2IiljP6VNOhlUttOb3chnkXlGuQIhAJ0qdYdqLF8FXYjptF8cDzWU8nkClAOiGuBHZRI+D8m3"},"inclusionProof":{"logIndex":"3486507261","rootHash":"rWaNS1EkW1wLyjBp5b43Nq/5vvPR6YcQQQGZDB6cQ8U=","treeSize":"3486507265","hashes":["QNaWrpma95Q2Ii9e7ecI6vH1kYwmw7kh0Tm8OM8EE34=","AFALTjX8vYkbGFpZGb5eNujiOLa479EoyhE8lsvBAqs=","Qh8cl2UbYM9EOL+CeWrW2WLvK6W1R+AKOk1abKiQor8=","8gpPATA8/zm5NyGa4N+3RZZFhLUdH0+ZpwotTIwcFYg=","9754Egf02ycYSNnC/A4rAfL956NfkfVXqOqnvdGmfF4=","fLvFj4rPJDFcCGPK+47k1APgaSzVu7kh+7/3nfcOhmc=","/6Y13fDlheN2bcnEsII/U9MAyKbIqLnFWAcVACDDQCw=","SlZV3uwFo6k72lrltHbOGXXvvNPan6CnD/J18uIb//c=","jyx4f5I0hMp434CJXP0Rut0PE282/+M4ouvnTUsV/UE=","J4FrfDFXm8W/ieq4gOlI681sJX3Ml/5l1aS1rO+w6mY=","3GcBUVQq6SethH9kno916KUFjBo/hYxuR4FwCIHSVM4=","h2QiGRcZnh9Vy0+1n4SpULgQJQzA7gslblU6vHJAgSw=","kvXR/O9NmwtrbMarzteT806WdkrAbUJZB1qow2SgxkQ=","3vFhaeVJmWIEPQcb814RjFRDT1tH7McB24OPJ32si80=","lE7/U/HjlRgHzdP8vPTBU4Zaz3UH2C7YdUvkQUuDfnQ=","Bz1DtqAVEHz4Bbo/X5KzdwHn3d91F2+g+KUOWnMj/2w=","C1iNsNunwIhtOOJvTDxee6pgbCkOjryTGo1g+5SLxEs=","2khvWt9d+ZdJVlsE9c5CUGSWz42nP08VUkB5e4aYzxI=","e3OaWwzL1tEWYXiUzGpH+1v9CiFPwweyfRzIh3Z+uO4=","b+938HIdfjdD/+LgIs3AVRE3Wc65/1NSSAL4OJ5FUXE=","kQz/4ND7LdhfAQn+idNsO+ZFC32sfkYRlhjWF5W+sPc=","zGfeoWKmrZlXMOvyi3R2GDbGpmI1skJW/wLTXCCgF0o=","1DaDKzrCNNBhmSdY5vFjz+NpAmxmYfYEgBCdPLrMP1A=","xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="],"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n3486507265\nrWaNS1EkW1wLyjBp5b43Nq/5vvPR6YcQQQGZDB6cQ8U=\n\n— rekor.sigstore.dev wNI9ajBGAiEAzJ3kviUPOvuZ83hVohKhY6vxrvD5zMXOTyIEMA4+DV8CIQDN2A0Mn/e9QgijyyztwE+qiQIsfJCBGD3wtNM6beAX4Q==\n"}},"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5MDE4YmZkYmZkMzRmOTZmNmUwZjE5ZGRkOTM1NWRhZDViNGVlMTNjMzVkNTM3ODg3ZGE0YmZiOTVlZWMzMTEyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUURTZmRNa2Z5UFJTbmFDRGhwOEg1QmxwdDVYNzFrMHpqZGdCVFpPQ2hYcUNnSWdQQjE3Ym1qZVEwWmdZTUNpTkV6TkpRM2JQeHBYS3JHMXdHQmtCQ0E2RmRBPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaE1SRU5EUW5KUFowRjNTVUpCWjBsVlEyOXVaa3c0TjNSM2JEVjVPRU51ZFM4NWVVMVZUWE4wY0hwRmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFFVFhoTlJFa3hUbnBOTTFkb1kwNU5hbGw0VFVSTmVFMUVUWGRPZWswelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUxVDFWeGRuWm1UVlZPYkZSdVdIVjZaM053Y1VNMEsyNXRUVGx5YWxKRGFXeHpPWGtLZWxoWWRGSXZVbUZtSzJ4b1NYQm9SMjU0VjIweFJtRnZVMDFXZFV3NVpUWk9XVWw0VldaMVpEaHhjSFpQVW1wNVlXRlBRMEprU1hkbloxaFBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFuSkNaMDVXU0ZKRlFrRm1PRVZaVkVKbWFHd3hiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWMllUSldlV0p0Vm5OamVURnFZakl4ZEdSWE5YQmtTR3QyVEcxa2NHUkhhREZaYVRrellqTktjZ3BhYlhoMlpETk5kbGx1Vm5CaVIxRjFaVmRHZEdKRlFubGFWMXA2VERKb2JGbFhVbnBNTWpGb1lWYzBkMDlSV1V0TGQxbENRa0ZIUkhaNlFVSkJVVkZ5Q21GSVVqQmpTRTAyVEhrNU1HSXlkR3hpYVRWb1dUTlNjR0l5TlhwTWJXUndaRWRvTVZsdVZucGFXRXBxWWpJMU1GcFhOVEJNYlU1MllsUkJaa0puYjNJS1FtZEZSVUZaVHk5TlFVVkRRa0pHTTJJelNuSmFiWGgyWkRFNWEyRllUbmRaV0ZKcVlVUkJNa0puYjNKQ1owVkZRVmxQTDAxQlJVUkNRMmN4V20xVk5RcGFSMHBwVFRKUk1FNVhXVEphYWtreVRsUkpOVTVVYUdwTlYxa3pUVWRWTVUxNlozaFBSR3Q0V20xRk0xbFhSWGROUWsxSFEybHpSMEZSVVVKbk56aDNDa0ZSVVVWQ1ZVb3hZVmQ0YTAxRGMwZERhWE5IUVZGUlFtYzNPSGRCVVZWRlNGZG9NVm95WkhCaWJXUnRXVmRPYkV3eWRHeGpiVFZzWWtoTmRGa3lPWFFLWWxoV2RXRllValZOUWpCSFEybHpSMEZSVVVKbk56aDNRVkZaUlVRelNteGFiazEyWVVkV2FGcElUWFppVjBad1ltcEJOMEpuYjNKQ1owVkZRVmxQTHdwTlFVVkpRa013VFVzeWFEQmtTRUo2VDJrNGRtUkhPWEphVnpSMVdWZE9NR0ZYT1hWamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcUNtSXlNSGRpVVZsTFMzZFpRa0pCUjBSMmVrRkNRMUZTWmtSR01XOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaFNGWnVXakpzZFZveVdtZ0tXVEpWZG1FeVZubGliVlp6WTNreGFtSXlNWFJrVnpWd1pFaHJka3h0WkhCa1IyZ3hXV2s1TTJJelNuSmFiWGgyWkROTmRsbHVWbkJpUjFGMVpWZEdkQXBpUlVKNVdsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1EyZFJjVVJEWnpGYWJWVTFXa2RLYVUweVVUQk9WMWt5Q2xwcVNUSk9WRWsxVGxSb2FrMVhXVE5OUjFVeFRYcG5lRTlFYTNoYWJVVXpXVmRGZDAxQ2MwZERhWE5IUVZGUlFtYzNPSGRCVVhORlJGRjNUR015Vm5NS1dta3hiMkl6VGpCYVYxRjNVVUZaUzB0M1dVSkNRVWRFZG5wQlFrUkJVWGxFUkVKdlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5XcGlNakIyWVVoV2JncGFNbXgxV2pKYWFGa3lWWFpoTWxaNVltMVdjMk41TVdwaU1qRjBaRmMxY0dSSWEzZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JGRlJjVVJEWnpGYWJWVTFDbHBIU21sTk1sRXdUbGRaTWxwcVNUSk9WRWsxVGxSb2FrMVhXVE5OUjFVeFRYcG5lRTlFYTNoYWJVVXpXVmRGZDAxQ09FZERhWE5IUVZGUlFtYzNPSGNLUVZFMFJVVlJkMUJqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxQ2IwZERhWE5IUVZGUlFtYzNPSGRCVVRoRlJFRjNTMDFVUVROTlZGRXpUbFJWZVFwUFZFRjFRbWR2Y2tKblJVVkJXVTh2VFVGRlVVSkRRVTFJYldnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx2WkZka2JtRlhOVzVhYlVacUNscFVRVmxDWjI5eVFtZEZSVUZaVHk5TlFVVlNRa0Z2VFVORVNURk9la2wzVG5wUmVrMUhNRWREYVhOSFFWRlJRbWMzT0hkQlVrbEZXSGQ0WkdGSVVqQUtZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW1neFdqSmtjR0p0WkcxWlYwNXNUREowYkdOdE5XeGlTRTEwV1RJNWRHSllWblZoV0ZJMVRIazFiZ3BoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3eVNqRmhWM2hyVEc1c2FHSlhlRUZqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxRVowZERhWE5IQ2tGUlVVSm5OemgzUVZKTlJVdG5kMjlPVjFwc1QxZFNhVmxxVG10T1JGWnRUbTFaZVU1cVZYbFBWRlUwV1hwR2JVNTZRbXhPVkUwMFRWUm5OVTFYV21nS1RqSkdhRTFFUVdoQ1oyOXlRbWRGUlVGWlR5OU5RVVZWUWtKTlRVVllaSFpqYlhSdFlrYzVNMWd5VW5Cak0wSm9aRWRPYjAxSFVVZERhWE5IUVZGUlFncG5OemgzUVZKVlJWWm5lRlZoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlV3eVJtcGtSMngyWW01TmRtTnVWblZqZVRnd1RWUkJORTE2VlRWTmFrRTBUVk01YUdSSVVteGlXRUl3WTNrNGVFMUNXVWNLUTJselIwRlJVVUpuTnpoM1FWSlpSVU5CZDBkalNGWnBZa2RzYWsxRldVZERhWE5IUVZGUlFtYzNPSGRCVW1kRlQwRjNNbU50Vm5kaWVuQnZaRmRrYmdwaFZ6VnVXbTFHYWxwVE9YSmFXRXAxV2xkNGVreFhUblppVnpFeFltMXNNR1ZVY0hsYVYxazJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOU1VkTUNrSm5iM0pDWjBWRlFXUmFOVUZuVVVOQ1NEQkZaWGRDTlVGSVkwRXpWREIzWVhOaVNFVlVTbXBIVWpSamJWZGpNMEZ4U2t0WWNtcGxVRXN6TDJnMGNIa0taME00Y0Rkdk5FRkJRVWRvYWl0UmNVUm5RVUZDUVUxQlUwUkNSMEZwUlVGb05GcEdObTU2WVdkVVlVOWlSVkJ2Y1daaFpraERTM1pXUzJrNGJVb3ZSd3B4UTFSTGREaHZLMUpWTUVOSlVVUTJPRU16TkVoT01UY3hMMU4yVVZOMU9VeElkVVZvT1VKT1lpOXFNbGRVUlRjeFEyZFphVWg2ZVROVVFVdENaMmR4Q21ocmFrOVFVVkZFUVhkT2JrRkVRbXRCYWtKVkwzWkZSM0Z6T0dkVGJVRjRPWGswYm1nMGJrOWxPSG92TVRsSGVEVk9jM2R1VVhKR01VeHRSbkZWVURRS01rMXZkQ3RMVkdobVpWRjJXREEwZWtvdlRVTk5SVmw0WTB0Uk9VSk9ORXRVTlUweWNIQnZkMmcxWmxoUWNHcFViSFl4TVVWV1FsUnFXRmRQVldFNUt3cHNiV2RoWnpobFRGUjVOM0ZGVW1wUVFUSlJkMlJSUFQwS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9fX19"}],"timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyTADAgEAMIICwAYJKoZIhvcNAQcCoIICsTCCAq0CAQMxDTALBglghkgBZQMEAgEwgbgGCyqGSIb3DQEJEAEEoIGoBIGlMIGiAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgeBtuKIWobPfafWLTeWzCcYQk473v8o1hFCxgUxHneBoCFQDWLvslBwhsQMCtkdTOoxfktdPljBgPMjAyNjEwMzEwMjU3MzdaMAMCAQGgMqQwMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhoAAxggHaMIIB1gIBATBRMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAsGCWCGSAFlAwQCAaCB/DAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MTAzMTAyNTczN1owLwYJKoZIhvcNAQkEMSIEILUq8Bqu4E+u4OQxwFRxE7I5oDubJkPB4rsX2ZUoyjXlMIGOBgsqhkiG9w0BCRACLzF/MH0wezB5BCCF+Se8B6tiysO0Q1bBDvyBssaIP9p6uebYcNnROs0FtzBVMD2kOzA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDAKBggqhkjOPQQDAgRmMGQCMHCbkV5CWTiOCvmEDQrDYS5FmkUxX4bFnx17paQEeX2hnzJK3SoUKlnbLo0KnqS7EQIwYW+fCUf4rgfYJBriGLKbx9HJUt8aaSNsuz+q2XEAu1pnl4Pgo9/L/2hf2vrGiHlU"}]}},"messageSignature":{"messageDigest":{"algorithm":"SHA2_256","digest":"kBi/2/00+W9uDxnd2TVdrVtO4Tw11TeIfaS/uV7sMRI="},"signature":"MEUCIQDSfdMkfyPRSnaCDhp8H5Blpt5X71k0zjdgBTZOChXqCgIgPB17bmjeQ0ZgYMCiNEzNJQ3bPxpXKrG1wGBkBCA6FdA="}}
build/torch210-cu126-aarch64-linux/modules.py ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ from typing import Optional
24
+
25
+ import torch # noqa: F401
26
+ from torch import nn, Tensor
27
+
28
+ from .functional import neighborhood_attention_generic
29
+ from ._types import (
30
+ CausalArg1DTypeOrDed,
31
+ CausalArg2DTypeOrDed,
32
+ CausalArg3DTypeOrDed,
33
+ CausalArgTypeOrDed,
34
+ Dimension1DTypeOrDed,
35
+ Dimension2DTypeOrDed,
36
+ Dimension3DTypeOrDed,
37
+ DimensionTypeOrDed,
38
+ )
39
+ from .utils.checks import check_all_args
40
+
41
+
42
+ class NeighborhoodAttentionGeneric(nn.Module):
43
+ def __init__(
44
+ self,
45
+ na_dim: int,
46
+ embed_dim: int,
47
+ num_heads: int,
48
+ kernel_size: DimensionTypeOrDed,
49
+ stride: DimensionTypeOrDed = 1,
50
+ dilation: DimensionTypeOrDed = 1,
51
+ is_causal: CausalArgTypeOrDed = False,
52
+ qkv_bias: bool = True,
53
+ qk_scale: Optional[float] = None,
54
+ proj_drop: float = 0.0,
55
+ ):
56
+ super().__init__()
57
+ kernel_size, stride, dilation, is_causal = check_all_args(
58
+ na_dim, kernel_size, stride, dilation, is_causal
59
+ )
60
+
61
+ if embed_dim % num_heads != 0:
62
+ raise ValueError(
63
+ "Number of attention heads must evenly divide embedding dimension, "
64
+ f"got {embed_dim=}, {num_heads=}."
65
+ )
66
+
67
+ self.na_dim = na_dim
68
+ self.embed_dim = embed_dim
69
+ self.num_heads = num_heads
70
+ self.head_dim = self.embed_dim // self.num_heads
71
+ self.scale = qk_scale or self.head_dim**-0.5
72
+ self.kernel_size = kernel_size
73
+ self.stride = stride
74
+ self.dilation = dilation
75
+ self.is_causal = is_causal
76
+
77
+ self.expected_input_tensor_rank = self.na_dim + 2 # batch, embedding dim
78
+
79
+ self.qkv = nn.Linear(self.embed_dim, self.embed_dim * 3, bias=qkv_bias)
80
+ self.proj = nn.Linear(self.embed_dim, self.embed_dim)
81
+ self.proj_drop = nn.Dropout(proj_drop)
82
+
83
+ def forward(self, x: Tensor) -> Tensor:
84
+ if x.dim() != self.expected_input_tensor_rank:
85
+ raise ValueError(
86
+ f"NeighborhoodAttention{self.na_dim}D expected a tensor with rank "
87
+ f"{self.expected_input_tensor_rank} ({self.na_dim} for token layout, 1 for batch, "
88
+ f"1 for embedding dimension), got {x.dim()=}."
89
+ )
90
+
91
+ B, *input_shape, C = x.shape
92
+
93
+ if C != self.embed_dim:
94
+ raise ValueError(
95
+ f"Expected embedding dimension {self.embed_dim}, got {C} ({x.shape=})."
96
+ )
97
+
98
+ # 3, batch, *input_shape, heads, head_dim
99
+ permutation = (
100
+ [self.na_dim + 1, 0]
101
+ + [x + 1 for x in range(self.na_dim)]
102
+ + [self.na_dim + 2, self.na_dim + 3]
103
+ )
104
+ qkv = (
105
+ self.qkv(x)
106
+ .reshape(B, *input_shape, 3, self.num_heads, self.head_dim)
107
+ .permute(*permutation)
108
+ )
109
+ q, k, v = qkv[0], qkv[1], qkv[2]
110
+ x = neighborhood_attention_generic( # type: ignore[assignment]
111
+ q,
112
+ k,
113
+ v,
114
+ kernel_size=self.kernel_size,
115
+ stride=self.stride,
116
+ dilation=self.dilation,
117
+ is_causal=self.is_causal,
118
+ scale=self.scale,
119
+ )
120
+ x = x.reshape(B, *input_shape, C)
121
+
122
+ return self.proj_drop(self.proj(x))
123
+
124
+ def extra_repr(self) -> str:
125
+ return (
126
+ f"head_dim={self.head_dim}, num_heads={self.num_heads}, "
127
+ + f"kernel_size={self.kernel_size}, "
128
+ + f"stride={self.stride}, "
129
+ + f"dilation={self.dilation}, "
130
+ + f"is_causal={self.is_causal}"
131
+ )
132
+
133
+
134
+ class NeighborhoodAttention1D(NeighborhoodAttentionGeneric):
135
+ """
136
+ 1-D Neighborhood Attention torch module.
137
+
138
+ Performs QKV and output linear projections in addition to the [na1d][natten.na1d] operation.
139
+
140
+ Args:
141
+ embed_dim: Embedding dimension size (a.k.a. number of channels, latent size).
142
+ !!! note
143
+ This is not `head_dim`. It's `head_dim * num_heads`.
144
+
145
+ num_heads: Number of attention heads.
146
+
147
+ kernel_size (Tuple[int] | int): Neighborhood window (kernel) size.
148
+
149
+ !!! note
150
+ `kernel_size` must be smaller than or equal to `seqlen`.
151
+
152
+ stride (Tuple[int] | int): Sliding window step size. Defaults to `1` (standard sliding
153
+ window).
154
+
155
+ !!! note
156
+ `stride` must be smaller than or equal to `kernel_size`.
157
+ When `stride == kernel_size`, there will be no overlap between sliding windows,
158
+ which is equivalent to blocked attention (a.k.a.
159
+ [window self attention](https://arxiv.org/abs/2103.14030)).
160
+
161
+ dilation (Tuple[int] | int): Dilation step size. Defaults to `1` (standard sliding window).
162
+
163
+ !!! note
164
+ The product of `dilation` and `kernel_size` must be smaller than or equal to
165
+ `seqlen`.
166
+
167
+ is_causal (Tuple[bool] | bool): Toggle causal masking. Defaults to `False`
168
+ (bi-directional).
169
+
170
+ qkv_bias: Enable bias in the QKV linear projection.
171
+
172
+ qk_scale: Attention scale. Defaults to `head_dim ** -0.5`.
173
+
174
+ proj_drop: Dropout score for projection layer. Defaults is `0.0` (no dropout).
175
+
176
+ Example:
177
+ ```python3
178
+ import torch
179
+ from . import NeighborhoodAttention1D
180
+
181
+ num_heads = 4
182
+ head_dim = 128
183
+ embed_dim = num_heads * head_dim
184
+
185
+ model = NeighborhoodAttention1D(
186
+ embed_dim=embed_dim,
187
+ num_heads=num_heads,
188
+ kernel_size=2048,
189
+ stride=2,
190
+ dilation=4,
191
+ is_causal=True
192
+ )
193
+
194
+ batch = 1
195
+ seqlen = 4096 # (1)!
196
+
197
+ x = torch.randn(batch, seqlen, embed_dim) # (2)!
198
+ y = model(x) # (3)!
199
+ ```
200
+
201
+ 1. Tokens are arranged in a sequential layout of size 4096, to which we apply a
202
+ kernel size of 2048, stride 2, dilation 4, and apply causal masking.
203
+
204
+ 2. `x.shape == [1, 4096, 512]`
205
+ 3. `y.shape == [1, 4096, 512]`
206
+ """
207
+
208
+ def __init__(
209
+ self,
210
+ embed_dim: int,
211
+ num_heads: int,
212
+ kernel_size: Dimension1DTypeOrDed,
213
+ stride: Dimension1DTypeOrDed = 1,
214
+ dilation: Dimension1DTypeOrDed = 1,
215
+ is_causal: CausalArg1DTypeOrDed = False,
216
+ qkv_bias: bool = True,
217
+ qk_scale: Optional[float] = None,
218
+ proj_drop: float = 0.0,
219
+ ):
220
+ super().__init__(
221
+ na_dim=1,
222
+ embed_dim=embed_dim,
223
+ num_heads=num_heads,
224
+ kernel_size=kernel_size,
225
+ stride=stride,
226
+ dilation=dilation,
227
+ is_causal=is_causal,
228
+ qkv_bias=qkv_bias,
229
+ qk_scale=qk_scale,
230
+ proj_drop=proj_drop,
231
+ )
232
+
233
+
234
+ class NeighborhoodAttention2D(NeighborhoodAttentionGeneric):
235
+ """
236
+ 2-D Neighborhood Attention torch module.
237
+
238
+ Performs QKV and output linear projections in addition to the [na2d][natten.na2d] operation.
239
+
240
+ Args:
241
+ embed_dim: Embedding dimension size (a.k.a. number of channels, latent size).
242
+ !!! note
243
+ This is not `head_dim`. It's `head_dim * num_heads`.
244
+
245
+ num_heads: Number of attention heads.
246
+
247
+ kernel_size (Tuple[int, int] | int): Neighborhood window (kernel) size/shape. If an
248
+ integer, it will be repeated for all 2 dimensions. For example `kernel_size=3` is
249
+ reinterpreted as `kernel_size=(3, 3)`.
250
+
251
+ !!! note
252
+ `kernel_size` must be smaller than or equal to token layout shape (`(X, Y)`) along
253
+ every dimension.
254
+
255
+ stride (Tuple[int, int] | int): Sliding window step size/shape. Defaults to `1` (standard
256
+ sliding window). If an integer, it will be repeated for all 2 dimensions. For example
257
+ `stride=2` is reinterpreted as `stride=(2, 2)`.
258
+
259
+ !!! note
260
+ `stride` must be smaller than or equal to `kernel_size` along every dimension.
261
+ When `stride == kernel_size`, there will be no overlap between sliding windows,
262
+ which is equivalent to blocked attention (a.k.a.
263
+ [window self attention](https://arxiv.org/abs/2103.14030)).
264
+
265
+ dilation (Tuple[int, int] | int): Dilation step size/shape. Defaults to `1` (standard
266
+ sliding window). If an integer, it will be repeated for all 2 dimensions. For example
267
+ `dilation=4` is reinterpreted as `dilation=(4, 4)`.
268
+
269
+ !!! note
270
+ The product of `dilation` and `kernel_size` must be smaller than or equal to
271
+ token layout shape (`(X, Y)`) along every dimension.
272
+
273
+ is_causal (Tuple[bool, bool] | bool): Toggle causal masking. Defaults to `False`
274
+ (bi-directional). If a boolean, it will be repeated for all 2 dimensions. For example
275
+ `is_causal=True` is reinterpreted as `is_causal=(True, True)`.
276
+
277
+ qkv_bias: Enable bias in the QKV linear projection.
278
+
279
+ qk_scale: Attention scale. Defaults to `head_dim ** -0.5`.
280
+
281
+ proj_drop: Dropout score for projection layer. Defaults is `0.0` (no dropout).
282
+
283
+ Example:
284
+ ```python3
285
+ import torch
286
+ from . import NeighborhoodAttention2D
287
+
288
+ num_heads = 4
289
+ head_dim = 128
290
+ embed_dim = num_heads * head_dim
291
+
292
+ model = NeighborhoodAttention2D(
293
+ embed_dim=embed_dim,
294
+ num_heads=num_heads,
295
+ kernel_size=(8, 16),
296
+ stride=(1, 2),
297
+ dilation=(2, 1),
298
+ is_causal=False
299
+ )
300
+
301
+ batch = 1
302
+ token_layout_shape = (16, 32) # (1)!
303
+
304
+ x = torch.randn(batch, *token_layout_shape, embed_dim) # (2)!
305
+ y = model(x) # (3)!
306
+ ```
307
+
308
+ 1. Tokens are arranged in a 16 x 32 layout, to which we apply a
309
+ kernel size of 8 x 16,
310
+ stride 1 x 2,
311
+ and dilation 2 x 1.
312
+
313
+ 2. `x.shape == [1, 16, 32, 512]`
314
+ 3. `y.shape == [1, 16, 32, 512]`
315
+ """
316
+
317
+ def __init__(
318
+ self,
319
+ embed_dim: int,
320
+ num_heads: int,
321
+ kernel_size: Dimension2DTypeOrDed,
322
+ stride: Dimension2DTypeOrDed = 1,
323
+ dilation: Dimension2DTypeOrDed = 1,
324
+ is_causal: CausalArg2DTypeOrDed = False,
325
+ qkv_bias: bool = True,
326
+ qk_scale: Optional[float] = None,
327
+ proj_drop: float = 0.0,
328
+ ):
329
+ super().__init__(
330
+ na_dim=2,
331
+ embed_dim=embed_dim,
332
+ num_heads=num_heads,
333
+ kernel_size=kernel_size,
334
+ stride=stride,
335
+ dilation=dilation,
336
+ is_causal=is_causal,
337
+ qkv_bias=qkv_bias,
338
+ qk_scale=qk_scale,
339
+ proj_drop=proj_drop,
340
+ )
341
+
342
+
343
+ class NeighborhoodAttention3D(NeighborhoodAttentionGeneric):
344
+ """
345
+ 3-D Neighborhood Attention torch module.
346
+
347
+ Performs QKV and output linear projections in addition to the [na3d][natten.na3d] operation.
348
+
349
+ Args:
350
+ embed_dim: Embedding dimension size (a.k.a. number of channels, latent size).
351
+ !!! note
352
+ This is not `head_dim`. It's `head_dim * num_heads`.
353
+
354
+ num_heads: Number of attention heads.
355
+
356
+ kernel_size (Tuple[int, int, int] | int): Neighborhood window (kernel) size/shape. If an
357
+ integer, it will be repeated for all 3 dimensions. For example `kernel_size=3` is
358
+ reinterpreted as `kernel_size=(3, 3, 3)`.
359
+
360
+ !!! note
361
+ `kernel_size` must be smaller than or equal to token layout shape (`(X, Y, Z)`)
362
+ along every dimension.
363
+
364
+ stride (Tuple[int, int, int] | int): Sliding window step size/shape. Defaults to `1`
365
+ (standard sliding window). If an integer, it will be repeated for all 3 dimensions.
366
+ For example `stride=2` is reinterpreted as `stride=(2, 2, 2)`.
367
+
368
+ !!! note
369
+ `stride` must be smaller than or equal to `kernel_size` along every dimension.
370
+ When `stride == kernel_size`, there will be no overlap between sliding windows,
371
+ which is equivalent to blocked attention (a.k.a.
372
+ [window self attention](https://arxiv.org/abs/2103.14030)).
373
+
374
+ dilation (Tuple[int, int, int] | int): Dilation step size/shape. Defaults to `1` (standard
375
+ sliding window). If an integer, it will be repeated for all 3 dimensions. For example
376
+ `dilation=4` is reinterpreted as `dilation=(4, 4, 4)`.
377
+
378
+ !!! note
379
+ The product of `dilation` and `kernel_size` must be smaller than or equal to
380
+ token layout shape (`(X, Y, Z)`) along every dimension.
381
+
382
+ is_causal (Tuple[bool, bool, bool] | bool): Toggle causal masking. Defaults to `False`
383
+ (bi-directional). If a boolean, it will be repeated for all 3 dimensions. For example
384
+ `is_causal=True` is reinterpreted as `is_causal=(True, True, True)`.
385
+
386
+ qkv_bias: Enable bias in the QKV linear projection.
387
+
388
+ qk_scale: Attention scale. Defaults to `head_dim ** -0.5`.
389
+
390
+ proj_drop: Dropout score for projection layer. Defaults is `0.0` (no dropout).
391
+
392
+ Example:
393
+ ```python3
394
+ import torch
395
+ from . import NeighborhoodAttention3D
396
+
397
+ num_heads = 4
398
+ head_dim = 128
399
+ embed_dim = num_heads * head_dim
400
+
401
+ model = NeighborhoodAttention3D(
402
+ embed_dim=embed_dim,
403
+ num_heads=num_heads,
404
+ kernel_size=(4, 8, 12),
405
+ stride=(1, 1, 4),
406
+ dilation=(1, 2, 1),
407
+ is_causal=(True, False, False)
408
+ )
409
+
410
+ batch = 1
411
+ token_layout_shape = (12, 16, 20) # (1)!
412
+
413
+ x = torch.randn(batch, *token_layout_shape, embed_dim) # (2)!
414
+ y = model(x) # (3)!
415
+ ```
416
+
417
+ 1. Tokens are arranged in a 12 x 16 x 20 layout, to which we apply a
418
+ kernel size of 4 x 8 x 12,
419
+ stride 1 x 1 x 4,
420
+ dilation 1 x 2 x 1, and apply causal masking to the left-most dimension (12).
421
+
422
+ 2. `x.shape == [1, 12, 16, 20, 512]`
423
+ 3. `y.shape == [1, 12, 16, 20, 512]`
424
+ """
425
+
426
+ def __init__(
427
+ self,
428
+ embed_dim: int,
429
+ num_heads: int,
430
+ kernel_size: Dimension3DTypeOrDed,
431
+ stride: Dimension3DTypeOrDed = 1,
432
+ dilation: Dimension3DTypeOrDed = 1,
433
+ is_causal: CausalArg3DTypeOrDed = False,
434
+ qkv_bias: bool = True,
435
+ qk_scale: Optional[float] = None,
436
+ proj_drop: float = 0.0,
437
+ ):
438
+ super().__init__(
439
+ na_dim=3,
440
+ embed_dim=embed_dim,
441
+ num_heads=num_heads,
442
+ kernel_size=kernel_size,
443
+ stride=stride,
444
+ dilation=dilation,
445
+ is_causal=is_causal,
446
+ qkv_bias=qkv_bias,
447
+ qk_scale=qk_scale,
448
+ proj_drop=proj_drop,
449
+ )
build/torch210-cu126-aarch64-linux/symbols.json ADDED
@@ -0,0 +1,1257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema_version": 1,
3
+ "module": "_natten_cuda_5fe9dbb",
4
+ "functions": [
5
+ {
6
+ "name": "are_deterministic_algorithms_enabled",
7
+ "qualname": "are_deterministic_algorithms_enabled",
8
+ "module": "_natten_cuda_5fe9dbb.context",
9
+ "kind": "function",
10
+ "doc": null,
11
+ "signature": {
12
+ "parameters": [],
13
+ "return_annotation": "bool"
14
+ }
15
+ },
16
+ {
17
+ "name": "use_deterministic_algorithms",
18
+ "qualname": "use_deterministic_algorithms",
19
+ "module": "_natten_cuda_5fe9dbb.context",
20
+ "kind": "function",
21
+ "doc": null,
22
+ "signature": {
23
+ "parameters": [
24
+ {
25
+ "name": "mode",
26
+ "kind": "POSITIONAL_OR_KEYWORD",
27
+ "default": "True",
28
+ "annotation": "bool"
29
+ }
30
+ ],
31
+ "return_annotation": null
32
+ }
33
+ },
34
+ {
35
+ "name": "use_kv_parallelism_in_fused_na",
36
+ "qualname": "use_kv_parallelism_in_fused_na",
37
+ "module": "_natten_cuda_5fe9dbb.context",
38
+ "kind": "function",
39
+ "doc": "Sets guards for using KV Parallelism in backpropagation in `\"cutlass-fna\"`/`\"cutlass-fmha\"`\nbackends.\n\nWarning:\n Disabling KV parallelism can significantly slow down training, particularly in\n small-batch/head and large-token problems.\n\nArgs:\n mode: If `True`, allows KV parallelism (default setting), and otherwise disables it.",
40
+ "signature": {
41
+ "parameters": [
42
+ {
43
+ "name": "mode",
44
+ "kind": "POSITIONAL_OR_KEYWORD",
45
+ "default": "True",
46
+ "annotation": "bool"
47
+ }
48
+ ],
49
+ "return_annotation": null
50
+ }
51
+ },
52
+ {
53
+ "name": "is_kv_parallelism_in_fused_na_enabled",
54
+ "qualname": "is_kv_parallelism_in_fused_na_enabled",
55
+ "module": "_natten_cuda_5fe9dbb.context",
56
+ "kind": "function",
57
+ "doc": "Returns whether KV parallelism in `\"cutlass-fna\"` and `\"cutlass-fmha\"` backends is enabled.",
58
+ "signature": {
59
+ "parameters": [],
60
+ "return_annotation": "bool"
61
+ }
62
+ },
63
+ {
64
+ "name": "set_memory_usage_preference",
65
+ "qualname": "set_memory_usage_preference",
66
+ "module": "_natten_cuda_5fe9dbb.context",
67
+ "kind": "function",
68
+ "doc": "Sets memory usage preference for KV parallelism in `\"cutlass-fna\"` and `\"cutlass-fmha\"`\nbackends.\n\nArgs:\n pref: Choices are `\"default\"`, `\"strict\"`, and `\"unrestricted\"`.",
69
+ "signature": {
70
+ "parameters": [
71
+ {
72
+ "name": "pref",
73
+ "kind": "POSITIONAL_OR_KEYWORD",
74
+ "default": "'default'",
75
+ "annotation": "str"
76
+ }
77
+ ],
78
+ "return_annotation": null
79
+ }
80
+ },
81
+ {
82
+ "name": "get_memory_usage_preference",
83
+ "qualname": "get_memory_usage_preference",
84
+ "module": "_natten_cuda_5fe9dbb.context",
85
+ "kind": "function",
86
+ "doc": null,
87
+ "signature": {
88
+ "parameters": [],
89
+ "return_annotation": "_natten_cuda_5fe9dbb.context.MemoryUsagePreference"
90
+ }
91
+ },
92
+ {
93
+ "name": "is_memory_usage_default",
94
+ "qualname": "is_memory_usage_default",
95
+ "module": "_natten_cuda_5fe9dbb.context",
96
+ "kind": "function",
97
+ "doc": "Returns whether memory usage preference for KV parallelism in `\"cutlass-fna\"` and\n`\"cutlass-fmha\"` backends is the default setting.",
98
+ "signature": {
99
+ "parameters": [],
100
+ "return_annotation": "bool"
101
+ }
102
+ },
103
+ {
104
+ "name": "is_memory_usage_strict",
105
+ "qualname": "is_memory_usage_strict",
106
+ "module": "_natten_cuda_5fe9dbb.context",
107
+ "kind": "function",
108
+ "doc": "Returns whether memory usage preference for KV parallelism in `\"cutlass-fna\"` and\n`\"cutlass-fmha\"` backends is the *restricted* setting.",
109
+ "signature": {
110
+ "parameters": [],
111
+ "return_annotation": "bool"
112
+ }
113
+ },
114
+ {
115
+ "name": "is_memory_usage_unrestricted",
116
+ "qualname": "is_memory_usage_unrestricted",
117
+ "module": "_natten_cuda_5fe9dbb.context",
118
+ "kind": "function",
119
+ "doc": "Returns whether memory usage preference for KV parallelism in `\"cutlass-fna\"` and\n`\"cutlass-fmha\"` backends is the *unrestricted* setting.",
120
+ "signature": {
121
+ "parameters": [],
122
+ "return_annotation": "bool"
123
+ }
124
+ },
125
+ {
126
+ "name": "is_flex_compile_allowed",
127
+ "qualname": "is_flex_compile_allowed",
128
+ "module": "_natten_cuda_5fe9dbb.context",
129
+ "kind": "function",
130
+ "doc": "Returns whether compilation is allowed in `\"flex-fna\"` and `\"flex-fmha\"` backends.",
131
+ "signature": {
132
+ "parameters": [],
133
+ "return_annotation": "bool"
134
+ }
135
+ },
136
+ {
137
+ "name": "is_flex_compile_backprop_allowed",
138
+ "qualname": "is_flex_compile_backprop_allowed",
139
+ "module": "_natten_cuda_5fe9dbb.context",
140
+ "kind": "function",
141
+ "doc": "Returns whether compilation for backpropagation is allowed in `\"flex-fna\"` and `\"flex-fmha\"`\nbackends.",
142
+ "signature": {
143
+ "parameters": [],
144
+ "return_annotation": "bool"
145
+ }
146
+ },
147
+ {
148
+ "name": "allow_flex_compile",
149
+ "qualname": "allow_flex_compile",
150
+ "module": "_natten_cuda_5fe9dbb.context",
151
+ "kind": "function",
152
+ "doc": "Sets guards for Flex Attention + `torch.compile`.\n\nAllows using our Flex FNA / Flex FMHA backends with `torch.compile`, meaning you can\npass `torch_compile=True` to the `na{1,2,3}d` or `attention` operation, along with\n`backend=\"flex-fna\"`/`backend=\"flex-fmha\"`, and NATTEN will compile the block-sparse mask, as\nwell as the attention operation using `torch.compile` for you.\n\nWarning:\n We have been *unable to verify the correctness* of this setting under all of our use\n cases. We are working on raising this issue with PyTorch directly, but until then we strongly\n recommend exercising caution when using this feature.\n\nDanger: backprop=True is strongly discouraged!\n Allowing `torch.compile` for backpropagation (detected by checking\n `tensor.requires_grad`) is guarded separately. We strongly recommend NOT using this setting, as\n it can impact your training results.\n\nArgs:\n mode: If `True`, enable compilation for forward pass, otherwise disable.\n backprop: If `True`, assuming compilation for forward pass is allowed, enable compilation\n for backward pass, otherwise disable.",
153
+ "signature": {
154
+ "parameters": [
155
+ {
156
+ "name": "mode",
157
+ "kind": "POSITIONAL_OR_KEYWORD",
158
+ "default": "True",
159
+ "annotation": "bool"
160
+ },
161
+ {
162
+ "name": "backprop",
163
+ "kind": "POSITIONAL_OR_KEYWORD",
164
+ "default": "False",
165
+ "annotation": "bool"
166
+ }
167
+ ],
168
+ "return_annotation": null
169
+ }
170
+ },
171
+ {
172
+ "name": "allow_flex_compile_backprop",
173
+ "qualname": "allow_flex_compile_backprop",
174
+ "module": "_natten_cuda_5fe9dbb.context",
175
+ "kind": "function",
176
+ "doc": "Sets guards for Flex Attention + `torch.compile` for backpropagation only.\n\nArgs:\n mode: If `True`, enable compilation for backprop (assuming forward compilation is already\n enabled), otherwise disable.",
177
+ "signature": {
178
+ "parameters": [
179
+ {
180
+ "name": "mode",
181
+ "kind": "POSITIONAL_OR_KEYWORD",
182
+ "default": "True",
183
+ "annotation": "bool"
184
+ }
185
+ ],
186
+ "return_annotation": null
187
+ }
188
+ },
189
+ {
190
+ "name": "disable_flex_compile",
191
+ "qualname": "disable_flex_compile",
192
+ "module": "_natten_cuda_5fe9dbb.context",
193
+ "kind": "function",
194
+ "doc": "Disallow Flex Attention + `torch.compile` entirely.",
195
+ "signature": {
196
+ "parameters": [],
197
+ "return_annotation": null
198
+ }
199
+ },
200
+ {
201
+ "name": "disable_flex_compile_backprop",
202
+ "qualname": "disable_flex_compile_backprop",
203
+ "module": "_natten_cuda_5fe9dbb.context",
204
+ "kind": "function",
205
+ "doc": "Disallow Flex Attention + `torch.compile` for backpropagation entirely.",
206
+ "signature": {
207
+ "parameters": [],
208
+ "return_annotation": null
209
+ }
210
+ },
211
+ {
212
+ "name": "get_bwd_configs_for_cutlass_fmha",
213
+ "qualname": "get_bwd_configs_for_cutlass_fmha",
214
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
215
+ "kind": "function",
216
+ "doc": "Returns CUTLASS FMHA backward pass configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,\nreturns *backward pass* configurations compatible with the specific compute capability, tensor\ndtype and head dim.\n\nEach configuration for this operation is a tuple of two integers: `(backward_q_tile_size,\nbackward_kv_tile_size)`. These are arguments to [natten.attention][natten.attention].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile\n sizes in the *backward pass*.",
217
+ "signature": {
218
+ "parameters": [
219
+ {
220
+ "name": "query",
221
+ "kind": "POSITIONAL_OR_KEYWORD",
222
+ "default": null,
223
+ "annotation": "torch.Tensor"
224
+ },
225
+ {
226
+ "name": "key",
227
+ "kind": "POSITIONAL_OR_KEYWORD",
228
+ "default": null,
229
+ "annotation": "torch.Tensor"
230
+ },
231
+ {
232
+ "name": "value",
233
+ "kind": "POSITIONAL_OR_KEYWORD",
234
+ "default": null,
235
+ "annotation": "torch.Tensor"
236
+ }
237
+ ],
238
+ "return_annotation": "List[Tuple[int, int]]"
239
+ }
240
+ },
241
+ {
242
+ "name": "get_bwd_configs_for_cutlass_fna",
243
+ "qualname": "get_bwd_configs_for_cutlass_fna",
244
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
245
+ "kind": "function",
246
+ "doc": "Returns CUTLASS FNA backward pass configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,\nreturns *backward pass* configurations compatible with the specific compute capability, tensor\ndtype and head dim.\n\nEach configuration for this operation is a tuple of two integers: `(backward_q_tile_shape,\nbackward_kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d],\n[natten.na2d][natten.na2d], and [natten.na3d][natten.na3d].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query\n and KV tile *shapes* in the *backward pass*.",
247
+ "signature": {
248
+ "parameters": [
249
+ {
250
+ "name": "query",
251
+ "kind": "POSITIONAL_OR_KEYWORD",
252
+ "default": null,
253
+ "annotation": "torch.Tensor"
254
+ },
255
+ {
256
+ "name": "key",
257
+ "kind": "POSITIONAL_OR_KEYWORD",
258
+ "default": null,
259
+ "annotation": "torch.Tensor"
260
+ },
261
+ {
262
+ "name": "value",
263
+ "kind": "POSITIONAL_OR_KEYWORD",
264
+ "default": null,
265
+ "annotation": "torch.Tensor"
266
+ }
267
+ ],
268
+ "return_annotation": "List[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]]]"
269
+ }
270
+ },
271
+ {
272
+ "name": "get_configs_for_cutlass_fmha",
273
+ "qualname": "get_configs_for_cutlass_fmha",
274
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
275
+ "kind": "function",
276
+ "doc": "Returns CUTLASS FMHA configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,\nreturns *forward pass* configurations compatible with the specific compute capability, tensor\ndtype and head dim.\n\nEach configuration for this operation is a tuple of two integers: `(q_tile_size,\nkv_tile_size)`. These are arguments to [natten.attention][natten.attention].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile\n sizes.",
277
+ "signature": {
278
+ "parameters": [
279
+ {
280
+ "name": "query",
281
+ "kind": "POSITIONAL_OR_KEYWORD",
282
+ "default": null,
283
+ "annotation": "torch.Tensor"
284
+ },
285
+ {
286
+ "name": "key",
287
+ "kind": "POSITIONAL_OR_KEYWORD",
288
+ "default": null,
289
+ "annotation": "torch.Tensor"
290
+ },
291
+ {
292
+ "name": "value",
293
+ "kind": "POSITIONAL_OR_KEYWORD",
294
+ "default": null,
295
+ "annotation": "torch.Tensor"
296
+ }
297
+ ],
298
+ "return_annotation": "List[Tuple[int, int]]"
299
+ }
300
+ },
301
+ {
302
+ "name": "get_configs_for_cutlass_fna",
303
+ "qualname": "get_configs_for_cutlass_fna",
304
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
305
+ "kind": "function",
306
+ "doc": "Returns CUTLASS FNA configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a device with compute capability >= 5.0, and if so,\nreturns *forward pass* configurations compatible with the specific compute capability, tensor\ndtype and head dim, and according to the rank of the token layout (1D/2D/3D).\n\nEach configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,\nkv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],\nand [natten.na3d][natten.na3d].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query\n and KV tile *shapes*.",
307
+ "signature": {
308
+ "parameters": [
309
+ {
310
+ "name": "query",
311
+ "kind": "POSITIONAL_OR_KEYWORD",
312
+ "default": null,
313
+ "annotation": "torch.Tensor"
314
+ },
315
+ {
316
+ "name": "key",
317
+ "kind": "POSITIONAL_OR_KEYWORD",
318
+ "default": null,
319
+ "annotation": "torch.Tensor"
320
+ },
321
+ {
322
+ "name": "value",
323
+ "kind": "POSITIONAL_OR_KEYWORD",
324
+ "default": null,
325
+ "annotation": "torch.Tensor"
326
+ }
327
+ ],
328
+ "return_annotation": "List[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]]]"
329
+ }
330
+ },
331
+ {
332
+ "name": "get_configs_for_cutlass_hopper_fmha",
333
+ "qualname": "get_configs_for_cutlass_hopper_fmha",
334
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
335
+ "kind": "function",
336
+ "doc": "Returns Hopper FMHA configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,\nreturns *forward pass* configurations compatible with the tensor dtype and head dim.\n\nEach configuration for this operation is a tuple of one integer tuple, and another integer:\n`((q_tile_size, kv_tile_size), kernel_schedule)`. These are arguments to\n[natten.attention][natten.attention].\n`kernel_schedule` is specific to Hopper FNA/FMHA only.\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[Tuple[int, int], KernelSchedule]]): List of tuples of one tuple of two integers\n corresponding to query and KV tile sizes, and a kernel schedule enum type.",
337
+ "signature": {
338
+ "parameters": [
339
+ {
340
+ "name": "query",
341
+ "kind": "POSITIONAL_OR_KEYWORD",
342
+ "default": null,
343
+ "annotation": "torch.Tensor"
344
+ },
345
+ {
346
+ "name": "key",
347
+ "kind": "POSITIONAL_OR_KEYWORD",
348
+ "default": null,
349
+ "annotation": "torch.Tensor"
350
+ },
351
+ {
352
+ "name": "value",
353
+ "kind": "POSITIONAL_OR_KEYWORD",
354
+ "default": null,
355
+ "annotation": "torch.Tensor"
356
+ }
357
+ ],
358
+ "return_annotation": "List[Tuple[Tuple[int, int], _natten_cuda_5fe9dbb._types.KernelSchedule]]"
359
+ }
360
+ },
361
+ {
362
+ "name": "get_bwd_configs_for_cutlass_hopper_fmha",
363
+ "qualname": "get_bwd_configs_for_cutlass_hopper_fmha",
364
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
365
+ "kind": "function",
366
+ "doc": "Returns Hopper FMHA backward pass configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,\nreturns *backward pass* configurations compatible with the tensor dtype and head dim.\n\nEach configuration for this operation is an integer tuple:\n`(backward_q_tile_size, backward_kv_tile_size)`. These are arguments to\n[natten.attention][natten.attention].\n\nNote that unlike forward pass, kernel schedule is not part of the configuration. All backward\npass kernels are persistent warp-specialized.\nSee CUTLASS's [example 88](https://github.com/NVIDIA/cutlass/tree/main/examples/88_hopper_fmha)\nfor more.\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[int, int]]): List of integer tuples corresponding to query and KV tile sizes.",
367
+ "signature": {
368
+ "parameters": [
369
+ {
370
+ "name": "query",
371
+ "kind": "POSITIONAL_OR_KEYWORD",
372
+ "default": null,
373
+ "annotation": "torch.Tensor"
374
+ },
375
+ {
376
+ "name": "key",
377
+ "kind": "POSITIONAL_OR_KEYWORD",
378
+ "default": null,
379
+ "annotation": "torch.Tensor"
380
+ },
381
+ {
382
+ "name": "value",
383
+ "kind": "POSITIONAL_OR_KEYWORD",
384
+ "default": null,
385
+ "annotation": "torch.Tensor"
386
+ }
387
+ ],
388
+ "return_annotation": "List[Tuple[int, int]]"
389
+ }
390
+ },
391
+ {
392
+ "name": "get_configs_for_cutlass_hopper_fna",
393
+ "qualname": "get_configs_for_cutlass_hopper_fna",
394
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
395
+ "kind": "function",
396
+ "doc": "Returns Hopper FNA configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,\nreturns *forward pass* configurations compatible with the tensor dtype and head dim.\n\nEach configuration for this operation is a tuple of one tuple, and another integer:\n`((q_tile_shape, kv_tile_shape), kernel_schedule)`. These are arguments to\n[natten.na1d][natten.na1d], [natten.na2d][natten.na2d], and [natten.na3d][natten.na3d].\n\n`kernel_schedule` is specific to Hopper FNA/FMHA only.\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[Tuple[tuple, tuple], KernelSchedule]]): List of tuples of one tuple of two\n shape tuples, corresponding to query and KV tile *shapes*, and a kernel schedule enum\n type.",
397
+ "signature": {
398
+ "parameters": [
399
+ {
400
+ "name": "query",
401
+ "kind": "POSITIONAL_OR_KEYWORD",
402
+ "default": null,
403
+ "annotation": "torch.Tensor"
404
+ },
405
+ {
406
+ "name": "key",
407
+ "kind": "POSITIONAL_OR_KEYWORD",
408
+ "default": null,
409
+ "annotation": "torch.Tensor"
410
+ },
411
+ {
412
+ "name": "value",
413
+ "kind": "POSITIONAL_OR_KEYWORD",
414
+ "default": null,
415
+ "annotation": "torch.Tensor"
416
+ }
417
+ ],
418
+ "return_annotation": "List[Tuple[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]], _natten_cuda_5fe9dbb._types.KernelSchedule]]"
419
+ }
420
+ },
421
+ {
422
+ "name": "get_bwd_configs_for_cutlass_hopper_fna",
423
+ "qualname": "get_bwd_configs_for_cutlass_hopper_fna",
424
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
425
+ "kind": "function",
426
+ "doc": "Returns Hopper FNA backward pass configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Hopper GPU (SM90; compute capability 9.0), and if so,\nreturns *backward pass* configurations compatible with the tensor dtype and head dim.\n\nEach configuration for this operation is a tuple of two tuples:\n`(q_tile_shape, kv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d],\n[natten.na2d][natten.na2d], and [natten.na3d][natten.na3d].\n\nNote that unlike forward pass, kernel schedule is not part of the configuration. All backward\npass kernels are persistent warp-specialized.\nSee CUTLASS's [example 88](https://github.com/NVIDIA/cutlass/tree/main/examples/88_hopper_fmha)\nfor more.\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[tuple, tuple]]): List of tuples of two shape tuples, corresponding to query and\n KV tile *shapes*.",
427
+ "signature": {
428
+ "parameters": [
429
+ {
430
+ "name": "query",
431
+ "kind": "POSITIONAL_OR_KEYWORD",
432
+ "default": null,
433
+ "annotation": "torch.Tensor"
434
+ },
435
+ {
436
+ "name": "key",
437
+ "kind": "POSITIONAL_OR_KEYWORD",
438
+ "default": null,
439
+ "annotation": "torch.Tensor"
440
+ },
441
+ {
442
+ "name": "value",
443
+ "kind": "POSITIONAL_OR_KEYWORD",
444
+ "default": null,
445
+ "annotation": "torch.Tensor"
446
+ }
447
+ ],
448
+ "return_annotation": "List[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]]]"
449
+ }
450
+ },
451
+ {
452
+ "name": "get_bwd_configs_for_cutlass_blackwell_fmha",
453
+ "qualname": "get_bwd_configs_for_cutlass_blackwell_fmha",
454
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
455
+ "kind": "function",
456
+ "doc": "Returns Blackwell FMHA backward pass configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability\n10.0), and if so, returns *backward pass* configurations compatible with the tensor dtype and\nhead dim.\n\nEach configuration for this operation is a tuple of two integers: `(q_tile_size,\nkv_tile_size)`. These are arguments to [natten.attention][natten.attention].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile\n sizes.",
457
+ "signature": {
458
+ "parameters": [
459
+ {
460
+ "name": "query",
461
+ "kind": "POSITIONAL_OR_KEYWORD",
462
+ "default": null,
463
+ "annotation": "torch.Tensor"
464
+ },
465
+ {
466
+ "name": "key",
467
+ "kind": "POSITIONAL_OR_KEYWORD",
468
+ "default": null,
469
+ "annotation": "torch.Tensor"
470
+ },
471
+ {
472
+ "name": "value",
473
+ "kind": "POSITIONAL_OR_KEYWORD",
474
+ "default": null,
475
+ "annotation": "torch.Tensor"
476
+ }
477
+ ],
478
+ "return_annotation": "List[Tuple[int, int]]"
479
+ }
480
+ },
481
+ {
482
+ "name": "get_bwd_configs_for_cutlass_blackwell_fna",
483
+ "qualname": "get_bwd_configs_for_cutlass_blackwell_fna",
484
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
485
+ "kind": "function",
486
+ "doc": "Returns Blackwell FNA backward pass configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability\n10.0), and if so, returns *backward pass* configurations compatible with the tensor dtype and\nhead dim, and according to the rank of the token layout (1D/2D/3D).\n\nEach configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,\nkv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],\nand [natten.na3d][natten.na3d].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query\n and KV tile *shapes*.",
487
+ "signature": {
488
+ "parameters": [
489
+ {
490
+ "name": "query",
491
+ "kind": "POSITIONAL_OR_KEYWORD",
492
+ "default": null,
493
+ "annotation": "torch.Tensor"
494
+ },
495
+ {
496
+ "name": "key",
497
+ "kind": "POSITIONAL_OR_KEYWORD",
498
+ "default": null,
499
+ "annotation": "torch.Tensor"
500
+ },
501
+ {
502
+ "name": "value",
503
+ "kind": "POSITIONAL_OR_KEYWORD",
504
+ "default": null,
505
+ "annotation": "torch.Tensor"
506
+ }
507
+ ],
508
+ "return_annotation": "List[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]]]"
509
+ }
510
+ },
511
+ {
512
+ "name": "get_configs_for_cutlass_blackwell_fmha",
513
+ "qualname": "get_configs_for_cutlass_blackwell_fmha",
514
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
515
+ "kind": "function",
516
+ "doc": "Returns Blackwell FMHA configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability\n10.0), and if so, returns *forward pass* configurations compatible with the tensor dtype and\nhead dim.\n\nEach configuration for this operation is a tuple of two integers: `(q_tile_size,\nkv_tile_size)`. These are arguments to [natten.attention][natten.attention].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile\n sizes.",
517
+ "signature": {
518
+ "parameters": [
519
+ {
520
+ "name": "query",
521
+ "kind": "POSITIONAL_OR_KEYWORD",
522
+ "default": null,
523
+ "annotation": "torch.Tensor"
524
+ },
525
+ {
526
+ "name": "key",
527
+ "kind": "POSITIONAL_OR_KEYWORD",
528
+ "default": null,
529
+ "annotation": "torch.Tensor"
530
+ },
531
+ {
532
+ "name": "value",
533
+ "kind": "POSITIONAL_OR_KEYWORD",
534
+ "default": null,
535
+ "annotation": "torch.Tensor"
536
+ }
537
+ ],
538
+ "return_annotation": "List[Tuple[int, int]]"
539
+ }
540
+ },
541
+ {
542
+ "name": "get_configs_for_cutlass_blackwell_fna",
543
+ "qualname": "get_configs_for_cutlass_blackwell_fna",
544
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
545
+ "kind": "function",
546
+ "doc": "Returns Blackwell FNA configurations compatible with input tensors, if any.\n\nChecks first if a CUDA tensor, and on a Blackwell datacenter GPU (SM100; compute capability\n10.0), and if so, returns *forward pass* configurations compatible with the tensor dtype and\nhead dim, and according to the rank of the token layout (1D/2D/3D).\n\nEach configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,\nkv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],\nand [natten.na3d][natten.na3d].\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n\nReturns:\n (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query\n and KV tile *shapes*.",
547
+ "signature": {
548
+ "parameters": [
549
+ {
550
+ "name": "query",
551
+ "kind": "POSITIONAL_OR_KEYWORD",
552
+ "default": null,
553
+ "annotation": "torch.Tensor"
554
+ },
555
+ {
556
+ "name": "key",
557
+ "kind": "POSITIONAL_OR_KEYWORD",
558
+ "default": null,
559
+ "annotation": "torch.Tensor"
560
+ },
561
+ {
562
+ "name": "value",
563
+ "kind": "POSITIONAL_OR_KEYWORD",
564
+ "default": null,
565
+ "annotation": "torch.Tensor"
566
+ }
567
+ ],
568
+ "return_annotation": "List[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]]]"
569
+ }
570
+ },
571
+ {
572
+ "name": "get_configs_for_flex_fmha",
573
+ "qualname": "get_configs_for_flex_fmha",
574
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
575
+ "kind": "function",
576
+ "doc": "Returns Flex FMHA configurations compatible with input tensors, if any.\n\nEach configuration for this operation is a tuple of two integers: `(q_tile_size,\nkv_tile_size)`. These are arguments to [natten.attention][natten.attention].\nNot specifying these arguments while backend is Flex will default to `q_tile_size = 64` and\n`kv_tile_size = 64`.\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n torch_compile: Whether or not you intend to use compiled block mask and flex attention kernel.\n\nReturns:\n (List[Tuple[int, int]]): List of tuples of two integers corresponding to query and KV tile\n sizes.",
577
+ "signature": {
578
+ "parameters": [
579
+ {
580
+ "name": "query",
581
+ "kind": "POSITIONAL_OR_KEYWORD",
582
+ "default": null,
583
+ "annotation": "torch.Tensor"
584
+ },
585
+ {
586
+ "name": "key",
587
+ "kind": "POSITIONAL_OR_KEYWORD",
588
+ "default": null,
589
+ "annotation": "torch.Tensor"
590
+ },
591
+ {
592
+ "name": "value",
593
+ "kind": "POSITIONAL_OR_KEYWORD",
594
+ "default": null,
595
+ "annotation": "torch.Tensor"
596
+ },
597
+ {
598
+ "name": "torch_compile",
599
+ "kind": "POSITIONAL_OR_KEYWORD",
600
+ "default": "False",
601
+ "annotation": "bool"
602
+ }
603
+ ],
604
+ "return_annotation": "List[Tuple[int, int]]"
605
+ }
606
+ },
607
+ {
608
+ "name": "get_configs_for_flex_fna",
609
+ "qualname": "get_configs_for_flex_fna",
610
+ "module": "_natten_cuda_5fe9dbb.backends.configs",
611
+ "kind": "function",
612
+ "doc": "Returns Flex FNA configurations compatible with input tensors, if any.\n\nEach configuration for this operation is a tuple of two integer tuples: `(q_tile_shape,\nkv_tile_shape)`. These are arguments to [natten.na1d][natten.na1d], [natten.na2d][natten.na2d],\nand [natten.na3d][natten.na3d].\nNot specifying these arguments while backend is Flex will default to single-dimensional tiling,\nand will not use our Token Permutation approach. By explicitly specifying tile shapes, you will\nautomatically use our Token Permutation approach, which saves you the most compute.\n\nArgs:\n query: Query tensor matching the shape, dtype, and device of your use case.\n key: Key tensor matching the shape, dtype, and device of your use case.\n value: Value tensor matching the shape, dtype, and device of your use case.\n torch_compile: Whether or not you intend to use compiled block mask and flex attention kernel.\n\nReturns:\n (List[Tuple[tuple, tuple]]): List of tuples of two integer tuples corresponding to query\n and KV tile *shapes*.",
613
+ "signature": {
614
+ "parameters": [
615
+ {
616
+ "name": "query",
617
+ "kind": "POSITIONAL_OR_KEYWORD",
618
+ "default": null,
619
+ "annotation": "torch.Tensor"
620
+ },
621
+ {
622
+ "name": "key",
623
+ "kind": "POSITIONAL_OR_KEYWORD",
624
+ "default": null,
625
+ "annotation": "torch.Tensor"
626
+ },
627
+ {
628
+ "name": "value",
629
+ "kind": "POSITIONAL_OR_KEYWORD",
630
+ "default": null,
631
+ "annotation": "torch.Tensor"
632
+ },
633
+ {
634
+ "name": "torch_compile",
635
+ "kind": "POSITIONAL_OR_KEYWORD",
636
+ "default": "False",
637
+ "annotation": "bool"
638
+ }
639
+ ],
640
+ "return_annotation": "List[Tuple[Tuple[int], Tuple[int]] | Tuple[Tuple[int, int], Tuple[int, int]] | Tuple[Tuple[int, int, int], Tuple[int, int, int]]]"
641
+ }
642
+ },
643
+ {
644
+ "name": "na1d",
645
+ "qualname": "na1d",
646
+ "module": "_natten_cuda_5fe9dbb.functional",
647
+ "kind": "function",
648
+ "doc": "Computes 1-D neighborhood attention.\n\nGQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fna` and\n`flex-fna` support GQA/MQA natively, and `cutlass-fna` and `hopper-fna` support it with\nexplicit repeats (increases memory usage and runtime).\n\nParameters:\n query (Tensor): 4-D query tensor, with the heads last layout\n (`[batch, seqlen, heads, head_dim]`)\n\n key (Tensor): 4-D key tensor, with the heads last layout\n (`[batch, seqlen, heads_kv, head_dim]`)\n\n value (Tensor): 4-D value tensor, with the heads last layout\n (`[batch, seqlen, heads_kv, head_dim_v]`)\n\n kernel_size (Tuple[int] | int): Neighborhood window (kernel) size.\n\n !!! note\n `kernel_size` must be smaller than or equal to `seqlen`.\n\n stride (Tuple[int] | int): Sliding window step size. Defaults to `1` (standard sliding\n window).\n\n !!! note\n `stride` must be smaller than or equal to `kernel_size`.\n When `stride == kernel_size`, there will be no overlap between sliding windows,\n which is equivalent to blocked attention (a.k.a.\n [window self attention](https://arxiv.org/abs/2103.14030)).\n\n dilation (Tuple[int] | int): Dilation step size. Defaults to `1` (standard sliding window).\n\n !!! note\n The product of `dilation` and `kernel_size` must be smaller than or equal to\n `seqlen`.\n\n is_causal (Tuple[bool] | bool): Toggle causal masking. Defaults to `False`\n (bi-directional).\n\n scale (float): Attention scale. Defaults to `head_dim ** -0.5`.\n\n additional_keys: `None` or 4-D key tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim]`), corresponding to key tokens from some\n additional context. Used when performing neighborhood cross-attention, where `query`\n tokens attend to their neighborhood, as well as some fixed additional set of tokens.\n\n additional_values: `None` or 4-D value tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to value tokens from some\n additional context. Used when performing neighborhood cross-attention, where `query`\n tokens attend to their neighborhood, as well as some fixed additional set of tokens.\n\n !!! note\n `additional_keys` and `additional_values` must both either be `Tensor`s, or both\n `None`s, and must match in shape.\n\nOther Parameters:\n backend (str): Backend implementation to run with. Choices are: `None` (pick the best\n available one), `\"cutlass-fna\"`, `\"hopper-fna\"`, `\"blackwell-fna\"`, `\"flex-fna\"`.\n Refer to [backends](backends.md) for more information.\n\n q_tile_shape (Tuple[int]): 1-D Tile shape for the query token layout in the forward pass\n kernel. You can use [profiler](profiler.md) to find valid choices for your use case,\n and search for the best combination.\n\n kv_tile_shape (Tuple[int]): 1-D Tile shape for the key-value token layout in the forward\n pass kernel. You can use [profiler](profiler.md) to find valid choices for your use\n case, and search for the best combination.\n\n backward_q_tile_shape (Tuple[int]): 1-D Tile shape for the query token layout in the\n backward pass kernel. This is ignored by `\"flex-fna\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case, and\n search for the best combination.\n\n backward_kv_tile_shape (Tuple[int]): 1-D Tile shape for the key/value token layout in the\n backward pass kernel. This is ignored by `\"flex-fna\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case, and\n search for the best combination.\n\n backward_kv_splits (Tuple[int]): Number of key/value tiles allowed to work in parallel in\n the backward pass kernel. Like tile shapes, this is a tuple and not an integer for\n neighborhood attention operations, and the size of the tuple corresponds to the number\n of dimensions / rank of the layout of tokens. This is only respected by the\n `\"cutlass-fna\"` backend, and only when\n [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.\n\n backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`\n product required by the backward pass, over the CUTLASS kernel. This only applies to\n the `\"cutlass-fna\"` backend.\n\n run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass\n kernel. This only applies to the `\"blackwell-fna\"` backend.\n\n kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are\n `None`: pick the default, `\"non\"` (non-persistent), `\"coop\"` (warp-specialized\n cooperative), or `\"pp\"` (warp-specialized ping-ponging). Refer to\n [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.\n\n torch_compile (bool): Applies only to the `\"flex-fna\"` backend. Whether or not to JIT\n compile the attention kernel. Due to this being an experimental feature in PyTorch, we\n do not recommend it, and it is guarded by context flags. Read more in\n [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).\n\n attention_kwargs: arguments to the [attention][natten.attention] operator, if used to\n implement neighborhood cross-attention, or self attention as a fast path for\n neighborhood attention.\n\n If `additional_{keys,values}` are specified, NATTEN usually performs a separate\n cross-attention using our [attention][natten.attention] operator, and\n [merges][natten.merge_attentions] the results.\n\n If for a given use case, the neighborhood attention problem is equivalent to self\n attention (not causal, `kernel_size == seqlen`), NATTEN will also attempt to directly\n use [attention][natten.attention].\n\n You can override arguments to [attention][natten.attention] by passing a\n dictionary here.\n\n !!! example\n ```python\n out = na1d(\n q, k, v, kernel_size=kernel_size,\n ...,\n attention_kwargs={\n \"backend\": \"blackwell-fmha\",\n \"run_persistent_kernel\": True,\n }\n )\n ```\n\n return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used\n in the backward pass, and for [attention merging][natten.merge_attentions].\n\nReturns:\n output (Tensor): 4-D output tensor, with the heads last layout\n (`[batch, seqlen, heads, head_dim_v]`).\n\n logsumexp (Tensor): only returned when `return_lse=True`. 3-D logsumexp tensor, with the\n heads last layout (`[batch, seqlen, heads]`).",
649
+ "signature": {
650
+ "parameters": [
651
+ {
652
+ "name": "query",
653
+ "kind": "POSITIONAL_OR_KEYWORD",
654
+ "default": null,
655
+ "annotation": "torch.Tensor"
656
+ },
657
+ {
658
+ "name": "key",
659
+ "kind": "POSITIONAL_OR_KEYWORD",
660
+ "default": null,
661
+ "annotation": "torch.Tensor"
662
+ },
663
+ {
664
+ "name": "value",
665
+ "kind": "POSITIONAL_OR_KEYWORD",
666
+ "default": null,
667
+ "annotation": "torch.Tensor"
668
+ },
669
+ {
670
+ "name": "kernel_size",
671
+ "kind": "POSITIONAL_OR_KEYWORD",
672
+ "default": null,
673
+ "annotation": "int | Tuple[int]"
674
+ },
675
+ {
676
+ "name": "stride",
677
+ "kind": "POSITIONAL_OR_KEYWORD",
678
+ "default": "1",
679
+ "annotation": "int | Tuple[int]"
680
+ },
681
+ {
682
+ "name": "dilation",
683
+ "kind": "POSITIONAL_OR_KEYWORD",
684
+ "default": "1",
685
+ "annotation": "int | Tuple[int]"
686
+ },
687
+ {
688
+ "name": "is_causal",
689
+ "kind": "POSITIONAL_OR_KEYWORD",
690
+ "default": "False",
691
+ "annotation": "bool | Tuple[bool] | None"
692
+ },
693
+ {
694
+ "name": "scale",
695
+ "kind": "POSITIONAL_OR_KEYWORD",
696
+ "default": "None",
697
+ "annotation": "float | None"
698
+ },
699
+ {
700
+ "name": "additional_keys",
701
+ "kind": "POSITIONAL_OR_KEYWORD",
702
+ "default": "None",
703
+ "annotation": "torch.Tensor | None"
704
+ },
705
+ {
706
+ "name": "additional_values",
707
+ "kind": "POSITIONAL_OR_KEYWORD",
708
+ "default": "None",
709
+ "annotation": "torch.Tensor | None"
710
+ },
711
+ {
712
+ "name": "attention_kwargs",
713
+ "kind": "POSITIONAL_OR_KEYWORD",
714
+ "default": "None",
715
+ "annotation": "Dict | None"
716
+ },
717
+ {
718
+ "name": "backend",
719
+ "kind": "POSITIONAL_OR_KEYWORD",
720
+ "default": "None",
721
+ "annotation": "str | None"
722
+ },
723
+ {
724
+ "name": "q_tile_shape",
725
+ "kind": "POSITIONAL_OR_KEYWORD",
726
+ "default": "None",
727
+ "annotation": "Tuple[int] | None"
728
+ },
729
+ {
730
+ "name": "kv_tile_shape",
731
+ "kind": "POSITIONAL_OR_KEYWORD",
732
+ "default": "None",
733
+ "annotation": "Tuple[int] | None"
734
+ },
735
+ {
736
+ "name": "backward_q_tile_shape",
737
+ "kind": "POSITIONAL_OR_KEYWORD",
738
+ "default": "None",
739
+ "annotation": "Tuple[int] | None"
740
+ },
741
+ {
742
+ "name": "backward_kv_tile_shape",
743
+ "kind": "POSITIONAL_OR_KEYWORD",
744
+ "default": "None",
745
+ "annotation": "Tuple[int] | None"
746
+ },
747
+ {
748
+ "name": "backward_kv_splits",
749
+ "kind": "POSITIONAL_OR_KEYWORD",
750
+ "default": "None",
751
+ "annotation": "Tuple[int] | None"
752
+ },
753
+ {
754
+ "name": "backward_use_pt_reduction",
755
+ "kind": "POSITIONAL_OR_KEYWORD",
756
+ "default": "False",
757
+ "annotation": "bool"
758
+ },
759
+ {
760
+ "name": "run_persistent_kernel",
761
+ "kind": "POSITIONAL_OR_KEYWORD",
762
+ "default": "True",
763
+ "annotation": "bool"
764
+ },
765
+ {
766
+ "name": "kernel_schedule",
767
+ "kind": "POSITIONAL_OR_KEYWORD",
768
+ "default": "None",
769
+ "annotation": "str | _natten_cuda_5fe9dbb._types.KernelSchedule | None"
770
+ },
771
+ {
772
+ "name": "torch_compile",
773
+ "kind": "POSITIONAL_OR_KEYWORD",
774
+ "default": "False",
775
+ "annotation": "bool"
776
+ },
777
+ {
778
+ "name": "return_lse",
779
+ "kind": "POSITIONAL_OR_KEYWORD",
780
+ "default": "False",
781
+ "annotation": "bool"
782
+ }
783
+ ],
784
+ "return_annotation": "torch.Tensor | Tuple[torch.Tensor, torch.Tensor]"
785
+ }
786
+ },
787
+ {
788
+ "name": "na2d",
789
+ "qualname": "na2d",
790
+ "module": "_natten_cuda_5fe9dbb.functional",
791
+ "kind": "function",
792
+ "doc": "Computes 2-D neighborhood attention.\n\nGQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fna` and\n`flex-fna` support GQA/MQA natively, and `cutlass-fna` and `hopper-fna` support it with\nexplicit repeats (increases memory usage and runtime).\n\nParameters:\n query (Tensor): 2-D query tensor, with the heads last layout:\n `[batch, X, Y, heads, head_dim]`, where token layout shape (feature map shape) is\n `(X, Y)`.\n\n key (Tensor): 2-D key tensor, with the heads last layout:\n `[batch, X, Y, heads_kv, head_dim]`, where token layout shape (feature map shape) is\n `(X, Y)`.\n\n value (Tensor): 2-D value tensor, with the heads last layout:\n `[batch, X, Y, heads_kv, head_dim_v]`, where token layout shape (feature map shape) is\n `(X, Y)`.\n\n kernel_size (Tuple[int, int] | int): Neighborhood window (kernel) size/shape. If an\n integer, it will be repeated for all 2 dimensions. For example `kernel_size=3` is\n reinterpreted as `kernel_size=(3, 3)`.\n\n !!! note\n `kernel_size` must be smaller than or equal to token layout shape (`(X, Y)`) along\n every dimension.\n\n stride (Tuple[int, int] | int): Sliding window step size/shape. Defaults to `1` (standard\n sliding window). If an integer, it will be repeated for all 2 dimensions. For example\n `stride=2` is reinterpreted as `stride=(2, 2)`.\n\n !!! note\n `stride` must be smaller than or equal to `kernel_size` along every dimension.\n When `stride == kernel_size`, there will be no overlap between sliding windows,\n which is equivalent to blocked attention (a.k.a.\n [window self attention](https://arxiv.org/abs/2103.14030)).\n\n dilation (Tuple[int, int] | int): Dilation step size/shape. Defaults to `1` (standard\n sliding window). If an integer, it will be repeated for all 2 dimensions. For example\n `dilation=4` is reinterpreted as `dilation=(4, 4)`.\n\n !!! note\n The product of `dilation` and `kernel_size` must be smaller than or equal to\n token layout shape (`(X, Y)`) along every dimension.\n\n is_causal (Tuple[bool, bool] | bool): Toggle causal masking. Defaults to `False`\n (bi-directional). If a boolean, it will be repeated for all 2 dimensions. For example\n `is_causal=True` is reinterpreted as `is_causal=(True, True)`.\n\n scale (float): Attention scale. Defaults to `head_dim ** -0.5`.\n\n additional_keys: `None` or 4-D key tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim]`), corresponding to key tokens from some\n additional context. Used when performing neighborhood cross-attention, where `query`\n tokens attend to their neighborhood, as well as some fixed additional set of tokens.\n\n additional_values: `None` or 4-D value tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to value tokens from some\n additional context. Used when performing neighborhood cross-attention, where `query`\n tokens attend to their neighborhood, as well as some fixed additional set of tokens.\n\n !!! note\n `additional_keys` and `additional_values` must both either be `Tensor`s, or both\n `None`s, and must match in shape.\n\nOther Parameters:\n backend (str): Backend implementation to run with. Choices are: `None` (pick the best\n available one), `\"cutlass-fna\"`, `\"hopper-fna\"`, `\"blackwell-fna\"`, `\"flex-fna\"`.\n Refer to [backends](backends.md) for more information.\n\n q_tile_shape (Tuple[int, int]): 2-D Tile shape for the query token layout in the forward\n pass kernel. You can use [profiler](profiler.md) to find valid choices for your use\n case, and search for the best combination.\n\n kv_tile_shape (Tuple[int, int]): 2-D Tile shape for the key-value token layout in the\n forward pass kernel. You can use [profiler](profiler.md) to find valid choices for your\n use case, and search for the best combination.\n\n backward_q_tile_shape (Tuple[int, int]): 2-D Tile shape for the query token layout in the\n backward pass kernel. This is ignored by `\"flex-fna\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case, and\n search for the best combination.\n\n backward_kv_tile_shape (Tuple[int, int]): 2-D Tile shape for the key/value token layout in\n the backward pass kernel. This is ignored by `\"flex-fna\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case, and\n search for the best combination.\n\n backward_kv_splits (Tuple[int, int]): Number of key/value tiles allowed to work in parallel\n in the backward pass kernel. Like tile shapes, this is a tuple and not an integer for\n neighborhood attention operations, and the size of the tuple corresponds to the number\n of dimensions / rank of the layout of tokens. This is only respected by the\n `\"cutlass-fna\"` backend, and only when\n [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.\n\n backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`\n product required by the backward pass, over the CUTLASS kernel. This only applies to\n the `\"cutlass-fna\"` backend.\n\n run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass\n kernel. This only applies to the `\"blackwell-fna\"` backend.\n\n kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are\n `None`: pick the default, `\"non\"` (non-persistent), `\"coop\"` (warp-specialized\n cooperative), or `\"pp\"` (warp-specialized ping-ponging). Refer to\n [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.\n\n torch_compile (bool): Applies only to the `\"flex-fna\"` backend. Whether or not to JIT\n compile the attention kernel. Due to this being an experimental feature in PyTorch, we\n do not recommend it, and it is guarded by context flags. Read more in\n [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).\n\n attention_kwargs: arguments to the [attention][natten.attention] operator, if used to\n implement neighborhood cross-attention, or self attention as a fast path for\n neighborhood attention.\n\n If `additional_{keys,values}` are specified, NATTEN usually performs a separate\n cross-attention using our [attention][natten.attention] operator, and\n [merges][natten.merge_attentions] the results.\n\n If for a given use case, the neighborhood attention problem is equivalent to self\n attention (not causal along any dims, `kernel_size == (X, Y)`), NATTEN will also\n attempt to directly use [attention][natten.attention].\n\n You can override arguments to [attention][natten.attention] by passing a\n dictionary here.\n\n !!! example\n ```python\n out = na2d(\n q, k, v, kernel_size=kernel_size,\n ...,\n attention_kwargs={\n \"backend\": \"blackwell-fmha\",\n \"run_persistent_kernel\": True,\n }\n )\n ```\n\n return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used\n in the backward pass, and for [attention merging][natten.merge_attentions].\n\nReturns:\n output (Tensor): 5-D output tensor, with the heads last layout\n (`[batch, X, Y, heads, head_dim_v]`).\n\n logsumexp (Tensor): only returned when `return_lse=True`. 4-D logsumexp tensor, with the\n heads last layout (`[batch, X, Y, heads]`).",
793
+ "signature": {
794
+ "parameters": [
795
+ {
796
+ "name": "query",
797
+ "kind": "POSITIONAL_OR_KEYWORD",
798
+ "default": null,
799
+ "annotation": "torch.Tensor"
800
+ },
801
+ {
802
+ "name": "key",
803
+ "kind": "POSITIONAL_OR_KEYWORD",
804
+ "default": null,
805
+ "annotation": "torch.Tensor"
806
+ },
807
+ {
808
+ "name": "value",
809
+ "kind": "POSITIONAL_OR_KEYWORD",
810
+ "default": null,
811
+ "annotation": "torch.Tensor"
812
+ },
813
+ {
814
+ "name": "kernel_size",
815
+ "kind": "POSITIONAL_OR_KEYWORD",
816
+ "default": null,
817
+ "annotation": "int | Tuple[int, int]"
818
+ },
819
+ {
820
+ "name": "stride",
821
+ "kind": "POSITIONAL_OR_KEYWORD",
822
+ "default": "1",
823
+ "annotation": "int | Tuple[int, int]"
824
+ },
825
+ {
826
+ "name": "dilation",
827
+ "kind": "POSITIONAL_OR_KEYWORD",
828
+ "default": "1",
829
+ "annotation": "int | Tuple[int, int]"
830
+ },
831
+ {
832
+ "name": "is_causal",
833
+ "kind": "POSITIONAL_OR_KEYWORD",
834
+ "default": "False",
835
+ "annotation": "bool | Tuple[bool, bool] | None"
836
+ },
837
+ {
838
+ "name": "scale",
839
+ "kind": "POSITIONAL_OR_KEYWORD",
840
+ "default": "None",
841
+ "annotation": "float | None"
842
+ },
843
+ {
844
+ "name": "additional_keys",
845
+ "kind": "POSITIONAL_OR_KEYWORD",
846
+ "default": "None",
847
+ "annotation": "torch.Tensor | None"
848
+ },
849
+ {
850
+ "name": "additional_values",
851
+ "kind": "POSITIONAL_OR_KEYWORD",
852
+ "default": "None",
853
+ "annotation": "torch.Tensor | None"
854
+ },
855
+ {
856
+ "name": "attention_kwargs",
857
+ "kind": "POSITIONAL_OR_KEYWORD",
858
+ "default": "None",
859
+ "annotation": "Dict | None"
860
+ },
861
+ {
862
+ "name": "backend",
863
+ "kind": "POSITIONAL_OR_KEYWORD",
864
+ "default": "None",
865
+ "annotation": "str | None"
866
+ },
867
+ {
868
+ "name": "q_tile_shape",
869
+ "kind": "POSITIONAL_OR_KEYWORD",
870
+ "default": "None",
871
+ "annotation": "Tuple[int, int] | None"
872
+ },
873
+ {
874
+ "name": "kv_tile_shape",
875
+ "kind": "POSITIONAL_OR_KEYWORD",
876
+ "default": "None",
877
+ "annotation": "Tuple[int, int] | None"
878
+ },
879
+ {
880
+ "name": "backward_q_tile_shape",
881
+ "kind": "POSITIONAL_OR_KEYWORD",
882
+ "default": "None",
883
+ "annotation": "Tuple[int, int] | None"
884
+ },
885
+ {
886
+ "name": "backward_kv_tile_shape",
887
+ "kind": "POSITIONAL_OR_KEYWORD",
888
+ "default": "None",
889
+ "annotation": "Tuple[int, int] | None"
890
+ },
891
+ {
892
+ "name": "backward_kv_splits",
893
+ "kind": "POSITIONAL_OR_KEYWORD",
894
+ "default": "None",
895
+ "annotation": "Tuple[int, int] | None"
896
+ },
897
+ {
898
+ "name": "backward_use_pt_reduction",
899
+ "kind": "POSITIONAL_OR_KEYWORD",
900
+ "default": "False",
901
+ "annotation": "bool"
902
+ },
903
+ {
904
+ "name": "run_persistent_kernel",
905
+ "kind": "POSITIONAL_OR_KEYWORD",
906
+ "default": "True",
907
+ "annotation": "bool"
908
+ },
909
+ {
910
+ "name": "kernel_schedule",
911
+ "kind": "POSITIONAL_OR_KEYWORD",
912
+ "default": "None",
913
+ "annotation": "str | _natten_cuda_5fe9dbb._types.KernelSchedule | None"
914
+ },
915
+ {
916
+ "name": "torch_compile",
917
+ "kind": "POSITIONAL_OR_KEYWORD",
918
+ "default": "False",
919
+ "annotation": "bool"
920
+ },
921
+ {
922
+ "name": "return_lse",
923
+ "kind": "POSITIONAL_OR_KEYWORD",
924
+ "default": "False",
925
+ "annotation": "bool"
926
+ }
927
+ ],
928
+ "return_annotation": "torch.Tensor | Tuple[torch.Tensor, torch.Tensor]"
929
+ }
930
+ },
931
+ {
932
+ "name": "na3d",
933
+ "qualname": "na3d",
934
+ "module": "_natten_cuda_5fe9dbb.functional",
935
+ "kind": "function",
936
+ "doc": "Computes 3-D neighborhood attention.\n\nGQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fna` and\n`flex-fna` support GQA/MQA natively, and `cutlass-fna` and `hopper-fna` support it with\nexplicit repeats (increases memory usage and runtime).\n\nParameters:\n query (Tensor): 3-D query tensor, with the heads last layout:\n `[batch, X, Y, Z, heads, head_dim]`, where token layout shape (feature map shape) is\n `(X, Y, Z)`.\n\n key (Tensor): 3-D key tensor, with the heads last layout:\n `[batch, X, Y, Z, heads_kv, head_dim]`, where token layout shape (feature map shape) is\n `(X, Y, Z)`.\n\n value (Tensor): 3-D value tensor, with the heads last layout:\n `[batch, X, Y, Z, heads_kv, head_dim_V]`, where token layout shape (feature map shape) is\n `(X, Y, Z)`.\n\n kernel_size (Tuple[int, int, int] | int): Neighborhood window (kernel) size/shape. If an\n integer, it will be repeated for all 3 dimensions. For example `kernel_size=3` is\n reinterpreted as `kernel_size=(3, 3, 3)`.\n\n !!! note\n `kernel_size` must be smaller than or equal to token layout shape (`(X, Y, Z)`)\n along every dimension.\n\n stride (Tuple[int, int, int] | int): Sliding window step size/shape. Defaults to `1`\n (standard sliding window). If an integer, it will be repeated for all 3 dimensions.\n For example `stride=2` is reinterpreted as `stride=(2, 2, 2)`.\n\n !!! note\n `stride` must be smaller than or equal to `kernel_size` along every dimension.\n When `stride == kernel_size`, there will be no overlap between sliding windows,\n which is equivalent to blocked attention (a.k.a.\n [window self attention](https://arxiv.org/abs/2103.14030)).\n\n dilation (Tuple[int, int, int] | int): Dilation step size/shape. Defaults to `1` (standard\n sliding window). If an integer, it will be repeated for all 3 dimensions. For example\n `dilation=4` is reinterpreted as `dilation=(4, 4, 4)`.\n\n !!! note\n The product of `dilation` and `kernel_size` must be smaller than or equal to\n token layout shape (`(X, Y, Z)`) along every dimension.\n\n is_causal (Tuple[bool, bool, bool] | bool): Toggle causal masking. Defaults to `False`\n (bi-directional). If a boolean, it will be repeated for all 3 dimensions. For example\n `is_causal=True` is reinterpreted as `is_causal=(True, True, True)`.\n\n scale (float): Attention scale. Defaults to `head_dim ** -0.5`.\n\n additional_keys: `None` or 4-D key tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to key tokens from some\n additional context. Used when performing neighborhood cross-attention, where `query`\n tokens attend to their neighborhood, as well as some fixed additional set of tokens.\n\n additional_values: `None` or 4-D value tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim_v]`), corresponding to value tokens from some\n additional context. Used when performing neighborhood cross-attention, where `query`\n tokens attend to their neighborhood, as well as some fixed additional set of tokens.\n\n !!! note\n `additional_keys` and `additional_values` must both either be `Tensor`s, or both\n `None`s, and must match in shape.\n\nOther Parameters:\n backend (str): Backend implementation to run with. Choices are: `None` (pick the best\n available one), `\"cutlass-fna\"`, `\"hopper-fna\"`, `\"blackwell-fna\"`, `\"flex-fna\"`.\n Refer to [backends](backends.md) for more information.\n\n q_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the query token layout in the\n forward pass kernel. You can use [profiler](profiler.md) to find valid choices for your\n use case, and search for the best combination.\n\n kv_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the key-value token layout in the\n forward pass kernel. You can use [profiler](profiler.md) to find valid choices for your\n use case, and search for the best combination.\n\n backward_q_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the query token layout in\n the backward pass kernel. This is ignored by `\"flex-fna\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case, and\n search for the best combination.\n\n backward_kv_tile_shape (Tuple[int, int, int]): 3-D Tile shape for the key/value token\n layout in the backward pass kernel. This is ignored by `\"flex-fna\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case,\n and search for the best combination.\n\n backward_kv_splits (Tuple[int, int, int]): Number of key/value tiles allowed to work in\n parallel in the backward pass kernel. Like tile shapes, this is a tuple and not an\n integer for neighborhood attention operations, and the size of the tuple corresponds to\n the number of dimensions / rank of the layout of tokens. This is only respected by the\n `\"cutlass-fna\"` backend, and only when\n [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.\n\n backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`\n product required by the backward pass, over the CUTLASS kernel. This only applies to\n the `\"cutlass-fna\"` backend.\n\n run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass\n kernel. This only applies to the `\"blackwell-fna\"` backend.\n\n kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are\n `None`: pick the default, `\"non\"` (non-persistent), `\"coop\"` (warp-specialized\n cooperative), or `\"pp\"` (warp-specialized ping-ponging). Refer to\n [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.\n\n torch_compile (bool): Applies only to the `\"flex-fna\"` backend. Whether or not to JIT\n compile the attention kernel. Due to this being an experimental feature in PyTorch, we\n do not recommend it, and it is guarded by context flags. Read more in\n [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).\n\n attention_kwargs: arguments to the [attention][natten.attention] operator, if used to\n implement neighborhood cross-attention, or self attention as a fast path for\n neighborhood attention.\n\n If `additional_{keys,values}` are specified, NATTEN usually performs a separate\n cross-attention using our [attention][natten.attention] operator, and\n [merges][natten.merge_attentions] the results.\n\n If for a given use case, the neighborhood attention problem is equivalent to self\n attention (not causal along any dims, `kernel_size == (X, Y, Z)`), NATTEN will also\n attempt to directly use [attention][natten.attention].\n\n You can override arguments to [attention][natten.attention] by passing a\n dictionary here.\n\n !!! example\n ```python\n out = na3d(\n q, k, v, kernel_size=kernel_size,\n ...,\n attention_kwargs={\n \"backend\": \"blackwell-fmha\",\n \"run_persistent_kernel\": True,\n }\n )\n ```\n\n return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used\n in the backward pass, and for [attention merging][natten.merge_attentions].\n\nReturns:\n output (Tensor): 6-D output tensor, with the heads last layout\n (`[batch, X, Y, Z, heads, head_dim_v]`).\n\n logsumexp (Tensor): only returned when `return_lse=True`. 5-D logsumexp tensor, with the\n heads last layout (`[batch, X, Y, Z, heads]`).",
937
+ "signature": {
938
+ "parameters": [
939
+ {
940
+ "name": "query",
941
+ "kind": "POSITIONAL_OR_KEYWORD",
942
+ "default": null,
943
+ "annotation": "torch.Tensor"
944
+ },
945
+ {
946
+ "name": "key",
947
+ "kind": "POSITIONAL_OR_KEYWORD",
948
+ "default": null,
949
+ "annotation": "torch.Tensor"
950
+ },
951
+ {
952
+ "name": "value",
953
+ "kind": "POSITIONAL_OR_KEYWORD",
954
+ "default": null,
955
+ "annotation": "torch.Tensor"
956
+ },
957
+ {
958
+ "name": "kernel_size",
959
+ "kind": "POSITIONAL_OR_KEYWORD",
960
+ "default": null,
961
+ "annotation": "int | Tuple[int, int, int]"
962
+ },
963
+ {
964
+ "name": "stride",
965
+ "kind": "POSITIONAL_OR_KEYWORD",
966
+ "default": "1",
967
+ "annotation": "int | Tuple[int, int, int]"
968
+ },
969
+ {
970
+ "name": "dilation",
971
+ "kind": "POSITIONAL_OR_KEYWORD",
972
+ "default": "1",
973
+ "annotation": "int | Tuple[int, int, int]"
974
+ },
975
+ {
976
+ "name": "is_causal",
977
+ "kind": "POSITIONAL_OR_KEYWORD",
978
+ "default": "False",
979
+ "annotation": "bool | Tuple[bool, bool, bool] | None"
980
+ },
981
+ {
982
+ "name": "scale",
983
+ "kind": "POSITIONAL_OR_KEYWORD",
984
+ "default": "None",
985
+ "annotation": "float | None"
986
+ },
987
+ {
988
+ "name": "additional_keys",
989
+ "kind": "POSITIONAL_OR_KEYWORD",
990
+ "default": "None",
991
+ "annotation": "torch.Tensor | None"
992
+ },
993
+ {
994
+ "name": "additional_values",
995
+ "kind": "POSITIONAL_OR_KEYWORD",
996
+ "default": "None",
997
+ "annotation": "torch.Tensor | None"
998
+ },
999
+ {
1000
+ "name": "attention_kwargs",
1001
+ "kind": "POSITIONAL_OR_KEYWORD",
1002
+ "default": "None",
1003
+ "annotation": "Dict | None"
1004
+ },
1005
+ {
1006
+ "name": "backend",
1007
+ "kind": "POSITIONAL_OR_KEYWORD",
1008
+ "default": "None",
1009
+ "annotation": "str | None"
1010
+ },
1011
+ {
1012
+ "name": "q_tile_shape",
1013
+ "kind": "POSITIONAL_OR_KEYWORD",
1014
+ "default": "None",
1015
+ "annotation": "Tuple[int, int, int] | None"
1016
+ },
1017
+ {
1018
+ "name": "kv_tile_shape",
1019
+ "kind": "POSITIONAL_OR_KEYWORD",
1020
+ "default": "None",
1021
+ "annotation": "Tuple[int, int, int] | None"
1022
+ },
1023
+ {
1024
+ "name": "backward_q_tile_shape",
1025
+ "kind": "POSITIONAL_OR_KEYWORD",
1026
+ "default": "None",
1027
+ "annotation": "Tuple[int, int, int] | None"
1028
+ },
1029
+ {
1030
+ "name": "backward_kv_tile_shape",
1031
+ "kind": "POSITIONAL_OR_KEYWORD",
1032
+ "default": "None",
1033
+ "annotation": "Tuple[int, int, int] | None"
1034
+ },
1035
+ {
1036
+ "name": "backward_kv_splits",
1037
+ "kind": "POSITIONAL_OR_KEYWORD",
1038
+ "default": "None",
1039
+ "annotation": "Tuple[int, int, int] | None"
1040
+ },
1041
+ {
1042
+ "name": "backward_use_pt_reduction",
1043
+ "kind": "POSITIONAL_OR_KEYWORD",
1044
+ "default": "False",
1045
+ "annotation": "bool"
1046
+ },
1047
+ {
1048
+ "name": "run_persistent_kernel",
1049
+ "kind": "POSITIONAL_OR_KEYWORD",
1050
+ "default": "True",
1051
+ "annotation": "bool"
1052
+ },
1053
+ {
1054
+ "name": "kernel_schedule",
1055
+ "kind": "POSITIONAL_OR_KEYWORD",
1056
+ "default": "None",
1057
+ "annotation": "str | _natten_cuda_5fe9dbb._types.KernelSchedule | None"
1058
+ },
1059
+ {
1060
+ "name": "torch_compile",
1061
+ "kind": "POSITIONAL_OR_KEYWORD",
1062
+ "default": "False",
1063
+ "annotation": "bool"
1064
+ },
1065
+ {
1066
+ "name": "return_lse",
1067
+ "kind": "POSITIONAL_OR_KEYWORD",
1068
+ "default": "False",
1069
+ "annotation": "bool"
1070
+ }
1071
+ ],
1072
+ "return_annotation": "torch.Tensor | Tuple[torch.Tensor, torch.Tensor]"
1073
+ }
1074
+ },
1075
+ {
1076
+ "name": "attention",
1077
+ "qualname": "attention",
1078
+ "module": "_natten_cuda_5fe9dbb.functional",
1079
+ "kind": "function",
1080
+ "doc": "Runs standard dot product attention.\n\nThis operation is used to implement neighborhood cross attention, in which we allow every\ntoken to interact with some additional context (`additional_keys` and `additional_values`\ntensors in [na1d][natten.na1d], [na2d][natten.na2d], and [na3d][natten.na3d]).\nThis operator is also used as a fast path for cases where neighborhood attention is equivalent\nto self attention (not causal along any dims, and `kernel_size` is equal to the number of input\ntokens).\n\nThis operation does not call into PyTorch's SDPA, and only runs one of the NATTEN backends\n(`cutlass-fmha`, `hopper-fmha`, `blackwell-fmha`, `flex-fmha`). Reasons for that include being\nable to control performance-related arguments, return logsumexp, and more.\nFor more information refer to [backends](backends.md).\n\nCausal mask, and Variable length (varlen) Attention are also supported in some backends\n(`cutlass-fmha` and `blackwell-fmha`).\n\nVarlen Attention is only supported for the sequence-packed layout: QKV tensors have batch size\n1, and tokens from different batches are concatenated without any padding along the sequence\ndimension. Sequence lengths for different batches can be provided in two ways:\n 1. `seqlens_Q` and `seqlens_KV` (less efficient): only provide the sequence lengths as\n integer tensors (must be on the same device as QKV), and NATTEN will compute cumulative\n and maximum sequence lengths on each call.\n This is **incompatible** with full-graph `torch.compile` since it requires a\n synchronization.\n 2. `cumulative_seqlen_{Q,KV}` and `max_seqlen_{Q,KV}` (more efficient):\n compute cumulative and maximum sequence lengths. `cumulative_seqlen_{Q,KV}` are integer\n tensors on the same device as QKV containing the cumulative sum of `seqlens_{Q,KV}`,\n with an additional `0` element in the beginning, therefore sized `batch+1`.\n `max_seqlen_{Q,KV}` are integers (not Tensors) that represent the maximum sequence\n lengths for Q and KV among all sequence batches.\n You can use `natten.utils.varlen.generate_varlen_parameters` to generate these\n parameters:\n ```python3\n from .utils.varlen import generate_varlen_parameters\n (\n cumulative_seqlen_Q,\n cumulative_seqlen_KV,\n max_seqlen_Q,\n max_seqlen_KV,\n ) = generate_varlen_parameters(q, k, v, seqlens_Q, seqlens_KV)\n ```\n As long as `generate_varlen_parameters` is called ahead of torch.compiling the model, it\n is supported without any graph breaks.\n\nGQA/MQA support (`heads != heads_kv`) is available. For now, `blackwell-fmha` and\n`flex-fmha` support GQA/MQA natively, and `cutlass-fmha` and `hopper-fmha` support it with\nexplicit repeats (increases memory usage and runtime).\n\nParameters:\n query (Tensor): 4-D query tensor, with the heads last layout\n (`[batch, seqlen, heads, head_dim]`)\n\n key (Tensor): 4-D key tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim]`)\n\n value (Tensor): 4-D value tensor, with the heads last layout\n (`[batch, seqlen_kv, heads_kv, head_dim_v]`)\n\n is_causal (bool): Toggle causal masking. Defaults to `False` (bi-directional).\n\n scale (float): Attention scale. Defaults to `head_dim ** -0.5`.\n\n seqlens_Q (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch`\n indicating the number of query tokens in each batch. Must be passed together with\n `seqlens_KV`.\n\n seqlens_KV (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch`\n indicating the number of key/value tokens in each batch. Must be passed together with\n `seqlens_Q`.\n\n cumulative_seqlen_Q (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch + 1`\n indicating the cumulative sum of number of query tokens in each batch, with an\n additional 0 element in the beginning. Must be passed together with\n `cumulative_seqlen_KV` and `max_seqlen_{Q,KV}`.\n\n cumulative_seqlen_KV (Optional[Tensor]): (varlen) Optional 1-D tensor with size `batch + 1`\n indicating the cumulative sum of number of key/value tokens in each batch, with an\n additional 0 element in the beginning. Must be passed together with\n `cumulative_seqlen_Q` and `max_seqlen_{Q,KV}`.\n\n max_seqlen_Q (Optional[int]): (varlen) Optional integer indicating the maximum query\n sequence length in all batches. Must be passed together with `cumulative_seqlen_{Q,KV}`\n and `max_seqlen_KV`.\n\n max_seqlen_KV (Optional[int]): (varlen) Optional integer indicating the maximum key/value\n sequence length in all batches. Must be passed together with `cumulative_seqlen_{Q,KV}`\n and `max_seqlen_Q`.\n\nOther Parameters:\n backend (str): Backend implementation to run with. Choices are: `None` (pick the best\n available one), `\"cutlass-fmha\"`, `\"hopper-fmha\"`, `\"blackwell-fmha\"`, `\"flex-fmha\"`.\n Refer to [backends](backends.md) for more information.\n\n q_tile_size (int): Tile size along query sequence length in the forward pass kernel.\n You can use [profiler](profiler.md) to find valid choices for your use case.\n\n kv_tile_size (int): Tile size along key/value sequence length in the forward pass kernel.\n You can use [profiler](profiler.md) to find valid choices for your use case.\n\n backward_q_tile_size (int): Tile size along query sequence length in the backward pass\n kernel. This is ignored by `\"flex-fmha\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case.\n\n backward_kv_tile_size (int): Tile size along key/value sequence length in the backward pass\n kernel. This is ignored by `\"flex-fmha\"`.\n You can use [profiler](profiler.md) to find valid choices for your use case.\n\n backward_kv_splits (int): Number of key/value tiles allowed to work in parallel in the\n backward pass kernel. This is only respected by the `\"cutlass-fmha\"` backend, only when\n [KV parallelism](context.md#kv-parallelism-in-fna) is enabled.\n\n backward_use_pt_reduction (bool): Whether to use PyTorch eager for computing the `dO * O`\n product required by the backward pass, over the CUTLASS kernel. This only applies to\n the `\"cutlass-fmha\"` backend.\n\n run_persistent_kernel (bool): Whether to use persistent tile scheduling in the forward pass\n kernel. This only applies to the `\"blackwell-fmha\"` backend.\n\n kernel_schedule (Optional[str]): Kernel type (Hopper architecture only). Choices are\n `None`: pick the default, `\"non\"` (non-persistent), `\"coop\"` (warp-specialized\n cooperative), or `\"pp\"` (warp-specialized ping-ponging). Refer to\n [Hopper FMHA/FNA backend](backends.md#hopper-fna-fmha) for more information.\n\n torch_compile (bool): Applies only to the `\"flex-fmha\"` backend. Whether or not to JIT\n compile the attention kernel. Due to this being an experimental feature in PyTorch, we\n do not recommend it, and it is guarded by context flags. Read more in\n [Flex Attention + `torch.compile`](context.md#flex-attention-torchcompile).\n\n return_lse (bool): Whether or not to return the `logsumexp` tensor. `logsumexp` can be used\n in the backward pass, and for [attention merging][natten.merge_attentions].\n\nReturns:\n output (Tensor): 4-D output tensor, with the heads last layout\n (`[batch, seqlen, heads, head_dim_v]`).\n\n logsumexp (Tensor): only returned when `return_lse=True`. 3-D logsumexp tensor, with the\n heads last layout (`[batch, seqlen, heads]`).",
1081
+ "signature": {
1082
+ "parameters": [
1083
+ {
1084
+ "name": "query",
1085
+ "kind": "POSITIONAL_OR_KEYWORD",
1086
+ "default": null,
1087
+ "annotation": "torch.Tensor"
1088
+ },
1089
+ {
1090
+ "name": "key",
1091
+ "kind": "POSITIONAL_OR_KEYWORD",
1092
+ "default": null,
1093
+ "annotation": "torch.Tensor"
1094
+ },
1095
+ {
1096
+ "name": "value",
1097
+ "kind": "POSITIONAL_OR_KEYWORD",
1098
+ "default": null,
1099
+ "annotation": "torch.Tensor"
1100
+ },
1101
+ {
1102
+ "name": "is_causal",
1103
+ "kind": "POSITIONAL_OR_KEYWORD",
1104
+ "default": "False",
1105
+ "annotation": "bool"
1106
+ },
1107
+ {
1108
+ "name": "scale",
1109
+ "kind": "POSITIONAL_OR_KEYWORD",
1110
+ "default": "None",
1111
+ "annotation": "float | None"
1112
+ },
1113
+ {
1114
+ "name": "seqlens_Q",
1115
+ "kind": "POSITIONAL_OR_KEYWORD",
1116
+ "default": "None",
1117
+ "annotation": "torch.Tensor | None"
1118
+ },
1119
+ {
1120
+ "name": "seqlens_KV",
1121
+ "kind": "POSITIONAL_OR_KEYWORD",
1122
+ "default": "None",
1123
+ "annotation": "torch.Tensor | None"
1124
+ },
1125
+ {
1126
+ "name": "cumulative_seqlen_Q",
1127
+ "kind": "POSITIONAL_OR_KEYWORD",
1128
+ "default": "None",
1129
+ "annotation": "torch.Tensor | None"
1130
+ },
1131
+ {
1132
+ "name": "cumulative_seqlen_KV",
1133
+ "kind": "POSITIONAL_OR_KEYWORD",
1134
+ "default": "None",
1135
+ "annotation": "torch.Tensor | None"
1136
+ },
1137
+ {
1138
+ "name": "max_seqlen_Q",
1139
+ "kind": "POSITIONAL_OR_KEYWORD",
1140
+ "default": "None",
1141
+ "annotation": "int | None"
1142
+ },
1143
+ {
1144
+ "name": "max_seqlen_KV",
1145
+ "kind": "POSITIONAL_OR_KEYWORD",
1146
+ "default": "None",
1147
+ "annotation": "int | None"
1148
+ },
1149
+ {
1150
+ "name": "backend",
1151
+ "kind": "POSITIONAL_OR_KEYWORD",
1152
+ "default": "None",
1153
+ "annotation": "str | None"
1154
+ },
1155
+ {
1156
+ "name": "q_tile_size",
1157
+ "kind": "POSITIONAL_OR_KEYWORD",
1158
+ "default": "None",
1159
+ "annotation": "int | None"
1160
+ },
1161
+ {
1162
+ "name": "kv_tile_size",
1163
+ "kind": "POSITIONAL_OR_KEYWORD",
1164
+ "default": "None",
1165
+ "annotation": "int | None"
1166
+ },
1167
+ {
1168
+ "name": "backward_q_tile_size",
1169
+ "kind": "POSITIONAL_OR_KEYWORD",
1170
+ "default": "None",
1171
+ "annotation": "int | None"
1172
+ },
1173
+ {
1174
+ "name": "backward_kv_tile_size",
1175
+ "kind": "POSITIONAL_OR_KEYWORD",
1176
+ "default": "None",
1177
+ "annotation": "int | None"
1178
+ },
1179
+ {
1180
+ "name": "backward_kv_splits",
1181
+ "kind": "POSITIONAL_OR_KEYWORD",
1182
+ "default": "None",
1183
+ "annotation": "int | None"
1184
+ },
1185
+ {
1186
+ "name": "backward_use_pt_reduction",
1187
+ "kind": "POSITIONAL_OR_KEYWORD",
1188
+ "default": "False",
1189
+ "annotation": "bool"
1190
+ },
1191
+ {
1192
+ "name": "run_persistent_kernel",
1193
+ "kind": "POSITIONAL_OR_KEYWORD",
1194
+ "default": "True",
1195
+ "annotation": "bool"
1196
+ },
1197
+ {
1198
+ "name": "kernel_schedule",
1199
+ "kind": "POSITIONAL_OR_KEYWORD",
1200
+ "default": "None",
1201
+ "annotation": "str | _natten_cuda_5fe9dbb._types.KernelSchedule | None"
1202
+ },
1203
+ {
1204
+ "name": "torch_compile",
1205
+ "kind": "POSITIONAL_OR_KEYWORD",
1206
+ "default": "False",
1207
+ "annotation": "bool"
1208
+ },
1209
+ {
1210
+ "name": "return_lse",
1211
+ "kind": "POSITIONAL_OR_KEYWORD",
1212
+ "default": "False",
1213
+ "annotation": "bool"
1214
+ }
1215
+ ],
1216
+ "return_annotation": "torch.Tensor | Tuple[torch.Tensor, torch.Tensor]"
1217
+ }
1218
+ },
1219
+ {
1220
+ "name": "merge_attentions",
1221
+ "qualname": "merge_attentions",
1222
+ "module": "_natten_cuda_5fe9dbb.attn_merge",
1223
+ "kind": "function",
1224
+ "doc": "Takes multiple attention *outputs* originating from the same query tensor, and their\ncorresponding logsumexps, and merges them as if their context (key/value pair) had been\nconcatenated.\n\nThis operation is used to implement cross-neighborhood attention, and can also be used for\ndistributed setups, such as context-parallelism.\n\nThis operation also attempts to use `torch.compile` to fuse the elementwise operations. This\ncan be disabled by passing `torch_compile=False`.\n\nParameters:\n outputs (List[Tensor]): List of 4-D attention output tensors, with the heads last layout\n (`[batch, seqlen, heads, head_dim]`)\n\n lse_tensors (List[Tensor]): List of 3-D logsumexp tensors, with the heads last layout\n (`[batch, seqlen, heads]`)\n\n torch_compile (bool): Attempt to use `torch.compile` to fuse the underlying elementwise\n operations. Default: True.\n\n use_autograd_fix (bool): fix backpropagation by using a custom autograd function. Only\n compatible with fused attention operations (Flash/FMHA/FNA), only as long as the inputs\n of this function are (views) of outputs from said attention operation.\n NATTEN's tests (tests/test_attn_merge.py) only verify correctness for when using\n attention operations from NATTEN. Integration for non-NATTEN ops must be verified by the\n end user.\n This must be disabled when using unfused Attention, which includes Flex without\n torch.compile. Default: True.\n\nReturns:\n output (Tensor): merged attention output.\n\n logsumexp (Tensor): updated logsumexp.",
1225
+ "signature": {
1226
+ "parameters": [
1227
+ {
1228
+ "name": "outputs",
1229
+ "kind": "POSITIONAL_OR_KEYWORD",
1230
+ "default": null,
1231
+ "annotation": "List[torch.Tensor]"
1232
+ },
1233
+ {
1234
+ "name": "lse_tensors",
1235
+ "kind": "POSITIONAL_OR_KEYWORD",
1236
+ "default": null,
1237
+ "annotation": "List[torch.Tensor]"
1238
+ },
1239
+ {
1240
+ "name": "torch_compile",
1241
+ "kind": "POSITIONAL_OR_KEYWORD",
1242
+ "default": "True",
1243
+ "annotation": "bool"
1244
+ },
1245
+ {
1246
+ "name": "use_autograd_fix",
1247
+ "kind": "POSITIONAL_OR_KEYWORD",
1248
+ "default": "True",
1249
+ "annotation": "bool"
1250
+ }
1251
+ ],
1252
+ "return_annotation": "Tuple[torch.Tensor, torch.Tensor]"
1253
+ }
1254
+ }
1255
+ ],
1256
+ "layers": []
1257
+ }
build/torch210-cu126-aarch64-linux/token_permute/__init__.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from ..token_permute.frontend import (
25
+ token_permute_operation,
26
+ token_unpermute_operation,
27
+ )
28
+
29
+ __all__ = [
30
+ "token_permute_operation",
31
+ "token_unpermute_operation",
32
+ ]
build/torch210-cu126-aarch64-linux/token_permute/cutlass_impl.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import functools
25
+ from typing import Tuple
26
+
27
+ import torch
28
+ from torch import Tensor
29
+ from torch.amp import custom_bwd, custom_fwd
30
+ from torch.autograd import Function
31
+
32
+ amp_fwd = functools.partial(custom_fwd, device_type="cuda")
33
+ amp_bwd = functools.partial(custom_bwd, device_type="cuda")
34
+
35
+ from .._libnatten import (
36
+ HAS_LIBNATTEN,
37
+ token_permute_1d,
38
+ token_permute_2d,
39
+ token_permute_3d,
40
+ token_unpermute_1d,
41
+ token_unpermute_2d,
42
+ token_unpermute_3d,
43
+ )
44
+ from .._types import DimensionType, NoneType
45
+ from ..utils import log
46
+ from ..utils.device import get_device_cc, is_cuda
47
+
48
+ logger = log.get_logger(__name__)
49
+
50
+
51
+ def can_run_cutlass_tokperm(tensor: Tensor) -> bool:
52
+ if not HAS_LIBNATTEN:
53
+ logger.debug(
54
+ "Can't use libnatten TokPerm kernels, because libnatten is not available."
55
+ )
56
+ return False
57
+
58
+ if not is_cuda(tensor.device):
59
+ logger.debug(
60
+ "Can't use libnatten TokPerm kernels, because input is not a CUDA tensor."
61
+ )
62
+ return False
63
+
64
+ is_fp8_allowed = get_device_cc(tensor.device) in [100, 103]
65
+ if tensor.dtype not in [
66
+ torch.float32,
67
+ torch.float16,
68
+ torch.bfloat16,
69
+ torch.float16,
70
+ ] and (
71
+ is_fp8_allowed and tensor.dtype not in [torch.float8_e5m2, torch.float8_e4m3fn]
72
+ ):
73
+ logger.debug(
74
+ f"Can't use libnatten TokPerm kernels; unexpected dtype {tensor.dtype}."
75
+ )
76
+ return False
77
+
78
+ return True
79
+
80
+
81
+ PERMUTE_OPS = {1: token_permute_1d, 2: token_permute_2d, 3: token_permute_3d}
82
+ UNPERMUTE_OPS = {1: token_unpermute_1d, 2: token_unpermute_2d, 3: token_unpermute_3d}
83
+
84
+
85
+ def make_cutlass_token_permute_autograd_fn(na_dim):
86
+ assert na_dim in [1, 2, 3]
87
+
88
+ class CutlassTokenPermuteAutogradFn(Function):
89
+ @staticmethod
90
+ @amp_fwd
91
+ def forward(
92
+ ctx,
93
+ tensor: Tensor,
94
+ tile_shape: DimensionType,
95
+ dilation: DimensionType,
96
+ flip_tiled_dims: bool,
97
+ ) -> Tensor:
98
+
99
+ output = PERMUTE_OPS[na_dim](
100
+ tensor,
101
+ tile_shape=tile_shape,
102
+ dilation=dilation,
103
+ flip_tiled_dims=flip_tiled_dims,
104
+ )
105
+
106
+ ctx.tile_shape = tile_shape
107
+ ctx.dilation = dilation
108
+ ctx.flip_tiled_dims = flip_tiled_dims
109
+ ctx.token_layout = tuple(x for x in tensor.shape[1 : na_dim + 1])
110
+ assert len(ctx.token_layout) == na_dim
111
+
112
+ return output
113
+
114
+ @staticmethod
115
+ @amp_bwd
116
+ def backward(ctx, d_output: Tensor) -> Tuple[
117
+ Tensor,
118
+ NoneType,
119
+ NoneType,
120
+ NoneType,
121
+ ]:
122
+
123
+ d_output_unpermuted = UNPERMUTE_OPS[na_dim](
124
+ d_output,
125
+ token_layout_shape=ctx.token_layout,
126
+ tile_shape=ctx.tile_shape,
127
+ dilation=ctx.dilation,
128
+ flip_tiled_dims=ctx.flip_tiled_dims,
129
+ )
130
+
131
+ return (
132
+ d_output_unpermuted,
133
+ None,
134
+ None,
135
+ None,
136
+ )
137
+
138
+ return CutlassTokenPermuteAutogradFn
139
+
140
+
141
+ def make_cutlass_token_unpermute_autograd_fn(na_dim):
142
+ assert na_dim in [1, 2, 3]
143
+
144
+ class CutlassTokenUnPermuteAutogradFn(Function):
145
+ @staticmethod
146
+ @amp_fwd
147
+ def forward(
148
+ ctx,
149
+ tensor: Tensor,
150
+ token_layout: DimensionType,
151
+ tile_shape: DimensionType,
152
+ dilation: DimensionType,
153
+ flip_tiled_dims: bool,
154
+ ) -> Tensor:
155
+
156
+ output = UNPERMUTE_OPS[na_dim](
157
+ tensor,
158
+ token_layout_shape=token_layout,
159
+ tile_shape=tile_shape,
160
+ dilation=dilation,
161
+ flip_tiled_dims=flip_tiled_dims,
162
+ )
163
+
164
+ ctx.tile_shape = tile_shape
165
+ ctx.dilation = dilation
166
+ ctx.flip_tiled_dims = flip_tiled_dims
167
+
168
+ return output
169
+
170
+ @staticmethod
171
+ @amp_bwd
172
+ def backward(ctx, d_output: Tensor) -> Tuple[
173
+ Tensor,
174
+ NoneType,
175
+ NoneType,
176
+ NoneType,
177
+ NoneType,
178
+ ]:
179
+
180
+ d_output_permuted = PERMUTE_OPS[na_dim](
181
+ d_output,
182
+ tile_shape=ctx.tile_shape,
183
+ dilation=ctx.dilation,
184
+ flip_tiled_dims=ctx.flip_tiled_dims,
185
+ )
186
+
187
+ return (
188
+ d_output_permuted,
189
+ None,
190
+ None,
191
+ None,
192
+ None,
193
+ )
194
+
195
+ return CutlassTokenUnPermuteAutogradFn
196
+
197
+
198
+ CutlassTokenPermute1DAutogradFn = make_cutlass_token_permute_autograd_fn(1)
199
+ CutlassTokenPermute2DAutogradFn = make_cutlass_token_permute_autograd_fn(2)
200
+ CutlassTokenPermute3DAutogradFn = make_cutlass_token_permute_autograd_fn(3)
201
+
202
+ CutlassTokenUnPermute1DAutogradFn = make_cutlass_token_unpermute_autograd_fn(1)
203
+ CutlassTokenUnPermute2DAutogradFn = make_cutlass_token_unpermute_autograd_fn(2)
204
+ CutlassTokenUnPermute3DAutogradFn = make_cutlass_token_unpermute_autograd_fn(3)
205
+
206
+ CutlassTokenPermuteAutogradFns = {
207
+ 1: CutlassTokenPermute1DAutogradFn,
208
+ 2: CutlassTokenPermute2DAutogradFn,
209
+ 3: CutlassTokenPermute3DAutogradFn,
210
+ }
211
+
212
+ CutlassTokenUnPermuteAutogradFns = {
213
+ 1: CutlassTokenUnPermute1DAutogradFn,
214
+ 2: CutlassTokenUnPermute2DAutogradFn,
215
+ 3: CutlassTokenUnPermute3DAutogradFn,
216
+ }
217
+
218
+
219
+ def token_permute_cutlass(
220
+ tensor: Tensor,
221
+ tile_shape: DimensionType,
222
+ dilation: DimensionType,
223
+ flip_tiled_dims,
224
+ ) -> Tensor:
225
+ if tensor.dim() not in [4, 5, 6]:
226
+ raise ValueError(
227
+ "Expected 4D, 5D, or 6D tensor (corresponding to NA1D, 2D, 3D), "
228
+ f"got {tensor.dim()}D input."
229
+ )
230
+
231
+ na_dim = tensor.dim() - 3
232
+ assert na_dim in [1, 2, 3]
233
+
234
+ if len(tile_shape) != na_dim:
235
+ raise ValueError(
236
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
237
+ )
238
+
239
+ if not can_run_cutlass_tokperm(tensor):
240
+ raise NotImplementedError(
241
+ "Use case is not compatible with CUTLASS Token Permute."
242
+ )
243
+
244
+ tensor = tensor.contiguous()
245
+ output = CutlassTokenPermuteAutogradFns[na_dim].apply(
246
+ tensor,
247
+ tile_shape,
248
+ dilation,
249
+ flip_tiled_dims,
250
+ )
251
+
252
+ return output
253
+
254
+
255
+ def token_unpermute_cutlass(
256
+ tensor: Tensor,
257
+ token_layout_shape: DimensionType,
258
+ tile_shape: DimensionType,
259
+ dilation: DimensionType,
260
+ flip_tiled_dims: bool,
261
+ ) -> Tensor:
262
+ if tensor.dim() != 4:
263
+ raise ValueError(f"Expected flattened 4D tensor, got {tensor.dim()}D input.")
264
+
265
+ na_dim = len(token_layout_shape)
266
+
267
+ if len(tile_shape) != na_dim:
268
+ raise ValueError(
269
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
270
+ )
271
+
272
+ if not can_run_cutlass_tokperm(tensor):
273
+ raise NotImplementedError(
274
+ "Use case is not compatible with CUTLASS Token UnPermute."
275
+ )
276
+
277
+ tensor = tensor.contiguous()
278
+ output = CutlassTokenUnPermuteAutogradFns[na_dim].apply(
279
+ tensor,
280
+ token_layout_shape,
281
+ tile_shape,
282
+ dilation,
283
+ flip_tiled_dims,
284
+ )
285
+
286
+ return output
build/torch210-cu126-aarch64-linux/token_permute/frontend.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import Optional
25
+
26
+ from torch import Tensor
27
+
28
+ from .._environment import USE_TORCH_IMPL_DEFAULT
29
+ from ..token_permute.cutlass_impl import (
30
+ can_run_cutlass_tokperm,
31
+ token_permute_cutlass,
32
+ token_unpermute_cutlass,
33
+ )
34
+ from ..token_permute.torch_impl import token_permute_torch, token_unpermute_torch
35
+ from .._types import DimensionType
36
+ from ..utils import log
37
+ from ..utils.tuples import ceil_div_tuple, mul_tuple
38
+
39
+ logger = log.get_logger(__name__)
40
+
41
+
42
+ def token_permute_operation(
43
+ tensor: Tensor,
44
+ tile_shape: DimensionType,
45
+ dilation: Optional[DimensionType] = None,
46
+ flip_tiled_dims: bool = True,
47
+ use_torch: bool = USE_TORCH_IMPL_DEFAULT,
48
+ ) -> tuple[Tensor, DimensionType, DimensionType]:
49
+ if tensor.dim() not in [4, 5, 6]:
50
+ raise ValueError(
51
+ "Expected 4D, 5D, or 6D tensor (corresponding to NA1D, 2D, 3D), "
52
+ f"got {tensor.dim()}D input."
53
+ )
54
+
55
+ na_dim = tensor.dim() - 3
56
+ assert na_dim in [1, 2, 3]
57
+
58
+ if len(tile_shape) != na_dim:
59
+ raise ValueError(
60
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
61
+ )
62
+
63
+ if dilation is not None and len(dilation) != na_dim:
64
+ raise ValueError(
65
+ f"Expected {na_dim}D dilation for NA{na_dim}D, " f"got {dilation=}."
66
+ )
67
+
68
+ dilation_: DimensionType = dilation or tuple(1 for _ in range(na_dim)) # type: ignore[assignment]
69
+
70
+ tensor = tensor.contiguous()
71
+ batch, *token_layout_, heads, dim = tensor.shape
72
+ token_layout: DimensionType = tuple(x for x in token_layout_) # type: ignore[assignment]
73
+
74
+ token_layout_post_dilation: DimensionType = mul_tuple(ceil_div_tuple(ceil_div_tuple(token_layout, tile_shape), dilation_), tile_shape) # type: ignore[assignment]
75
+
76
+ if not use_torch and can_run_cutlass_tokperm(tensor):
77
+ output = token_permute_cutlass(
78
+ tensor,
79
+ tile_shape=tile_shape,
80
+ dilation=dilation_,
81
+ flip_tiled_dims=flip_tiled_dims,
82
+ )
83
+ else:
84
+ output = token_permute_torch(
85
+ tensor,
86
+ tile_shape=tile_shape,
87
+ dilation=dilation_,
88
+ flip_tiled_dims=flip_tiled_dims,
89
+ )
90
+
91
+ return output, token_layout, token_layout_post_dilation
92
+
93
+
94
+ def token_unpermute_operation(
95
+ tensor: Tensor,
96
+ token_layout_shape: DimensionType,
97
+ tile_shape: DimensionType,
98
+ dilation: Optional[DimensionType] = None,
99
+ flip_tiled_dims: bool = True,
100
+ use_torch: bool = USE_TORCH_IMPL_DEFAULT,
101
+ ) -> Tensor:
102
+ if tensor.dim() != 4:
103
+ raise ValueError(f"Expected flattened 4D tensor, got {tensor.dim()}D input.")
104
+
105
+ na_dim = len(token_layout_shape)
106
+
107
+ if len(tile_shape) != na_dim:
108
+ raise ValueError(
109
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
110
+ )
111
+
112
+ if dilation is not None and len(dilation) != na_dim:
113
+ raise ValueError(
114
+ f"Expected {na_dim}D dilation for NA{na_dim}D, " f"got {dilation=}."
115
+ )
116
+
117
+ dilation_: DimensionType = dilation or tuple(1 for _ in range(na_dim)) # type: ignore[assignment]
118
+
119
+ tensor = tensor.contiguous()
120
+ if not use_torch and can_run_cutlass_tokperm(tensor):
121
+ output = token_unpermute_cutlass(
122
+ tensor,
123
+ token_layout_shape,
124
+ tile_shape=tile_shape,
125
+ dilation=dilation_,
126
+ flip_tiled_dims=flip_tiled_dims,
127
+ )
128
+ else:
129
+ output = token_unpermute_torch(
130
+ tensor,
131
+ token_layout_shape,
132
+ tile_shape=tile_shape,
133
+ dilation=dilation_,
134
+ flip_tiled_dims=flip_tiled_dims,
135
+ )
136
+
137
+ return output
build/torch210-cu126-aarch64-linux/token_permute/torch_impl.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+ import math
24
+
25
+ import torch
26
+ from torch import Tensor
27
+
28
+ from .._types import DimensionType
29
+ from ..utils import log
30
+ from ..utils.environment import is_torch_compiling
31
+ from ..utils.tuples import ceil_div_tuple, mul_tuple, sub_tuple
32
+
33
+ logger = log.get_logger(__name__)
34
+
35
+
36
+ DISABLE_PADDING_WARNING = True
37
+ TOKEN_PERMUTE_PADDING_RATIO_LIMIT_UNTIL_WARNING = 0.5
38
+
39
+
40
+ def _maybe_pad(
41
+ tensor: Tensor, tile_shape: DimensionType, dilation: DimensionType
42
+ ) -> Tensor:
43
+ if tensor.dim() not in [4, 5, 6]:
44
+ raise ValueError(
45
+ "Expected 4D, 5D, or 6D tensor (corresponding to NA1D, 2D, 3D), "
46
+ f"got {tensor.dim()}D input."
47
+ )
48
+
49
+ na_dim = tensor.dim() - 3
50
+ assert na_dim in [1, 2, 3]
51
+
52
+ if len(tile_shape) != na_dim:
53
+ raise ValueError(
54
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
55
+ )
56
+
57
+ if dilation is not None and len(dilation) != na_dim:
58
+ raise ValueError(
59
+ f"Expected {na_dim}D dilation for NA{na_dim}D, " f"got {dilation=}."
60
+ )
61
+
62
+ token_layout = tensor.shape[1 : na_dim + 1]
63
+ tile_shape_ = tuple(x for x in tile_shape)
64
+ if dilation is not None:
65
+ # NOTE: LCM?
66
+ # tile_shape_ = tuple(math.lcm(t, d) for t, d in zip(tile_shape, dilation))
67
+ tile_shape_ = tuple(t * d for t, d in zip(tile_shape, dilation))
68
+
69
+ rest = tuple((x + t - 1) // t for x, t in zip(token_layout, tile_shape_))
70
+ residual = tuple(r * t - x for x, t, r in zip(token_layout, tile_shape_, rest))
71
+
72
+ assert all(res >= 0 for res in residual)
73
+
74
+ if not DISABLE_PADDING_WARNING and any(
75
+ res / sz > TOKEN_PERMUTE_PADDING_RATIO_LIMIT_UNTIL_WARNING
76
+ for res, sz in zip(residual, token_layout)
77
+ ):
78
+ padded_token_layout = tuple(x + p for x, p in zip(token_layout, residual))
79
+ logger.warning(
80
+ "Potentially excessive padding detected in token permute: "
81
+ f"input shape {token_layout} will be padded to {padded_token_layout} to handle "
82
+ "token permutation, which can result in excessive memory usage, and "
83
+ "performance implications. Consider choosing your tile shapes, input shapes "
84
+ "(and dilation if you use it) accordingly. Refer to NATTEN docs for more info."
85
+ )
86
+
87
+ if any(res > 0 for res in residual):
88
+ padding = [0, 0, 0, 0] # head_dim_left, head_dim_right, heads_left, heads_right
89
+ for res in reversed(residual):
90
+ padding.append(0) # left pad
91
+ padding.append(res) # right pad
92
+ tensor_padded = torch.nn.functional.pad(tensor, padding, "constant", 0)
93
+ else:
94
+ tensor_padded = tensor
95
+
96
+ return tensor_padded
97
+
98
+
99
+ def _token_permute(
100
+ tensor: Tensor,
101
+ tile_shape: DimensionType,
102
+ dilation: DimensionType,
103
+ flip_tiled_dims: bool,
104
+ ) -> Tensor:
105
+ if tensor.dim() not in [4, 5, 6]:
106
+ raise ValueError(
107
+ "Expected 4D, 5D, or 6D tensor (corresponding to NA1D, 2D, 3D), "
108
+ f"got {tensor.dim()}D input."
109
+ )
110
+
111
+ na_dim = tensor.dim() - 3
112
+ assert na_dim in [1, 2, 3]
113
+
114
+ if len(tile_shape) != na_dim:
115
+ raise ValueError(
116
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
117
+ )
118
+
119
+ dilation = dilation or tuple(1 for _ in range(na_dim))
120
+
121
+ if len(dilation) != na_dim:
122
+ raise ValueError(
123
+ f"Expected {na_dim}D dilation for NA{na_dim}D, " f"got {dilation=}."
124
+ )
125
+
126
+ batch, *token_layout, heads, dim = tensor.shape
127
+
128
+ if any(
129
+ x % d != 0 or (x // d) % t != 0
130
+ for x, t, d in zip(token_layout, tile_shape, dilation)
131
+ ):
132
+ raise ValueError(
133
+ "Tensor must be divisible by static tile shape and dilation, but got "
134
+ f"{tensor.shape=}, {tile_shape=}, {dilation=}."
135
+ )
136
+
137
+ num_dilation_groups = math.prod(dilation)
138
+ token_layout_post_dilation = tuple(x // d for x, d in zip(token_layout, dilation))
139
+ rest = tuple(x // d // t for x, t, d in zip(token_layout, tile_shape, dilation))
140
+ logical_divide_dims = []
141
+ for d, r, t in zip(dilation, rest, tile_shape):
142
+ logical_divide_dims += [r, t, d]
143
+
144
+ # Two permutations at once:
145
+ # 1. logical divide to tiled divide
146
+ # 2. (optionally) flip order of tiled modes (i.e. (X,Y,Z) -> (Z,Y,X)) for compatibility with
147
+ # CuTe's identity layout mapping.
148
+ permutation_idxes_r = []
149
+ permutation_idxes_t = []
150
+ permutation_idxes_d = []
151
+ for i in range(na_dim):
152
+ if flip_tiled_dims:
153
+ permutation_idxes_r += [(na_dim - i - 1) * 3 + 1]
154
+ permutation_idxes_t += [(na_dim - i - 1) * 3 + 2]
155
+ permutation_idxes_d += [(na_dim - i - 1) * 3 + 3]
156
+ else:
157
+ permutation_idxes_r += [i * 3 + 1]
158
+ permutation_idxes_t += [i * 3 + 2]
159
+ permutation_idxes_d += [i * 3 + 3]
160
+
161
+ permutation_idxes = (
162
+ [0]
163
+ + permutation_idxes_d
164
+ + permutation_idxes_r
165
+ + permutation_idxes_t
166
+ + [na_dim * 3 + 1, na_dim * 3 + 2]
167
+ )
168
+
169
+ # View, not copy
170
+ tensor_tiled = tensor.view(batch, *logical_divide_dims, heads, dim)
171
+ if not is_torch_compiling():
172
+ assert tensor_tiled.data_ptr() == tensor.data_ptr()
173
+
174
+ # View, not copy
175
+ tensor_permuted = tensor_tiled.permute(*permutation_idxes)
176
+ if not is_torch_compiling():
177
+ assert tensor_permuted.data_ptr() == tensor_tiled.data_ptr()
178
+
179
+ # Reshape back and copy
180
+ tensor_flatten = tensor_permuted.reshape(
181
+ num_dilation_groups * batch, math.prod(token_layout_post_dilation), heads, dim
182
+ ).contiguous()
183
+ # NOTE: token permute without dilation is a no-op for 1-D
184
+ # assert na_dim == 1 or tensor_flatten.data_ptr() != tensor_permuted.data_ptr()
185
+ assert tensor_flatten.is_contiguous()
186
+
187
+ return tensor_flatten
188
+
189
+
190
+ def _token_unpermute(
191
+ tensor: Tensor,
192
+ token_layout: DimensionType,
193
+ tile_shape: DimensionType,
194
+ dilation: DimensionType,
195
+ flip_tiled_dims: bool,
196
+ ):
197
+ if tensor.dim() != 4:
198
+ raise ValueError(f"Expected flattened 4D tensor, got {tensor.dim()}D input.")
199
+
200
+ na_dim = len(token_layout)
201
+ assert na_dim in [1, 2, 3]
202
+
203
+ if len(tile_shape) != na_dim:
204
+ raise ValueError(
205
+ f"Expected {na_dim}D tiler for NA{na_dim}D, " f"got {tile_shape=}."
206
+ )
207
+
208
+ dilation = dilation or tuple(1 for _ in range(na_dim))
209
+
210
+ if len(dilation) != na_dim:
211
+ raise ValueError(
212
+ f"Expected {na_dim}D dilation for NA{na_dim}D, " f"got {dilation=}."
213
+ )
214
+
215
+ num_dilation_groups = math.prod(dilation)
216
+
217
+ batch, seqlen, heads, dim = tensor.shape
218
+
219
+ if batch % num_dilation_groups != 0:
220
+ raise ValueError(
221
+ "Expected batch size in token-permuted tensor to be divisible by "
222
+ f"number of dilation groups {num_dilation_groups} ({dilation=}), got {batch=}."
223
+ )
224
+
225
+ batch_actual = batch // num_dilation_groups
226
+
227
+ rest_shape = ceil_div_tuple(ceil_div_tuple(token_layout, tile_shape), dilation)
228
+ token_layout_padded = mul_tuple(mul_tuple(rest_shape, tile_shape), dilation)
229
+
230
+ # View, not copy
231
+ rest_shape_ = reversed(rest_shape) if flip_tiled_dims else rest_shape
232
+ tile_shape_ = reversed(tile_shape) if flip_tiled_dims else tile_shape
233
+ dilation_ = reversed(dilation) if flip_tiled_dims else dilation
234
+ tensor_tiled = tensor.view(
235
+ batch_actual, *dilation_, *rest_shape_, *tile_shape_, heads, dim
236
+ )
237
+ if not is_torch_compiling():
238
+ assert tensor_tiled.data_ptr() == tensor.data_ptr()
239
+
240
+ # Undo permutation
241
+ # batch
242
+ permutation_idxes = [0]
243
+
244
+ # dilation, rest, tile -> rest, tile, dilation
245
+ for i in range(na_dim):
246
+ if flip_tiled_dims:
247
+ permutation_idxes += [2 * na_dim - i, 3 * na_dim - i, na_dim - i]
248
+ else:
249
+ permutation_idxes += [na_dim + i + 1, 2 * na_dim + i + 1, i + 1]
250
+
251
+ # heads, head_dim
252
+ permutation_idxes += [na_dim * 3 + 1, na_dim * 3 + 2]
253
+
254
+ # View, not copy
255
+ tensor_permuted = tensor_tiled.permute(*permutation_idxes)
256
+ if not is_torch_compiling():
257
+ assert tensor_permuted.data_ptr() == tensor_tiled.data_ptr()
258
+
259
+ # Reshape back and copy
260
+ out = tensor_permuted.reshape(
261
+ batch_actual, *token_layout_padded, heads, dim
262
+ ).contiguous()
263
+ # NOTE: token permute without dilation is a no-op for 1-D
264
+ # assert na_dim == 1 or out.data_ptr() != tensor_permuted.data_ptr()
265
+ assert out.is_contiguous()
266
+
267
+ return out
268
+
269
+
270
+ def _maybe_unpad(tensor: Tensor, padding: DimensionType):
271
+ if tensor.dim() not in [4, 5, 6]:
272
+ raise ValueError(
273
+ "Expected 4D, 5D, or 6D tensor (corresponding to NA1D, 2D, 3D), "
274
+ f"got {tensor.dim()}D input."
275
+ )
276
+
277
+ na_dim = tensor.dim() - 3
278
+ assert na_dim in [1, 2, 3]
279
+
280
+ if len(padding) != na_dim:
281
+ raise ValueError(
282
+ f"Expected {na_dim}D padding shape for NA{na_dim}D, " f"got {padding=}."
283
+ )
284
+
285
+ token_layout = tensor.shape[1 : na_dim + 1]
286
+
287
+ # Slice
288
+ if any(p for p in padding):
289
+ assert all(p >= 0 for p in padding)
290
+
291
+ orig_lens = tuple(x - p for x, p in zip(token_layout, padding))
292
+
293
+ # TODO: there must be a better way
294
+ if len(orig_lens) == 1:
295
+ x = orig_lens[0]
296
+ return tensor[:, :x].contiguous()
297
+ elif len(orig_lens) == 2:
298
+ x, y = orig_lens
299
+ return tensor[:, :x, :y].contiguous()
300
+ elif len(orig_lens) == 3:
301
+ x, y, z = orig_lens
302
+ return tensor[:, :x, :y, :z].contiguous()
303
+ else:
304
+ raise NotImplementedError()
305
+
306
+ return tensor
307
+
308
+
309
+ def token_permute_torch(
310
+ tensor: Tensor,
311
+ tile_shape: DimensionType,
312
+ dilation: DimensionType,
313
+ flip_tiled_dims: bool,
314
+ ) -> Tensor:
315
+ if tensor.dim() not in [4, 5, 6]:
316
+ raise ValueError(
317
+ "Expected 4D, 5D, or 6D tensor (corresponding to NA1D, 2D, 3D), "
318
+ f"got {tensor.dim()}D input."
319
+ )
320
+
321
+ tensor_pad = _maybe_pad(tensor, tile_shape=tile_shape, dilation=dilation)
322
+ output = _token_permute(
323
+ tensor_pad,
324
+ tile_shape=tile_shape,
325
+ dilation=dilation,
326
+ flip_tiled_dims=flip_tiled_dims,
327
+ )
328
+
329
+ return output
330
+
331
+
332
+ def token_unpermute_torch(
333
+ tensor: Tensor,
334
+ token_layout: DimensionType,
335
+ tile_shape: DimensionType,
336
+ dilation: DimensionType,
337
+ flip_tiled_dims: bool,
338
+ ) -> Tensor:
339
+ if tensor.dim() != 4:
340
+ raise ValueError(f"Expected flattened 4D tensor, got {tensor.dim()}D input.")
341
+
342
+ token_layout_padded = mul_tuple(
343
+ mul_tuple(
344
+ ceil_div_tuple(ceil_div_tuple(token_layout, tile_shape), dilation),
345
+ dilation,
346
+ ),
347
+ tile_shape,
348
+ )
349
+ padding = sub_tuple(token_layout_padded, token_layout)
350
+
351
+ output = _maybe_unpad(
352
+ _token_unpermute(
353
+ tensor,
354
+ token_layout=token_layout,
355
+ tile_shape=tile_shape,
356
+ dilation=dilation,
357
+ flip_tiled_dims=flip_tiled_dims,
358
+ ),
359
+ padding=padding,
360
+ )
361
+
362
+ return output
363
+
364
+
365
+ __all__ = [
366
+ "token_permute_torch",
367
+ "token_unpermute_torch",
368
+ ]
build/torch210-cu126-aarch64-linux/utils/__init__.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
build/torch210-cu126-aarch64-linux/utils/checks.py ADDED
@@ -0,0 +1,726 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import functools
25
+ from collections.abc import Sequence
26
+ from typing import Any, Optional, Tuple, Union
27
+
28
+ import torch # noqa: F401
29
+ from torch import Tensor
30
+
31
+ from .._types import CausalArgType, DimensionType, KernelSchedule, NoneType
32
+ from ..utils import log
33
+ from ..utils.environment import is_torch_compiling
34
+ from ..utils.tuples import create_causal_arg_from_bool, create_dim_from_int
35
+ from ..utils.varlen import generate_varlen_parameters
36
+
37
+ logger = log.get_logger(__name__)
38
+
39
+
40
+ def log_or_raise_error(
41
+ msg: str, raise_error: bool = False, exception: Any = RuntimeError
42
+ ):
43
+ if raise_error:
44
+ raise exception(msg)
45
+ else:
46
+ logger.debug(msg)
47
+
48
+
49
+ def _universal_tensor_checks(
50
+ query: Tensor, key: Tensor, value: Tensor, raise_error: bool = True
51
+ ) -> bool:
52
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
53
+
54
+ if query.is_sparse or key.is_sparse or value.is_sparse:
55
+ target_fn(
56
+ "NATTEN does not support sparse tensors.", exception=NotImplementedError
57
+ )
58
+ return False
59
+
60
+ if query.is_nested or key.is_nested or value.is_nested:
61
+ target_fn(
62
+ "NATTEN does not support nested tensors.", exception=NotImplementedError
63
+ )
64
+ return False
65
+
66
+ if query.device != key.device or query.device != value.device:
67
+ target_fn(
68
+ "Query, key, and value must be on the same device, "
69
+ f"got {query.device=}, {key.device=}, {value.device=}.",
70
+ exception=ValueError,
71
+ )
72
+ return False
73
+
74
+ if query.dtype != key.dtype or query.dtype != value.dtype:
75
+ target_fn(
76
+ "Query, key, and value must assume the same data type, "
77
+ f"got {query.dtype=}, {key.dtype=}, {value.dtype=}.",
78
+ exception=ValueError,
79
+ )
80
+ return False
81
+
82
+ return True
83
+
84
+
85
+ def na_tensor_checks(
86
+ query: Tensor,
87
+ key: Tensor,
88
+ value: Tensor,
89
+ must_match_head_dims: bool = False,
90
+ supports_gqa_mqa: bool = True,
91
+ raise_error: bool = True,
92
+ backend_name: Optional[str] = None,
93
+ ) -> bool:
94
+ backend_name = backend_name or "This operation/backend"
95
+ if not _universal_tensor_checks(query, key, value):
96
+ return False
97
+
98
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
99
+
100
+ if query.dim() != key.dim() or query.dim() != value.dim():
101
+ target_fn(
102
+ "Query, key, and value must have the same rank, "
103
+ f"got {query.dim()=}, {key.dim()=}, {value.dim()=}.",
104
+ exception=ValueError,
105
+ )
106
+ return False
107
+
108
+ if query.dim() not in [4, 5, 6]:
109
+ target_fn(
110
+ "Expected 4-D, 5-D, or 6-D tensors as inputs (corresponding to NA1D, NA2D, and NA3D), "
111
+ f"got {query.dim()=}.",
112
+ exception=ValueError,
113
+ )
114
+ return False
115
+
116
+ na_dim = query.dim() - 3 # minus batch, heads, head_dim
117
+
118
+ if query.shape[-1] != key.shape[-1]:
119
+ target_fn(
120
+ f"Q and K head dims must match, got {query.shape[-1]=}, {key.shape[-1]=}.",
121
+ exception=ValueError,
122
+ )
123
+ return False
124
+
125
+ if query.shape[0] != key.shape[0] or query.shape[0] != value.shape[0]:
126
+ target_fn(
127
+ "Q, K, and V must match in batch size, got "
128
+ f"{query.shape[0]=}, {key.shape[0]=}, {value.shape[0]=}.",
129
+ exception=ValueError,
130
+ )
131
+ return False
132
+
133
+ if must_match_head_dims and query.shape[-1] != value.shape[-1]:
134
+ target_fn(
135
+ f"{backend_name} does not support different head dims for QK and V, got "
136
+ f"{query.shape[-1]=}, {value.shape[-1]=}.",
137
+ exception=ValueError,
138
+ )
139
+ return False
140
+
141
+ if (
142
+ query.shape[1 : na_dim + 1] != key.shape[1 : na_dim + 1]
143
+ or query.shape[1 : na_dim + 1] != value.shape[1 : na_dim + 1]
144
+ ):
145
+ target_fn(
146
+ "Neighborhood Attention operations require Q, K, and V to match in their token layouts, got "
147
+ f"{query.shape[1:na_dim+1]=}, {key.shape[1:na_dim+1]=}, {value.shape[1:na_dim+1]=}.",
148
+ exception=ValueError,
149
+ )
150
+ return False
151
+
152
+ if not supports_gqa_mqa and (
153
+ query.shape[-2] != key.shape[-2] or query.shape[-2] != value.shape[-2]
154
+ ):
155
+ target_fn(
156
+ f"{backend_name} does not support GQA/MQA, therefore number of heads in Q, K, and V "
157
+ f"must match, got {query.shape[-2]=}, {key.shape[-2]=}, {value.shape[-2]=}.",
158
+ exception=ValueError,
159
+ )
160
+ return False
161
+
162
+ if supports_gqa_mqa:
163
+ if key.shape[-2] != value.shape[-2]:
164
+ target_fn(
165
+ "Key and value must always have the same number of heads, got "
166
+ f"{key.shape[-2]=}, {value.shape[-2]=}.",
167
+ exception=ValueError,
168
+ )
169
+ return False
170
+
171
+ heads_q = query.shape[-2]
172
+ heads_kv = key.shape[-2]
173
+
174
+ if heads_q < heads_kv or heads_q % heads_kv != 0:
175
+ target_fn(
176
+ "Key/value heads must evenly divide query heads, got "
177
+ f"{heads_q=}, {heads_kv=}.",
178
+ exception=ValueError,
179
+ )
180
+ return False
181
+
182
+ return True
183
+
184
+
185
+ def fmha_tensor_checks(
186
+ query: Tensor,
187
+ key: Tensor,
188
+ value: Tensor,
189
+ must_match_head_dims: bool = False,
190
+ supports_gqa_mqa: bool = True,
191
+ raise_error: bool = True,
192
+ backend_name: Optional[str] = None,
193
+ ) -> bool:
194
+ backend_name = backend_name or "This operation/backend"
195
+ if not _universal_tensor_checks(query, key, value):
196
+ return False
197
+
198
+ target_fn = functools.partial(log_or_raise_error, raise_error=raise_error)
199
+
200
+ if query.dim() != key.dim() or query.dim() != value.dim():
201
+ target_fn(
202
+ "Query, key, and value must have the same rank, "
203
+ f"got {query.dim()=}, {key.dim()=}, {value.dim()=}.",
204
+ exception=ValueError,
205
+ )
206
+ return False
207
+
208
+ if query.dim() != 4:
209
+ target_fn(
210
+ "Expected 4-D tensors as inputs to FMHA, " f"got {query.dim()=}.",
211
+ exception=ValueError,
212
+ )
213
+ return False
214
+
215
+ if query.shape[-1] != key.shape[-1]:
216
+ target_fn(
217
+ f"Q and K head dims must match, got {query.shape[-1]=}, {key.shape[-1]=}.",
218
+ exception=ValueError,
219
+ )
220
+ return False
221
+
222
+ if must_match_head_dims and query.shape[-1] != value.shape[-1]:
223
+ target_fn(
224
+ f"{backend_name} does not support different head dims for QK and V, got "
225
+ f"{query.shape[-1]=}, {value.shape[-1]=}.",
226
+ exception=ValueError,
227
+ )
228
+ return False
229
+
230
+ if query.shape[0] != key.shape[0] or query.shape[0] != value.shape[0]:
231
+ target_fn(
232
+ "Q, K, and V must match in batch size, got "
233
+ f"{query.shape[0]=}, {key.shape[0]=}, {value.shape[0]=}.",
234
+ exception=ValueError,
235
+ )
236
+ return False
237
+
238
+ if key.shape[1] != value.shape[1]:
239
+ target_fn(
240
+ f"K and V must match in sequence length, got {key.shape[1]=}, {value.shape[1]=}.",
241
+ exception=ValueError,
242
+ )
243
+ return False
244
+
245
+ if not supports_gqa_mqa and (
246
+ query.shape[-2] != key.shape[-2] or query.shape[-2] != value.shape[-2]
247
+ ):
248
+ target_fn(
249
+ f"{backend_name} does not support GQA/MQA, therefore number of heads in Q, K, and V "
250
+ f"must match, got {query.shape[-2]=}, {key.shape[-2]=}, {value.shape[-2]=}.",
251
+ exception=ValueError,
252
+ )
253
+ return False
254
+
255
+ if supports_gqa_mqa:
256
+ if key.shape[-2] != value.shape[-2]:
257
+ target_fn(
258
+ "Key and value must always have the same number of heads, got "
259
+ f"{key.shape[-2]=}, {value.shape[-2]=}.",
260
+ exception=ValueError,
261
+ )
262
+ return False
263
+
264
+ heads_q = query.shape[-2]
265
+ heads_kv = key.shape[-2]
266
+
267
+ if heads_q < heads_kv or heads_q % heads_kv != 0:
268
+ target_fn(
269
+ "Key/value heads must evenly divide query heads, got "
270
+ f"{heads_q=}, {heads_kv=}.",
271
+ exception=ValueError,
272
+ )
273
+ return False
274
+
275
+ return True
276
+
277
+
278
+ def additional_kv_tensor_checks(
279
+ query: Tensor,
280
+ key: Tensor,
281
+ value: Tensor,
282
+ add_key: Optional[Tensor] = None,
283
+ add_value: Optional[Tensor] = None,
284
+ must_match_head_dims: bool = False,
285
+ supports_gqa_mqa: bool = True,
286
+ ):
287
+
288
+ if (add_key is not None) ^ (add_value is not None):
289
+ raise ValueError(
290
+ "`additional_keys` and `additional_values` must be either both Tensors or None."
291
+ )
292
+
293
+ if add_key is None:
294
+ return
295
+
296
+ assert add_key is not None and add_value is not None
297
+
298
+ _universal_tensor_checks(query, add_key, add_value)
299
+
300
+ if query.shape[-1] != add_key.shape[-1]:
301
+ raise ValueError(
302
+ f"Q and K head dims must match, got {query.shape[-1]=}, {add_key.shape[-1]=}."
303
+ )
304
+
305
+ if must_match_head_dims and query.shape[-1] != add_value.shape[-1]:
306
+ raise ValueError(
307
+ "This operation does not support different head dims for QK and V, got "
308
+ f"{query.shape[-1]=}, {add_value.shape[-1]=}."
309
+ )
310
+
311
+ if query.shape[0] != add_key.shape[0] or query.shape[0] != add_value.shape[0]:
312
+ raise ValueError(
313
+ "Q, additional K, and additional V must match in batch size, got "
314
+ f"{query.shape[0]=}, {add_key.shape[0]=}, {add_value.shape[0]=}."
315
+ )
316
+
317
+ if add_key.shape[1] != add_value.shape[1]:
318
+ raise ValueError(
319
+ f"Additional K and V must match in sequence length, got {add_key.shape[1]=}, "
320
+ f"{add_value.shape[1]=}."
321
+ )
322
+
323
+ if key.shape[0] != add_key.shape[0] or value.shape[0] != add_value.shape[0]:
324
+ raise ValueError(
325
+ "Additional key/value tokens must match the self attention key/value tokens in batch "
326
+ f"size, got {key.shape[0]=} != {add_key.shape[0]=}, and "
327
+ f"{value.shape[0]=} != {add_value.shape[0]=}."
328
+ )
329
+
330
+ if key.shape[-2] != add_key.shape[-2] or value.shape[-2] != add_value.shape[-2]:
331
+ raise ValueError(
332
+ "Additional key/value tokens must match the self attention key/value tokens in number "
333
+ f"of heads, got {key.shape[-2]=} != {add_key.shape[-2]=}, and "
334
+ f"{value.shape[-2]=} != {add_value.shape[-2]=}."
335
+ )
336
+
337
+ if key.shape[-1] != add_key.shape[-1] or value.shape[-1] != add_value.shape[-1]:
338
+ raise ValueError(
339
+ "Additional key/value tokens must match the self attention key/value tokens in head "
340
+ f"dim, got {key.shape[-1]=} != {add_key.shape[-1]=}, and "
341
+ f"{value.shape[-1]=} != {add_value.shape[-1]=}."
342
+ )
343
+
344
+ if not supports_gqa_mqa and (
345
+ query.shape[-2] != add_key.shape[-2] or query.shape[-2] != add_value.shape[-2]
346
+ ):
347
+ raise ValueError(
348
+ f"This operation does not support GQA/MQA, therefore number of heads in Q, K, and V "
349
+ f"must match, got {query.shape[-2]=}, {key.shape[-2]=}, {value.shape[-2]=}."
350
+ )
351
+
352
+ if supports_gqa_mqa:
353
+ if (
354
+ key.shape[-2] != value.shape[-2]
355
+ or key.shape[-2] != add_key.shape[-2]
356
+ or key.shape[-2] != add_value.shape[-2]
357
+ ):
358
+ raise ValueError(
359
+ "Key and value, original and additional, must always have the same number of heads, got "
360
+ f"{key.shape[-2]=}, {value.shape[-2]=}, {add_key.shape[-2]=}, {add_value.shape[-2]=}."
361
+ )
362
+
363
+ heads_q = query.shape[-2]
364
+ heads_kv = key.shape[-2]
365
+
366
+ if heads_q < heads_kv or heads_q % heads_kv != 0:
367
+ raise ValueError(
368
+ "Key/value heads must evenly divide query heads, got "
369
+ f"{heads_q=}, {heads_kv=}."
370
+ )
371
+
372
+
373
+ def check_input_size_arg(na_dim: int, input_size: Any) -> DimensionType:
374
+ assert na_dim > 0 and na_dim < 4
375
+ if (
376
+ isinstance(input_size, Sequence)
377
+ and len(input_size) == na_dim
378
+ and all(isinstance(x, int) and x > 1 for x in input_size)
379
+ ):
380
+ return tuple(x for x in input_size)
381
+
382
+ if isinstance(input_size, int) and input_size > 1:
383
+ return create_dim_from_int(na_dim, value=input_size)
384
+
385
+ raise ValueError(
386
+ "Invalid value for `input_size`; expected an integer or iterable of integers, all >= 2, "
387
+ f"got {type(input_size)=}, {input_size=}."
388
+ )
389
+
390
+
391
+ def check_kernel_size_arg(na_dim: int, kernel_size: Any) -> DimensionType:
392
+ assert na_dim > 0 and na_dim < 4
393
+ if (
394
+ isinstance(kernel_size, Sequence)
395
+ and len(kernel_size) == na_dim
396
+ and all(isinstance(x, int) and x > 1 for x in kernel_size)
397
+ ):
398
+ return tuple(x for x in kernel_size)
399
+
400
+ if isinstance(kernel_size, int) and kernel_size > 1:
401
+ return create_dim_from_int(na_dim, value=kernel_size)
402
+
403
+ raise ValueError(
404
+ "Invalid value for `kernel_size`; expected an integer or iterable of integers, all >= 2, "
405
+ f"got {type(kernel_size)=}, {kernel_size=}."
406
+ )
407
+
408
+
409
+ def check_stride_arg(na_dim: int, stride: Any) -> DimensionType:
410
+ assert na_dim > 0 and na_dim < 4
411
+ if stride is None:
412
+ return create_dim_from_int(na_dim, value=1)
413
+
414
+ if (
415
+ isinstance(stride, Sequence)
416
+ and len(stride) == na_dim
417
+ and all(isinstance(x, int) and x > 0 for x in stride)
418
+ ):
419
+ return tuple(x for x in stride)
420
+
421
+ if isinstance(stride, int) and stride > 0:
422
+ return create_dim_from_int(na_dim, value=stride)
423
+
424
+ raise ValueError(
425
+ "Invalid value for `stride`; expected an integer or tuple of positive integers, "
426
+ f"got {type(stride)=}, {stride=}."
427
+ )
428
+
429
+
430
+ def check_dilation_arg(na_dim: int, dilation: Any) -> DimensionType:
431
+ assert na_dim > 0 and na_dim < 4
432
+ if dilation is None:
433
+ return create_dim_from_int(na_dim, value=1)
434
+
435
+ if (
436
+ isinstance(dilation, Sequence)
437
+ and len(dilation) == na_dim
438
+ and all(isinstance(x, int) and x > 0 for x in dilation)
439
+ ):
440
+ return tuple(x for x in dilation)
441
+
442
+ if isinstance(dilation, int) and dilation > 0:
443
+ return create_dim_from_int(na_dim, value=dilation)
444
+
445
+ raise ValueError(
446
+ "Invalid value for `dilation`; expected an integer or tuple of positive integers, "
447
+ f"got {type(dilation)=}, {dilation=}."
448
+ )
449
+
450
+
451
+ def check_causal_arg(na_dim: int, is_causal: Any) -> CausalArgType:
452
+ assert na_dim > 0 and na_dim < 4
453
+
454
+ if is_causal is None:
455
+ return create_causal_arg_from_bool(na_dim, value=False)
456
+
457
+ if (
458
+ isinstance(is_causal, Sequence)
459
+ and len(is_causal) == na_dim
460
+ and all(isinstance(c, bool) for c in is_causal)
461
+ ):
462
+ return tuple(c for c in is_causal)
463
+
464
+ if isinstance(is_causal, bool):
465
+ return create_causal_arg_from_bool(na_dim, value=is_causal)
466
+
467
+ raise ValueError(
468
+ "Invalid value for `is_causal`; expected a boolean or tuple of booleans, "
469
+ f"got {type(is_causal)=}, {is_causal=}."
470
+ )
471
+
472
+
473
+ def check_all_args(
474
+ na_dim: int, kernel_size: Any, stride: Any, dilation: Any, is_causal: Any
475
+ ) -> Tuple[DimensionType, DimensionType, DimensionType, CausalArgType]:
476
+ kernel_size_out, stride_out, dilation_out, is_causal_out = (
477
+ check_kernel_size_arg(na_dim, kernel_size),
478
+ check_stride_arg(na_dim, stride),
479
+ check_dilation_arg(na_dim, dilation),
480
+ check_causal_arg(na_dim, is_causal),
481
+ )
482
+
483
+ return kernel_size_out, stride_out, dilation_out, is_causal_out
484
+
485
+
486
+ def check_args_against_input(
487
+ input_tensor: Tensor,
488
+ kernel_size: DimensionType,
489
+ stride: DimensionType,
490
+ dilation: DimensionType,
491
+ is_causal: CausalArgType,
492
+ ):
493
+ assert input_tensor.dim() in [4, 5, 6]
494
+ na_dim = input_tensor.dim() - 3
495
+ input_size = input_tensor.shape[1 : 1 + na_dim]
496
+
497
+ if any(k * d > x for x, k, d in zip(input_size, kernel_size, dilation)):
498
+ raise ValueError(
499
+ "The product of kernel size and dilation cannot be larger than input size "
500
+ f"along any dimension, got {input_size=} ({input_tensor.shape=}), "
501
+ f"{kernel_size=}, {dilation=}."
502
+ )
503
+
504
+ if any(s > k for k, s in zip(kernel_size, stride)):
505
+ raise ValueError(
506
+ "Stride cannot be larger than kernel size along any dimension, got "
507
+ f"{kernel_size=}, {stride=}."
508
+ )
509
+
510
+
511
+ def is_self_attention(
512
+ input_tensor: Tensor,
513
+ kernel_size: DimensionType,
514
+ is_causal: CausalArgType,
515
+ has_additional_attention: bool,
516
+ ):
517
+ assert input_tensor.dim() in [4, 5, 6]
518
+ na_dim = input_tensor.dim() - 3
519
+ input_size = input_tensor.shape[1 : 1 + na_dim]
520
+
521
+ # Special case: 1-D causal with full window is equivalent to standard 1-D causal
522
+ # as long as there isn't any additional context (non causal)
523
+ if na_dim == 1 and not has_additional_attention:
524
+ return kernel_size[0] == input_size[0]
525
+
526
+ return all(k == x and not c for x, k, c in zip(input_size, kernel_size, is_causal))
527
+
528
+
529
+ def check_tile_shape(
530
+ tile_shape: Any,
531
+ ) -> DimensionType:
532
+ if (
533
+ isinstance(tile_shape, Sequence)
534
+ and len(tile_shape) <= 3
535
+ and all(isinstance(x, int) for x in tile_shape)
536
+ ):
537
+ return tuple(x for x in tile_shape)
538
+
539
+ raise ValueError(
540
+ f"Unsupported value for tile shape; expected an iterable of at most 3 integers, "
541
+ f"got {type(tile_shape)=}, {tile_shape}."
542
+ )
543
+
544
+
545
+ def check_kernel_schedule(kernel_schedule: Any) -> Optional[KernelSchedule]:
546
+ if kernel_schedule is None:
547
+ return None
548
+
549
+ if isinstance(kernel_schedule, KernelSchedule):
550
+ return kernel_schedule
551
+
552
+ if kernel_schedule == "non":
553
+ return KernelSchedule.NonPersistent
554
+ elif kernel_schedule == "coop":
555
+ return KernelSchedule.WarpSpecializedCooperative
556
+ elif kernel_schedule == "pp":
557
+ return KernelSchedule.WarpSpecializedPingpong
558
+
559
+ raise ValueError(
560
+ f"Kernel schedule {kernel_schedule} is invalid; choices are: "
561
+ "`non` (non-persistent), `coop` (warp-specialized cooperative), and "
562
+ "`pp` (warp-specialized ping-ponging)."
563
+ )
564
+
565
+
566
+ # Varlen FMHA Checks
567
+
568
+
569
+ def varlen_tensor_checks(
570
+ query: Tensor,
571
+ key: Tensor,
572
+ value: Tensor,
573
+ seqlens_Q: Optional[Tensor] = None,
574
+ seqlens_KV: Optional[Tensor] = None,
575
+ cumulative_seqlen_Q: Optional[Tensor] = None,
576
+ cumulative_seqlen_KV: Optional[Tensor] = None,
577
+ max_seqlen_Q: Optional[int] = None,
578
+ max_seqlen_KV: Optional[int] = None,
579
+ ) -> Union[
580
+ Tuple[NoneType, NoneType, int, int],
581
+ Tuple[Tensor, Tensor, int, int],
582
+ ]:
583
+ if query.shape[0] != key.shape[0] or query.shape[0] != value.shape[0]:
584
+ raise ValueError(
585
+ "Q, K, and V must match in batch size, got "
586
+ f"{query.shape[0]=}, {key.shape[0]=}, {value.shape[0]=}."
587
+ )
588
+
589
+ if all(
590
+ x is None
591
+ for x in [
592
+ seqlens_Q,
593
+ seqlens_KV,
594
+ cumulative_seqlen_Q,
595
+ cumulative_seqlen_KV,
596
+ ]
597
+ ) and all(
598
+ x is None or x == 0
599
+ for x in [
600
+ max_seqlen_Q,
601
+ max_seqlen_KV,
602
+ ]
603
+ ):
604
+ # Not varlen
605
+ return None, None, 0, 0
606
+
607
+ if seqlens_Q is not None or seqlens_KV is not None:
608
+ # Generate cumulative_seqlen_{Q,KV}, max_seqlen_{Q,KV}, total_seqlen_{Q,KV}
609
+ # based on user input
610
+ return generate_varlen_parameters(
611
+ query=query,
612
+ key=key,
613
+ value=value,
614
+ seqlens_Q=seqlens_Q,
615
+ seqlens_KV=seqlens_KV,
616
+ )
617
+
618
+ # Validate user-input cumulative_seqlen_{Q,KV}, max_seqlen_{Q,KV}, total_seqlen_{Q,KV}
619
+ if any(
620
+ x is None
621
+ for x in [
622
+ cumulative_seqlen_Q,
623
+ cumulative_seqlen_KV,
624
+ max_seqlen_Q,
625
+ max_seqlen_KV,
626
+ ]
627
+ ):
628
+ raise ValueError(
629
+ "Variable length Attention requires all of "
630
+ "cumulative_seqlen_{Q,KV} and max_seqlen_{Q,KV} to be set."
631
+ )
632
+
633
+ if query.shape[0] != 1:
634
+ raise ValueError(
635
+ "Variable length Attention only supports sequence-packed memory layout "
636
+ f"(batch = 1), got {query.shape[0]=}."
637
+ )
638
+
639
+ assert cumulative_seqlen_Q is not None
640
+ assert cumulative_seqlen_KV is not None
641
+ assert max_seqlen_Q is not None
642
+ assert max_seqlen_KV is not None
643
+
644
+ if not isinstance(max_seqlen_Q, int) or not isinstance(max_seqlen_KV, int):
645
+ raise ValueError(
646
+ "max_seqlen_Q and max_seqlen_KV must be ints, got "
647
+ f"{type(max_seqlen_Q)=}, {type(max_seqlen_KV)=}, {max_seqlen_Q=}, {max_seqlen_KV=}."
648
+ )
649
+
650
+ total_seqlen_Q = query.shape[1]
651
+ total_seqlen_KV = key.shape[1]
652
+ if max_seqlen_Q > total_seqlen_Q:
653
+ raise ValueError(
654
+ "Maximum sequence length cannot exceed total, got "
655
+ f"{max_seqlen_Q=}, {total_seqlen_Q=}."
656
+ )
657
+
658
+ if max_seqlen_KV > total_seqlen_KV:
659
+ raise ValueError(
660
+ "Maximum sequence length cannot exceed total, got "
661
+ f"{max_seqlen_KV=}, {total_seqlen_KV=}."
662
+ )
663
+
664
+ # NOTE: this check introduces recompiles
665
+ if not is_torch_compiling():
666
+ if (max_seqlen_Q == 0) != (max_seqlen_KV == 0):
667
+ raise ValueError(
668
+ "max_seqlen_Q and max_seqlen_KV must both be zero or both be non-zero, got "
669
+ f"{max_seqlen_Q=}, {max_seqlen_KV=}."
670
+ )
671
+
672
+ if max_seqlen_Q < 0 or max_seqlen_KV < 0:
673
+ raise ValueError(
674
+ "Maximum sequence length cannot be negative, got "
675
+ f"{max_seqlen_Q=}, {max_seqlen_KV=}."
676
+ )
677
+
678
+ if not isinstance(cumulative_seqlen_Q, Tensor) or not isinstance(
679
+ cumulative_seqlen_KV, Tensor
680
+ ):
681
+ raise ValueError(
682
+ "cumulative_seqlen_Q and cumulative_seqlen_KV must both be tensors."
683
+ )
684
+
685
+ if (
686
+ cumulative_seqlen_Q.device != query.device
687
+ or cumulative_seqlen_KV.device != query.device
688
+ ):
689
+ raise ValueError(
690
+ "cumulative_seqlen_Q and cumulative_seqlen_KV must be on the same device as QKV, but "
691
+ f"{cumulative_seqlen_Q.device=}, {cumulative_seqlen_KV.device=}, {query.device=}."
692
+ )
693
+
694
+ if (
695
+ cumulative_seqlen_Q.dtype != torch.int32
696
+ or cumulative_seqlen_KV.dtype != torch.int32
697
+ ):
698
+ raise ValueError(
699
+ "cumulative_seqlen_Q and cumulative_seqlen_KV must both be torch.int32 tensors, got "
700
+ f"{cumulative_seqlen_Q.dtype=}, {cumulative_seqlen_KV.dtype=}."
701
+ )
702
+
703
+ if cumulative_seqlen_Q.dim() != 1 or cumulative_seqlen_KV.dim() != 1:
704
+ raise ValueError(
705
+ "cumulative_seqlen_Q and cumulative_seqlen_KV must both be 1-D tensors, got "
706
+ f"{cumulative_seqlen_Q.dim()=}, {cumulative_seqlen_KV.dim()=}."
707
+ )
708
+
709
+ if cumulative_seqlen_Q.shape[0] != cumulative_seqlen_KV.shape[0]:
710
+ raise ValueError(
711
+ "cumulative_seqlen_Q and cumulative_seqlen_KV must match in size, got "
712
+ f"{cumulative_seqlen_Q.shape=}, {cumulative_seqlen_KV.shape=}."
713
+ )
714
+
715
+ if cumulative_seqlen_Q.shape[0] < 2:
716
+ raise ValueError(
717
+ "cumulative_seqlen_Q and cumulative_seqlen_KV must contain at least 2 elements, got "
718
+ f"{cumulative_seqlen_Q.shape=}, {cumulative_seqlen_KV.shape=}."
719
+ )
720
+
721
+ return (
722
+ cumulative_seqlen_Q,
723
+ cumulative_seqlen_KV,
724
+ max_seqlen_Q,
725
+ max_seqlen_KV,
726
+ )
build/torch210-cu126-aarch64-linux/utils/device.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import Optional
25
+
26
+ import torch
27
+
28
+
29
+ def is_cuda(device: torch.device) -> bool:
30
+ return torch.cuda.is_available() and torch.version.cuda and device.type == "cuda" # type: ignore
31
+
32
+
33
+ def is_rocm(device: torch.device) -> bool:
34
+ return torch.cuda.is_available() and torch.version.hip and device.type == "cuda" # type: ignore
35
+
36
+
37
+ def is_cpu(device: torch.device) -> bool:
38
+ return device.type == "cpu"
39
+
40
+
41
+ def get_device_cc(device: Optional[torch.device] = None) -> int:
42
+ if (
43
+ torch.cuda.is_available()
44
+ and torch.version.cuda
45
+ and (device is None or is_cuda(device))
46
+ ):
47
+ major, minor = torch.cuda.get_device_capability(device)
48
+ return major * 10 + minor
49
+
50
+ return 0
build/torch210-cu126-aarch64-linux/utils/dtype.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import torch
25
+
26
+
27
+ def is_full(dtype: torch.dtype) -> bool:
28
+ return dtype == torch.float32
29
+
30
+
31
+ def is_half(dtype: torch.dtype) -> bool:
32
+ return dtype in [torch.float16, torch.bfloat16]
33
+
34
+
35
+ def is_fp8(dtype: torch.dtype) -> bool:
36
+ return dtype in [torch.float8_e5m2, torch.float8_e4m3fn]
build/torch210-cu126-aarch64-linux/utils/environment.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import os
25
+
26
+ import torch
27
+
28
+ from ..utils.device import get_device_cc
29
+
30
+
31
+ def parse_env_flag(env_var: str, default: bool) -> bool:
32
+ default_str = "1" if default else "0"
33
+ out_str = os.getenv(env_var, default_str)
34
+ if out_str.strip() == "":
35
+ return default
36
+ if out_str == "0":
37
+ return False
38
+ if out_str == "1":
39
+ return True
40
+ return default
41
+
42
+
43
+ def parse_env_int(env_var: str, default: int) -> int:
44
+ out_str = os.getenv(env_var, str(default))
45
+ if out_str.strip() == "":
46
+ return default
47
+ try:
48
+ return int(out_str)
49
+ except ValueError:
50
+ return default
51
+
52
+
53
+ def parse_env_str(env_var: str, default: str) -> str:
54
+ return os.getenv(env_var, str(default))
55
+
56
+
57
+ _IS_CUDA_AVAILABLE = torch.cuda.is_available()
58
+
59
+ _TORCH_VERSION = [int(x) for x in torch.__version__.split(".")[:2]]
60
+
61
+ _IS_TORCH_COMPILE_SUPPORTED = _TORCH_VERSION >= [2, 6] and get_device_cc() >= 70
62
+
63
+ # Guard registering libnatten APIs as torch ops with environment variables
64
+ # In case any unusual bugs from torch compile come up again
65
+ # Also restrict to torch 2.8 and later
66
+ # https://github.com/pytorch/pytorch/issues/137979#issuecomment-3614956989
67
+ DISABLE_TORCH_OPS = _TORCH_VERSION < [2, 8] or parse_env_flag(
68
+ "NATTEN_DISABLE_TORCH_OPS", False
69
+ )
70
+
71
+
72
+ # Controls all regions guarded against torch compile
73
+ # Logs, and certain assertions cause graph breaks.
74
+ def is_torch_compiling() -> bool:
75
+ try:
76
+ return torch.compiler.is_compiling()
77
+ except:
78
+ # Assume too old to support torch compile
79
+ return False
build/torch210-cu126-aarch64-linux/utils/log.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import enum
25
+ import logging
26
+ import os
27
+ import sys
28
+
29
+ from ..utils.environment import is_torch_compiling, parse_env_str
30
+
31
+ log_format = "| %(asctime)s | [[ %(name)s ]] [ %(levelname)s ]: %(message)s"
32
+
33
+
34
+ class LogLevel(enum.Enum):
35
+ Default = 0
36
+ Debug = 1
37
+ Info = 2
38
+ Warnings = 3
39
+ Errors = 4
40
+ Critical = 5
41
+
42
+
43
+ def _get_log_level() -> LogLevel:
44
+ log_level = parse_env_str("NATTEN_LOG_LEVEL", "").lower()
45
+
46
+ if log_level == "debug":
47
+ return LogLevel.Debug
48
+ elif log_level == "info":
49
+ return LogLevel.Info
50
+ elif log_level == "warning":
51
+ return LogLevel.Warnings
52
+ elif log_level == "error":
53
+ return LogLevel.Errors
54
+ elif log_level == "critical":
55
+ return LogLevel.Critical
56
+
57
+ return LogLevel.Default
58
+
59
+
60
+ _map_log_level = {
61
+ LogLevel.Default: logging.INFO,
62
+ LogLevel.Debug: logging.DEBUG,
63
+ LogLevel.Info: logging.INFO,
64
+ LogLevel.Warnings: logging.WARNING,
65
+ LogLevel.Errors: logging.ERROR,
66
+ LogLevel.Critical: logging.CRITICAL,
67
+ }
68
+
69
+
70
+ # Tests will stream into stderr instead of stdout
71
+ # It can be set to either stderr, stdout or any writeable file.
72
+ # Otherwise logging will be disabled.
73
+ def _get_log_pipe():
74
+ log_pipe = parse_env_str("NATTEN_LOG_PIPE", "stdout")
75
+
76
+ # Skip checking /dev/null writablity
77
+ if log_pipe == "/dev/null":
78
+ return None
79
+
80
+ if log_pipe.lower() == "stderr":
81
+ return sys.stderr
82
+
83
+ if log_pipe.lower() == "stdout":
84
+ return sys.stdout
85
+
86
+ # Treat as file path; validate writability
87
+ if os.path.isfile(log_pipe) and os.access(log_pipe, os.W_OK):
88
+ return log_pipe
89
+
90
+ try:
91
+ open(log_pipe, "a").close()
92
+ return log_pipe
93
+ except OSError:
94
+ pass
95
+
96
+ return None
97
+
98
+
99
+ class NattenLogger:
100
+ def __init__(self, name: str):
101
+ self.logger = logging.getLogger(name)
102
+ self.log_level = _map_log_level[_get_log_level()]
103
+ self.logger.setLevel(self.log_level)
104
+ self.formatter = logging.Formatter(log_format)
105
+ log_pipe = _get_log_pipe()
106
+ if log_pipe in [sys.stderr, sys.stdout]:
107
+ self.handler = logging.StreamHandler(log_pipe)
108
+ elif isinstance(log_pipe, str):
109
+ self.handler = logging.FileHandler(log_pipe)
110
+ else:
111
+ # Invalid / null
112
+ self.handler = logging.NullHandler() # type: ignore[assignment]
113
+ self.handler.setLevel(self.log_level)
114
+ self.handler.setFormatter(self.formatter)
115
+ self.logger.addHandler(self.handler)
116
+
117
+ def is_safe_to_log(self) -> bool:
118
+ return not is_torch_compiling()
119
+
120
+ def info(self, *args, **kwargs):
121
+ if self.is_safe_to_log():
122
+ self.logger.info(*args, **kwargs)
123
+
124
+ def debug(self, *args, **kwargs):
125
+ if self.is_safe_to_log():
126
+ self.logger.debug(*args, **kwargs)
127
+
128
+ def warning(self, *args, **kwargs):
129
+ if self.is_safe_to_log():
130
+ self.logger.warning(*args, **kwargs)
131
+
132
+
133
+ def get_logger(name) -> NattenLogger:
134
+ return NattenLogger(name)
build/torch210-cu126-aarch64-linux/utils/tensor.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ from typing import Optional
25
+
26
+ import torch
27
+ from torch import Size, Tensor
28
+
29
+
30
+ def _get_expected_attn_shape(input_tensor: Tensor, attention_dim: int) -> Size:
31
+ shape = [x for x in input_tensor.shape[:-1]] + [attention_dim]
32
+ return Size(shape)
33
+
34
+
35
+ def make_attn_tensor_from_input(input_tensor: Tensor, attention_dim: int) -> Tensor:
36
+ return torch.empty(
37
+ _get_expected_attn_shape(input_tensor, attention_dim),
38
+ device=input_tensor.device,
39
+ dtype=input_tensor.dtype,
40
+ requires_grad=input_tensor.requires_grad,
41
+ )
42
+
43
+
44
+ def check_additional_keys(
45
+ input_tensor: Tensor, additional_keys: Optional[Tensor]
46
+ ) -> int:
47
+ if additional_keys is None:
48
+ return 0
49
+
50
+ if additional_keys.dim() != 4:
51
+ raise ValueError(
52
+ "Additional tokens have to be shaped as a rank-4 tensor; "
53
+ f"got {additional_keys.dim()}."
54
+ )
55
+ batch_size, heads, tokens, dim = additional_keys.shape
56
+ expected_batch_size = input_tensor.shape[0]
57
+ expected_heads = input_tensor.shape[1]
58
+ expected_dim = input_tensor.shape[-1]
59
+ if (
60
+ batch_size != expected_batch_size
61
+ or expected_heads != heads
62
+ or expected_dim != dim
63
+ ):
64
+ raise ValueError(
65
+ "Shape mismatch between input tensor and additional tokens; "
66
+ "they must match in batch size, heads, and dim per head. "
67
+ f"Got {input_tensor.shape=}, {additional_keys.shape=}."
68
+ )
69
+ return tokens
70
+
71
+
72
+ def check_additional_values(
73
+ attn_tensor: Tensor,
74
+ additional_values: Optional[Tensor],
75
+ value: Tensor,
76
+ expected_attn_weights: int,
77
+ ) -> int:
78
+ if additional_values is None and attn_tensor.shape[-1] == expected_attn_weights:
79
+ return 0
80
+ if additional_values is None:
81
+ raise ValueError(
82
+ f"Expected {expected_attn_weights} attention weights per token, "
83
+ f"got {attn_tensor.shape[-1]=}."
84
+ )
85
+
86
+ if additional_values.dim() != 4:
87
+ raise ValueError(
88
+ "Additional tokens have to be shaped as a rank-4 tensor; "
89
+ f"got {additional_values.dim()}."
90
+ )
91
+
92
+ if additional_values.shape[-1] != value.shape[-1]:
93
+ raise ValueError(
94
+ "Additional value tokens must match the dimension of the "
95
+ f"rest of the tokens, got {additional_values.shape[-1]=} != "
96
+ f"{value.shape[-1]=}."
97
+ )
98
+
99
+ batch_size, heads, tokens, dim = additional_values.shape
100
+ if tokens + expected_attn_weights != attn_tensor.shape[-1]:
101
+ raise ValueError(
102
+ f"Expected {expected_attn_weights + tokens} attention weights per token, "
103
+ f"got {attn_tensor.shape[-1]=}."
104
+ )
105
+ expected_batch_size = attn_tensor.shape[0]
106
+ expected_heads = attn_tensor.shape[1]
107
+ if batch_size != expected_batch_size or expected_heads != heads:
108
+ raise ValueError(
109
+ "Shape mismatch between attention tensor and additional tokens; "
110
+ "they must match in batch size and heads. "
111
+ f"Got {attn_tensor.shape=}, {additional_values.shape=}."
112
+ )
113
+ return tokens
build/torch210-cu126-aarch64-linux/utils/testing.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #################################################################################################
2
+ # Copyright (c) 2022 - 2026 Ali Hassani.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #
22
+ #################################################################################################
23
+
24
+ import torch
25
+
26
+ from .._environment import _IS_CUDA_AVAILABLE, _RUN_EXTENDED_TESTS, HAS_LIBNATTEN
27
+ from ..backends.flex import _FLEX_COMPILE_SUPPORTED, _FLEX_SUPPORTED
28
+ from ..utils.device import get_device_cc, is_cuda
29
+
30
+
31
+ def skip_if_libnatten_is_not_supported():
32
+ def decorator(f):
33
+ def wrapper(self, *args, **kwargs):
34
+ if not _IS_CUDA_AVAILABLE:
35
+ self.skipTest("CUDA is not available.")
36
+ elif not HAS_LIBNATTEN:
37
+ self.skipTest("Libnatten is not available.")
38
+ else:
39
+ return f(self, *args, **kwargs)
40
+
41
+ return wrapper
42
+
43
+ return decorator
44
+
45
+
46
+ def skip_if_cuda_is_not_supported():
47
+ def decorator(f):
48
+ def wrapper(self, *args, **kwargs):
49
+ if not _IS_CUDA_AVAILABLE:
50
+ self.skipTest("CUDA is not available.")
51
+ else:
52
+ return f(self, *args, **kwargs)
53
+
54
+ return wrapper
55
+
56
+ return decorator
57
+
58
+
59
+ def skip_if_flex_is_not_supported():
60
+ def decorator(f):
61
+ def wrapper(self, *args, **kwargs):
62
+ if not _FLEX_SUPPORTED or get_device_cc() < 70:
63
+ self.skipTest("Flex backend is not supported.")
64
+ else:
65
+ return f(self, *args, **kwargs)
66
+
67
+ return wrapper
68
+
69
+ return decorator
70
+
71
+
72
+ def skip_if_flex_compile_is_not_supported():
73
+ def decorator(f):
74
+ def wrapper(self, *args, **kwargs):
75
+ if not _FLEX_COMPILE_SUPPORTED:
76
+ self.skipTest("Flex (compiled) backend is not supported.")
77
+ else:
78
+ return f(self, *args, **kwargs)
79
+
80
+ return wrapper
81
+
82
+ return decorator
83
+
84
+
85
+ def skip_if_not_running_extended_tests():
86
+ def decorator(f):
87
+ def wrapper(self, *args, **kwargs):
88
+ if not _RUN_EXTENDED_TESTS:
89
+ self.skipTest("Skipping extended test cases.")
90
+ else:
91
+ return f(self, *args, **kwargs)
92
+
93
+ return wrapper
94
+
95
+ return decorator
96
+
97
+
98
+ def skip_if_hopper_kernels_not_supported():
99
+ def decorator(f):
100
+ def wrapper(self, *args, **kwargs):
101
+ if get_device_cc() != 90:
102
+ self.skipTest("Hopper kernels are only supported on SM90.")
103
+ else:
104
+ return f(self, *args, **kwargs)
105
+
106
+ return wrapper
107
+
108
+ return decorator
109
+
110
+
111
+ def skip_if_blackwell_kernels_not_supported():
112
+ def decorator(f):
113
+ def wrapper(self, *args, **kwargs):
114
+ if get_device_cc() not in [100, 103]:
115
+ self.skipTest(
116
+ "Blackwell kernels are only supported on SM100 and SM103."
117
+ )
118
+ else:
119
+ return f(self, *args, **kwargs)
120
+
121
+ return wrapper
122
+
123
+ return decorator
124
+
125
+
126
+ def supports_float16(device: torch.device) -> bool:
127
+ if is_cuda(device):
128
+ device_cc = get_device_cc(device)
129
+
130
+ if device_cc < 50:
131
+ return False
132
+
133
+ return True
134
+
135
+ # TODO:
136
+ return True
137
+
138
+
139
+ def supports_bfloat16(device: torch.device) -> bool:
140
+ if is_cuda(device):
141
+ device_cc = get_device_cc(device)
142
+
143
+ if device_cc < 80:
144
+ return False
145
+
146
+ return True
147
+
148
+ # TODO:
149
+ return False