kernels-bot commited on
Commit
e2117e8
·
verified ·
1 Parent(s): 586cdf4

Uploaded using `kernel-builder`.

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. build/torch211-cxx11-cu126-x86_64-linux/__init__.py +178 -0
  2. build/torch211-cxx11-cu126-x86_64-linux/_environment.py +59 -0
  3. build/torch211-cxx11-cu126-x86_64-linux/_libnatten/__init__.py +109 -0
  4. build/torch211-cxx11-cu126-x86_64-linux/_libnatten/torch_wrappers.py +1006 -0
  5. build/torch211-cxx11-cu126-x86_64-linux/_natten_cuda_3641131.abi3.so +3 -0
  6. build/torch211-cxx11-cu126-x86_64-linux/_ops.py +9 -0
  7. build/torch211-cxx11-cu126-x86_64-linux/_types.py +85 -0
  8. build/torch211-cxx11-cu126-x86_64-linux/attn_merge.py +292 -0
  9. build/torch211-cxx11-cu126-x86_64-linux/backends/__init__.py +254 -0
  10. build/torch211-cxx11-cu126-x86_64-linux/backends/blackwell_fmha.py +254 -0
  11. build/torch211-cxx11-cu126-x86_64-linux/backends/blackwell_fna.py +500 -0
  12. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/__init__.py +584 -0
  13. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/checks.py +750 -0
  14. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/__init__.py +422 -0
  15. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/backward_knobs.py +228 -0
  16. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_backward_128x128.py +304 -0
  17. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_backward_128x64.py +223 -0
  18. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_backward_64x64.py +168 -0
  19. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_forward_32x128.py +90 -0
  20. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_forward_64x128.py +82 -0
  21. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_forward_64x64.py +63 -0
  22. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass_blackwell/__init__.py +391 -0
  23. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass_hopper/__init__.py +522 -0
  24. build/torch211-cxx11-cu126-x86_64-linux/backends/configs/flex/__init__.py +210 -0
  25. build/torch211-cxx11-cu126-x86_64-linux/backends/flex.py +799 -0
  26. build/torch211-cxx11-cu126-x86_64-linux/backends/fmha.py +283 -0
  27. build/torch211-cxx11-cu126-x86_64-linux/backends/fna.py +417 -0
  28. build/torch211-cxx11-cu126-x86_64-linux/backends/hopper_fmha.py +261 -0
  29. build/torch211-cxx11-cu126-x86_64-linux/backends/hopper_fna.py +512 -0
  30. build/torch211-cxx11-cu126-x86_64-linux/backends/reference.py +343 -0
  31. build/torch211-cxx11-cu126-x86_64-linux/context.py +231 -0
  32. build/torch211-cxx11-cu126-x86_64-linux/functional.py +1151 -0
  33. build/torch211-cxx11-cu126-x86_64-linux/metadata.json +81 -0
  34. build/torch211-cxx11-cu126-x86_64-linux/metadata.json.sigstore +1 -0
  35. build/torch211-cxx11-cu126-x86_64-linux/modules.py +449 -0
  36. build/torch211-cxx11-cu126-x86_64-linux/natten/__init__.py +26 -0
  37. build/torch211-cxx11-cu126-x86_64-linux/token_permute/__init__.py +32 -0
  38. build/torch211-cxx11-cu126-x86_64-linux/token_permute/cutlass_impl.py +286 -0
  39. build/torch211-cxx11-cu126-x86_64-linux/token_permute/frontend.py +137 -0
  40. build/torch211-cxx11-cu126-x86_64-linux/token_permute/torch_impl.py +368 -0
  41. build/torch211-cxx11-cu126-x86_64-linux/utils/__init__.py +22 -0
  42. build/torch211-cxx11-cu126-x86_64-linux/utils/checks.py +726 -0
  43. build/torch211-cxx11-cu126-x86_64-linux/utils/device.py +50 -0
  44. build/torch211-cxx11-cu126-x86_64-linux/utils/dtype.py +36 -0
  45. build/torch211-cxx11-cu126-x86_64-linux/utils/environment.py +79 -0
  46. build/torch211-cxx11-cu126-x86_64-linux/utils/log.py +134 -0
  47. build/torch211-cxx11-cu126-x86_64-linux/utils/tensor.py +113 -0
  48. build/torch211-cxx11-cu126-x86_64-linux/utils/testing.py +149 -0
  49. build/torch211-cxx11-cu126-x86_64-linux/utils/tuples.py +51 -0
  50. build/torch211-cxx11-cu126-x86_64-linux/utils/varlen.py +135 -0
build/torch211-cxx11-cu126-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .version import __version__
65
+
66
+ # kernel-builder port: the package contents are installed flat into the build
67
+ # variant directory, so a module literally named `types` would shadow the
68
+ # standard library `types` module whenever that directory is on PYTHONPATH
69
+ # (e.g. kernel-builder test shells and CI runners), breaking interpreter
70
+ # startup. The module therefore lives in `_types`; alias it here so
71
+ # `natten.types` keeps working like upstream.
72
+ import sys as _sys
73
+
74
+ from . import _types as types
75
+
76
+ _sys.modules[__name__ + ".types"] = types
77
+
78
+ # kernel-builder's compat shim (`natten/__init__.py` inside the build variant
79
+ # directory) executes this package under a path-derived module name and copies
80
+ # our globals into a `natten` module whose __path__ contains no submodules.
81
+ # Attribute access (`natten.functional`) works there, but real submodule
82
+ # imports (`from natten.functional import na2d`, `import natten.utils.testing`)
83
+ # would either fail with ModuleNotFoundError or — when resolvable through a
84
+ # parent package's __path__ — re-execute the module under a second name,
85
+ # duplicating module state. Bridge this with a meta-path finder that resolves
86
+ # any `natten.*` import to our already-loaded module objects. Only installed
87
+ # when `natten` in sys.modules is *our* compat shim, so a real `natten`
88
+ # distribution in the same environment is never hijacked.
89
+ if __name__ != "natten":
90
+ from pathlib import Path as _Path
91
+
92
+ _compat = _sys.modules.get("natten")
93
+ _is_our_compat = (
94
+ _compat is not None
95
+ and getattr(_compat, "__file__", None) is not None
96
+ and _Path(_compat.__file__).resolve()
97
+ == _Path(__file__).resolve().parent / "natten" / "__init__.py"
98
+ )
99
+
100
+ if _is_our_compat:
101
+ import importlib as _importlib
102
+ from importlib.abc import Loader as _Loader
103
+ from importlib.abc import MetaPathFinder as _MetaPathFinder
104
+ from importlib.util import spec_from_loader as _spec_from_loader
105
+
106
+ _real_root = __name__
107
+
108
+ class _NattenAliasLoader(_Loader):
109
+ def __init__(self, module):
110
+ self._module = module
111
+ self._spec = getattr(module, "__spec__", None)
112
+ self._loader = getattr(module, "__loader__", None)
113
+
114
+ def create_module(self, spec):
115
+ return self._module
116
+
117
+ def exec_module(self, module):
118
+ # The import machinery stamped the alias spec onto the real
119
+ # module in module_from_spec; restore its original identity.
120
+ module.__spec__ = self._spec
121
+ module.__loader__ = self._loader
122
+
123
+ class _NattenAliasFinder(_MetaPathFinder):
124
+ def find_spec(self, fullname, path=None, target=None):
125
+ if not fullname.startswith("natten."):
126
+ return None
127
+ real_name = _real_root + fullname[len("natten") :]
128
+ try:
129
+ module = _importlib.import_module(real_name)
130
+ except ImportError:
131
+ return None
132
+ return _spec_from_loader(fullname, _NattenAliasLoader(module))
133
+
134
+ # Must precede PathFinder, which would otherwise re-execute
135
+ # submodules reachable through a real parent package's __path__.
136
+ _sys.meta_path.insert(0, _NattenAliasFinder())
137
+
138
+ __all__ = [
139
+ "__version__",
140
+ "NeighborhoodAttention1D",
141
+ "NeighborhoodAttention2D",
142
+ "NeighborhoodAttention3D",
143
+ "are_deterministic_algorithms_enabled",
144
+ "use_deterministic_algorithms",
145
+ "use_kv_parallelism_in_fused_na",
146
+ "is_kv_parallelism_in_fused_na_enabled",
147
+ "set_memory_usage_preference",
148
+ "get_memory_usage_preference",
149
+ "is_memory_usage_default",
150
+ "is_memory_usage_strict",
151
+ "is_memory_usage_unrestricted",
152
+ "is_flex_compile_allowed",
153
+ "is_flex_compile_backprop_allowed",
154
+ "allow_flex_compile",
155
+ "allow_flex_compile_backprop",
156
+ "disable_flex_compile",
157
+ "disable_flex_compile_backprop",
158
+ "get_bwd_configs_for_cutlass_fmha",
159
+ "get_bwd_configs_for_cutlass_fna",
160
+ "get_configs_for_cutlass_fmha",
161
+ "get_configs_for_cutlass_fna",
162
+ "get_configs_for_cutlass_hopper_fmha",
163
+ "get_bwd_configs_for_cutlass_hopper_fmha",
164
+ "get_configs_for_cutlass_hopper_fna",
165
+ "get_bwd_configs_for_cutlass_hopper_fna",
166
+ "get_bwd_configs_for_cutlass_blackwell_fmha",
167
+ "get_bwd_configs_for_cutlass_blackwell_fna",
168
+ "get_configs_for_cutlass_blackwell_fmha",
169
+ "get_configs_for_cutlass_blackwell_fna",
170
+ "get_configs_for_flex_fmha",
171
+ "get_configs_for_flex_fna",
172
+ "HAS_LIBNATTEN",
173
+ "na1d",
174
+ "na2d",
175
+ "na3d",
176
+ "attention",
177
+ "merge_attentions",
178
+ ]
build/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-linux/_natten_cuda_3641131.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a38a8d19b1065ea660148531865732a9ec754be0efb551fcaeef77ce8d71d890
3
+ size 103606392
build/torch211-cxx11-cu126-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _natten_cuda_3641131
3
+ ops = torch.ops._natten_cuda_3641131
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_natten_cuda_3641131::{op_name}"
build/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "natten",
3
+ "id": "_natten_cuda_3641131",
4
+ "version": 1,
5
+ "license": "MIT",
6
+ "upstream": "https://github.com/SHI-Labs/NATTEN",
7
+ "python-depends": [],
8
+ "backend": {
9
+ "type": "cuda",
10
+ "archs": [
11
+ "8.0",
12
+ "9.0",
13
+ "9.0a"
14
+ ]
15
+ },
16
+ "digest": {
17
+ "algorithm": "sha256",
18
+ "files": {
19
+ "__init__.py": "gum9e68BeqHOe1E0Pa6VqIUe18hV1/LH0hbn3J4ytj8=",
20
+ "_environment.py": "9P189XTk2YMyWuRNI5eCI3UvHRqVR8Zh2hgJsHP7MYI=",
21
+ "_libnatten/__init__.py": "yMW+2Kd5Nxs716jaNq15IleYQ86HR5u7SMEQDqVVunI=",
22
+ "_libnatten/torch_wrappers.py": "v7vkrytkghNz+cJI7l2PXGqWactsSM1ImwCsHfyQ3iA=",
23
+ "_natten_cuda_3641131.abi3.so": "o4qNGbEGXqZgFIUxhlcyqex1S+DvtVH8ru93zo1x2JA=",
24
+ "_ops.py": "KdQwUQbokH7RjoPK3Y6hQVR6zOhKfYdEPoQ1qvrIRms=",
25
+ "_types.py": "OzK1SfxBXbR01LDSzFOpvm5qUCLs3qOc021I31Eibm8=",
26
+ "attn_merge.py": "KDItp+YcD/18PEXqCOOxSBeLYYt50LhYZnyluE5iT7A=",
27
+ "backends/__init__.py": "r8Lco2ESXb2yqPccmIu6kyUD8niprXF7J0WA7urlAbE=",
28
+ "backends/blackwell_fmha.py": "hDQuJ27vACJ46dj4QKaa7CWB2Sy7650ZtHlJCgQQ3Bk=",
29
+ "backends/blackwell_fna.py": "YA6YqgDnTJ/4F5FCe7uf7H0qOMhF4k4UTkKVtYtfYDU=",
30
+ "backends/configs/__init__.py": "NG3ArX78+S0/E3i2R0LTjmr30ZY71qf9Xv3396Wfw34=",
31
+ "backends/configs/checks.py": "d7qCFUqvF9fl0nuRiTr6eDXyaAf0/ksyrkqI8Z2VG+E=",
32
+ "backends/configs/cutlass/__init__.py": "H+6PW4E9/I2UtU0CUB+8rEvSjpT6hqVHEjO5JQD0cao=",
33
+ "backends/configs/cutlass/backward_knobs.py": "s6CakBU+ApSjFfLGLyC42kpmXZBc2Si3Ud+4SKIZ7zM=",
34
+ "backends/configs/cutlass/fna_backward_128x128.py": "ZcUo6u0hDGUXasU52U98XV+KUF475QyP8I5O8T042yM=",
35
+ "backends/configs/cutlass/fna_backward_128x64.py": "3gXgi7PqQpP8A7ClS8GPA1eXwOiLCgPEeOL4oJxYWXY=",
36
+ "backends/configs/cutlass/fna_backward_64x64.py": "i5iRd+TrdtDS8S48cVj6/D7+951L6lPLNuqkLIA1k/c=",
37
+ "backends/configs/cutlass/fna_forward_32x128.py": "1acDc2mpRTKBC3opzjZEEr0R9lPmQ3wLPSOIlJlM5aM=",
38
+ "backends/configs/cutlass/fna_forward_64x128.py": "8NfkwqYqJ8FfdFeXZU1e6fUF8F6dRUZRsJ+c9aVDZAI=",
39
+ "backends/configs/cutlass/fna_forward_64x64.py": "vu1gBAvQLQuO+iJdz4GdidayIywG13b83Lorw/I43ac=",
40
+ "backends/configs/cutlass_blackwell/__init__.py": "RtGGNcP4bvzu1JozMLd16mJtDncxRIAa+W7Lg7I2KHs=",
41
+ "backends/configs/cutlass_hopper/__init__.py": "DjEcfThaXh2JGrA5EW72mmRdh6Kwk/oOmaOkZm9PkNU=",
42
+ "backends/configs/flex/__init__.py": "HFK8hxYrU85UyEZPO6c66DoLvfJcji6jmTNFUGzgGDs=",
43
+ "backends/flex.py": "6xSMb2kuezRaeOcBN4UWY8F6kJG7F6DoZdSv08vvmMg=",
44
+ "backends/fmha.py": "sA52KmY/Dj5Qm5E2eX9IWNVMke0Vn94FAU+tYe1zPLc=",
45
+ "backends/fna.py": "xUzyoiHkFwDyf+Cv0VOhDt+5X5F8X6ckVAo5cJZXGsc=",
46
+ "backends/hopper_fmha.py": "+xWkbfNibNwTtU8d9TmUUedITYnnUQwykYCgBcoyTkE=",
47
+ "backends/hopper_fna.py": "fKfBfO0gNJtdJeghLDa4tQpCdIeOvlkDLf5PaD0P2P8=",
48
+ "backends/reference.py": "SG0s45Zme6O8gSjPAxw3ib9T0pqvDAxCmbGn/DQ/0tc=",
49
+ "context.py": "IzU+TvVnBZ41jc93BjQ7+/Q3Eg+8fDNSK4YCbTc2OU8=",
50
+ "functional.py": "zQq6Et4t+ywceLDcrQmiXYh2xpkRqSk1vBdHE7QsCGE=",
51
+ "modules.py": "1C3KPU4tOHIuRT+rOd6XVqPt98Ho/oehFEKOpwv445Q=",
52
+ "natten/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY=",
53
+ "token_permute/__init__.py": "NPxL8EMHxAxjHMcHTMFznH0oNQjGUr2divAbtGaWNhs=",
54
+ "token_permute/cutlass_impl.py": "nCXz+b8TJAbU/j0RiB0PMO94nqFZIvNxO9tKhLC/vDs=",
55
+ "token_permute/frontend.py": "mVlwIP2pjFEq/KHEiT9t9qE3Zl+pVpeeVhceclOIVOU=",
56
+ "token_permute/torch_impl.py": "7PKSTynMOiaEaGRxEoyWz6SVrC0Lcx227Cvqdc4kB9E=",
57
+ "utils/__init__.py": "+RppytrGJc5JC8/fPz8XWYdCwAT8JOxpmLDQTlpO6gI=",
58
+ "utils/checks.py": "FP+WlZSIT7sjyzKOjnytCuFbqN2PejuKJ6SyApkZPn0=",
59
+ "utils/device.py": "3UJXsbft+HlnP2+nAnIoE1Hlq5yq9LhLSNFx9I5QHaw=",
60
+ "utils/dtype.py": "p6d+m2q4qj480Kcqg/b7WeV8+n4oRr5vi+dMb9vsxE4=",
61
+ "utils/environment.py": "GyeBUYS5y09z0h50VEL09PWCutXI57kWGxnzX5GUvVM=",
62
+ "utils/log.py": "pvlKMKeLzqrNvjHgygV985sT0TjtnbYcjluTDJ4y2Mc=",
63
+ "utils/tensor.py": "PhXmULtQny1AVmZAMByMKV7xNm/gf6M6VQa3gkjQrTQ=",
64
+ "utils/testing.py": "nl8DQHWiR6AWmecX2mHpkZxZBomc9QRHXc1MqvF9AG8=",
65
+ "utils/tuples.py": "/LrawWeD5LDugY/SD92NNcr9M3BNfs/F+8lSYs+130Y=",
66
+ "utils/varlen.py": "ON5q6wFAyBOY0PscRYht7LdGrwr5p96TcojdrprVaHI=",
67
+ "version.py": "FVIe4O+OfUJ2NXT3Ebezok+DAPBcOkgEU6tlHu0PPg8="
68
+ }
69
+ },
70
+ "provenance": {
71
+ "kernel-builder": {
72
+ "version": "0.17.0-dev0",
73
+ "sha": "ce92bd77c807ce3a2f4b85d8bab69caf027b51d0",
74
+ "dirty": false
75
+ },
76
+ "kernel": {
77
+ "sha": "3641131fa0a08b8174b3035cb79e5cf27bd5baad",
78
+ "dirty": false
79
+ }
80
+ }
81
+ }
build/torch211-cxx11-cu126-x86_64-linux/metadata.json.sigstore ADDED
@@ -0,0 +1 @@
 
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHSjCCBtGgAwIBAgIUENWDkPegFVvZRJM6q8qMhUuMwLYwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwNzI5MDkyMTM0WhcNMjYwNzI5MDkzMTM0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdcq8BxBrq06UzALZ5aKGyV1ze0u7pb0jLRBda6h/713y6d9xSP9cMdnfrnlQjnbleRt+Ok7NLTIgUAy7rXutS6OCBfAwggXsMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU64vuL4RzrTPjhn01iVDcqNT9rHwwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wawYDVR0RAQH/BGEwX4ZdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoMjBhNjEzYzEwZDliMWM5Y2I0M2E1MWU4NTBlOWZhMDQ2OWE5NzE5OTATBgorBgEEAYO/MAEEBAVCdWlsZDArBgorBgEEAYO/MAEFBB1odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMG0GCisGAQQBg78wAQkEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoMjBhNjEzYzEwZDliMWM5Y2I0M2E1MWU4NTBlOWZhMDQ2OWE5NzE5OTAbBgorBgEEAYO/MAELBA0MC3NlbGYtaG9zdGVkMEAGCisGAQQBg78wAQwEMgwwaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MDgGCisGAQQBg78wAQ0EKgwoMjBhNjEzYzEwZDliMWM5Y2I0M2E1MWU4NTBlOWZhMDQ2OWE5NzE5OTAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAaBgorBgEEAYO/MAEPBAwMCjEwNzE0NzU1MjkwLgYKKwYBBAGDvzABEAQgDB5odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2UwGAYKKwYBBAGDvzABEQQKDAgyNTcyMDc0MzBtBgorBgEEAYO/MAESBF8MXWh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS8uZ2l0aHViL3dvcmtmbG93cy9idWlsZC55YW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAETBCoMKDIwYTYxM2MxMGQ5YjFjOWNiNDNhNTFlODUwZTlmYTA0NjlhOTcxOTkwIQYKKwYBBAGDvzABFAQTDBF3b3JrZmxvd19kaXNwYXRjaDBkBgorBgEEAYO/MAEVBFYMVGh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS9hY3Rpb25zL3J1bnMvMzA0MTgyNTI4NjkvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzBGBgorBgEEAYO/MAEYBDgMNnJlcG86aHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHk6cmVmOnJlZnMvaGVhZHMvbWFpbjCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABn60t5mgAAAQDAEcwRQIgLUvkKk7oLX3B1UWy0X+Ch8UaFJEDl2EeSAEXM5+1a1oCIQDOWS4Jgt9qjKDL1aAqGDgONFF3WMIm/o/GJtWVFQ+snjAKBggqhkjOPQQDAwNnADBkAjBTqD6LtwE6ptoxeZRDivuw5uK1fsfPjFpmohRPD/euDT12EwjBMk7iRplRIGBseh8CMHu6bQRud0BCGIl7QJ55GAmRZmXYTQ8Y5VZTg7IyK7rPG4I9aaMiU9rzv7skJMxh/w=="}, "tlogEntries":[{"logIndex":"2280149228", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"hashedrekord", "version":"0.0.1"}, "integratedTime":"1785316894", "inclusionPromise":{"signedEntryTimestamp":"MEUCIG1ry/M5a8Q+Zyp4icfw0vKvjTOu5i8SQyztovlzxmVdAiEAwHju/lVvwJ3JkEkdWiTpuOkt2r0lPghXA1VFjFlIw28="}, "inclusionProof":{"logIndex":"2158244966", "rootHash":"bP7TpLzXp35jXCgw2q1PbA0L9n++o7lG9fyD6jOXIws=", "treeSize":"2158244969", "hashes":["VleMKHTzOxE/Vx4/PHWMo+XAmrpe5Blq6nTIjr0HVTA=", "cg+uSPas8GSp/Xpk1dyZMoM2AY4AWFBpNQ/+mUY91k0=", "swwiNtY8cPozMCjx7lEmyW50d36M1Kz23Nu5dDd+sZQ=", "qaYLnSshaNGiEaRlCETE/NaWT6ZItJQ4IOINv1ad63E=", "8CZ1EqhgyxGzJym/Y5ujtMUP4B7JUw/hSYjhV6H7YHM=", "sS+fl5SKwsQjQE6HrC426ByW+1/o21xz4dSeBr22cqY=", "DwNueYXiyuVjob7c8CRNRN57ioovkJTOeOeOQIi6/pI=", "bXYdybsl/2BK7Yled3FQJEE3Hk0TeGcWnXUxOkVZv8w=", "xrwx0yHkH5ZmCgeWs/jLZZ8RcejqpeuUeAWNJkAYrHk=", "8MN6j2GHUjPnNtIwpas9l/XjdwE0/KPUhFDlrVkngko=", "b+xUZfuENQxvSOJxzNvYvRG8eVphfszPpZmuf4/cQ6c=", "OVsvZCKnWA+498QUIaQCtitUT6huDbC7SmhH1l8MxXI=", "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n2158244969\nbP7TpLzXp35jXCgw2q1PbA0L9n++o7lG9fyD6jOXIws=\n\n— rekor.sigstore.dev wNI9ajBGAiEAnh/bQrjUVU/DKYSDXrcfenN3Nvhk+jyyz6tJiqR6VCECIQC4O/QWRaJOz5zc14uWgveqlFIBfe0saoa77r7MayGBlA==\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI3ZWNhN2Y5Nzg4MDhjNzJiNDAxOGJkN2VhYWMzYzVjNDAxYWRlNDZjMTdmYjEyYWU5NzAyNjM4NWE3MmExYzQ2In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRisrKy9DZzVHMTRqUGFVRGRIbUtDb1lZVW8rZVdhc3JkYkdodDczNVRnNUFpQmpqZ2UrenlDUGRjeXFLLzMrZS92bzBqVFpPQlE2ODNBZzhWVGhtdWZ2bVE9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaFRha05EUW5SSFowRjNTVUpCWjBsVlJVNVhSR3RRWldkR1ZuWmFVa3BOTm5FNGNVMW9WWFZOZDB4WmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDU2U1RWTlJHdDVUVlJOTUZkb1kwNU5hbGwzVG5wSk5VMUVhM3BOVkUwd1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZrWTNFNFFuaENjbkV3TmxWNlFVeGFOV0ZMUjNsV01YcGxNSFUzY0dJd2FreFNRbVFLWVRab0x6Y3hNM2syWkRsNFUxQTVZMDFrYm1aeWJteFJhbTVpYkdWU2RDdFBhemRPVEZSSloxVkJlVGR5V0hWMFV6WlBRMEptUVhkbloxaHpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlUyTkhaMUNrdzBVbnB5VkZCcWFHNHdNV2xXUkdOeFRsUTVja2gzZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJGM1dVUldVakJTUVZGSUwwSkhSWGRZTkZwa1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKb01Wb3laSEJpYldSdFdWZE9iQXBNTW5Sc1kyMDFiR0pJVFhSWk1qbDBZbGhXZFdGWVVqVk1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1rb3hZVmQ0YTB4dWJHaGlWM2hCQ21OdFZtMWplVGx2V2xkR2EyTjVPWFJaVjJ4MVRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpBS1lWYzVkV041Tlc1aFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NIZFpTMHQzV1VKQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWN3cGlNMlJtV2tkc2VtTkhSakJaTW1kM1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOU5ha0pvVG1wRmVsbDZSWGRhUkd4cFRWZE5OVmt5U1RCTk1rVXhDazFYVlRST1ZFSnNUMWRhYUUxRVVUSlBWMFUxVG5wRk5VOVVRVlJDWjI5eVFtZEZSVUZaVHk5TlFVVkZRa0ZXUTJSWGJITmFSRUZ5UW1kdmNrSm5SVVVLUVZsUEwwMUJSVVpDUWpGdlpGZGtibUZYTlc1YWJVWnFXbE01Y2xwWVNuVmFWM2g2VEZkT2RtSlhNVEZpYld3d1pWUkJaRUpuYjNKQ1owVkZRVmxQTHdwTlFVVkhRa0U1ZVZwWFducE1NbWhzV1ZkU2Vrd3lNV2hoVnpSM1QzZFpTMHQzV1VKQ1FVZEVkbnBCUWtOQlVYUkVRM1J2WkVoU2QyTjZiM1pNTTFKMkNtRXlWblZNYlVacVpFZHNkbUp1VFhWYU1td3dZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVY3dSME5wYzBkQlVWRkNaemM0ZDBGUmEwVUtXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5YURGYU1tUndZbTFrYlZsWFRteE1NblJzWTIwMWJHSklUWFJaTWpsMFlsaFdkUXBoV0ZJMVRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2VERKS01XRlhlR3RNYm14b1lsZDRRV050Vm0xamVUbHZXbGRHYTJONU9YUlpWMngxQ2sxRVowZERhWE5IUVZGUlFtYzNPSGRCVVc5RlMyZDNiMDFxUW1oT2FrVjZXWHBGZDFwRWJHbE5WMDAxV1RKSk1FMHlSVEZOVjFVMFRsUkNiRTlYV21nS1RVUlJNazlYUlRWT2VrVTFUMVJCWWtKbmIzSkNaMFZGUVZsUEwwMUJSVXhDUVRCTlF6Tk9iR0pIV1hSaFJ6bDZaRWRXYTAxRlFVZERhWE5IUVZGUlFncG5OemgzUVZGM1JVMW5kM2RoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlUxRVowZERhWE5IUVZGUlFtYzNPSGRCVVRCRlMyZDNiMDFxUW1oT2FrVjZXWHBGZDFwRWJHbE5WMDAxV1RKSk1FMHlSVEVLVFZkVk5FNVVRbXhQVjFwb1RVUlJNazlYUlRWT2VrVTFUMVJCWmtKbmIzSkNaMFZGUVZsUEwwMUJSVTlDUWtWTlJETktiRnB1VFhaaFIxWm9Xa2hOZGdwaVYwWndZbXBCWVVKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhkTlEycEZkMDU2UlRCT2VsVXhUV3ByZDB4bldVdExkMWxDUWtGSFJIWjZRVUpGUVZGbkNrUkNOVzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWM1IwRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVc0tSRUZuZVU1VVkzbE5SR013VFhwQ2RFSm5iM0pDWjBWRlFWbFBMMDFCUlZOQ1JqaE5XRmRvTUdSSVFucFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVid3BrVjJSdVlWYzFibHB0Um1wYVV6bHlXbGhLZFZwWGVIcE1WMDUyWWxjeE1XSnRiREJsVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxwQ21SWGJITmFRelUxV1ZjeGMxRklTbXhhYmsxMllVZFdhRnBJVFhaaVYwWndZbXBCTkVKbmIzSkNaMFZGUVZsUEwwMUJSVlJDUTI5TlMwUkpkMWxVV1hnS1RUSk5lRTFIVVRWWmFrWnFUMWRPYVU1RVRtaE9WRVpzVDBSVmQxcFViRzFaVkVFd1RtcHNhRTlVWTNoUFZHdDNTVkZaUzB0M1dVSkNRVWRFZG5wQlFncEdRVkZVUkVKR00ySXpTbkphYlhoMlpERTVhMkZZVG5kWldGSnFZVVJDYTBKbmIzSkNaMFZGUVZsUEwwMUJSVlpDUmxsTlZrZG9NR1JJUW5wUGFUaDJDbG95YkRCaFNGWnBURzFPZG1KVE9XOWtWMlJ1WVZjMWJscHRSbXBhVXpseVdsaEtkVnBYZUhwTVYwNTJZbGN4TVdKdGJEQmxVemxvV1ROU2NHSXlOWG9LVEROS01XSnVUWFpOZWtFd1RWUm5lVTVVU1RST2FtdDJXVmhTTUZwWE1YZGtTRTEyVFZSQlYwSm5iM0pDWjBWRlFWbFBMMDFCUlZkQ1FXZE5RbTVDTVFwWmJYaHdXWHBDUjBKbmIzSkNaMFZGUVZsUEwwMUJSVmxDUkdkTlRtNUtiR05IT0RaaFNGWnVXakpzZFZveVdtaFpNbFYyWVRKV2VXSnRWbk5qZVRGcUNtSXlNWFJrVnpWd1pFaHJObU50Vm0xUGJrcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRMEpwWjFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT0VKSWIwRUtaVUZDTWtGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtNDJNSFExYldkQlFVRlJSQXBCUldOM1VsRkpaMHhWZG10TGF6ZHZURmd6UWpGVlYza3dXQ3REYURoVllVWktSVVJzTWtWbFUwRkZXRTAxS3pGaE1XOURTVkZFVDFkVE5FcG5kRGx4Q21wTFJFd3hZVUZ4UjBSblQwNUdSak5YVFVsdEwyOHZSMHAwVjFaR1VTdHpibXBCUzBKblozRm9hMnBQVUZGUlJFRjNUbTVCUkVKclFXcENWSEZFTmt3S2RIZEZObkIwYjNobFdsSkVhWFoxZHpWMVN6Rm1jMlpRYWtad2JXOW9VbEJFTDJWMVJGUXhNa1YzYWtKTmF6ZHBVbkJzVWtsSFFuTmxhRGhEVFVoMU5ncGlVVkoxWkRCQ1EwZEpiRGRSU2pVMVIwRnRVbHB0V0ZsVVVUaFpOVlphVkdjM1NYbExOM0pRUnpSSk9XRmhUV2xWT1hKNmRqZHphMHBOZUdndmR6MDlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ=="}], "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyjADAgEAMIICwQYJKoZIhvcNAQcCoIICsjCCAq4CAQMxDTALBglghkgBZQMEAgEwgbgGCyqGSIb3DQEJEAEEoIGoBIGlMIGiAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgTRh2Eax7lxF4e3pxV97waQT6rugjVfOy5zU6VruHlNoCFQCzP0L3DFHjsreAIT7slbSE22uoJhgPMjAyNjA3MjkwOTIxMzRaMAMCAQGgMqQwMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhoAAxggHbMIIB1wIBATBRMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAsGCWCGSAFlAwQCAaCB/DAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDcyOTA5MjEzNFowLwYJKoZIhvcNAQkEMSIEIJvXfubflBPfSzp3U8kYq+xQV4Aq0cKj0OZVocVa4XgrMIGOBgsqhkiG9w0BCRACLzF/MH0wezB5BCCF+Se8B6tiysO0Q1bBDvyBssaIP9p6uebYcNnROs0FtzBVMD2kOzA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDAKBggqhkjOPQQDAgRnMGUCMQDyIRWFENKc/oHwIdQh1+VD+l7q51BeMBuIvHCOkw9M/7GDIDs0BI1Eb2NrIhWdEvwCMCIC9LZ/FUVL0d3vEMgfWM1BmJ9SgYP4s4HuqRo2C4qgeWH81djcTRO3BzCyS3J7yA=="}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"fsp/l4gIxytAGL1+qsPFxAGt5GwX+xKulwJjhacqHEY="}, "signature":"MEQCIF+++/Cg5G14jPaUDdHmKCoYYUo+eWasrdbGht735Tg5AiBjjge+zyCPdcyqK/3+e/vo0jTZOBQ683Ag8VThmufvmQ=="}}
build/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-linux/natten/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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/torch211-cxx11-cu126-x86_64-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
build/torch211-cxx11-cu126-x86_64-linux/utils/tuples.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .._types import CausalArgType, DimensionType
25
+
26
+
27
+ def ceil_div_int(x: int, y: int) -> int:
28
+ return (x + y - 1) // y
29
+
30
+
31
+ def ceil_div_tuple(X: tuple, Y: tuple) -> tuple:
32
+ assert len(X) == len(Y)
33
+ return tuple(ceil_div_int(x, y) for x, y in zip(X, Y))
34
+
35
+
36
+ def mul_tuple(X: tuple, Y: tuple) -> tuple:
37
+ assert len(X) == len(Y)
38
+ return tuple(x * y for x, y in zip(X, Y))
39
+
40
+
41
+ def sub_tuple(X: tuple, Y: tuple) -> tuple:
42
+ assert len(X) == len(Y)
43
+ return tuple(x - y for x, y in zip(X, Y))
44
+
45
+
46
+ def create_dim_from_int(na_dim: int, value: int) -> DimensionType:
47
+ return tuple(value for _ in range(na_dim)) # type: ignore
48
+
49
+
50
+ def create_causal_arg_from_bool(na_dim: int, value: bool) -> CausalArgType:
51
+ return tuple(value for _ in range(na_dim)) # type: ignore
build/torch211-cxx11-cu126-x86_64-linux/utils/varlen.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, Tuple, Union
25
+
26
+ import torch # noqa: F401
27
+ from torch import Tensor
28
+
29
+ from .._types import NoneType
30
+ from ..utils.environment import is_torch_compiling
31
+
32
+
33
+ def generate_varlen_parameters(
34
+ query: Tensor,
35
+ key: Tensor,
36
+ value: Tensor,
37
+ seqlens_Q: Optional[Tensor] = None,
38
+ seqlens_KV: Optional[Tensor] = None,
39
+ ) -> Union[
40
+ Tuple[NoneType, NoneType, int, int],
41
+ Tuple[Tensor, Tensor, int, int],
42
+ ]:
43
+ # NOTE: max_seqlen_{Q,KV} require a device-host sync, since they're expected to be ints (with
44
+ # which we launch the varlen kernel) and not device tensors.
45
+ # .item() introduces control flow and breaks the graph.
46
+ # It is also inefficient to repeat this per-op, and mostly there for convenience.
47
+ # generate_varlen_parameters should ideally always be called by the user ahead of model
48
+ # forward / backward.
49
+ if is_torch_compiling():
50
+ raise RuntimeError(
51
+ "Running 'generate_varlen_parameters' in a torch-compiled region is disallowed as it "
52
+ "results in graph breaks. Please consider calling ahead of time and pass "
53
+ "'cumulative_seqlen_{Q,KV}' and 'max_seqlen_{Q,KV}' instead of 'seqlens_{Q,KV}' to "
54
+ "'attention'. "
55
+ )
56
+
57
+ if query.shape[0] != key.shape[0] or query.shape[0] != value.shape[0]:
58
+ raise ValueError(
59
+ "Q, K, and V must match in batch size, got "
60
+ f"{query.shape[0]=}, {key.shape[0]=}, {value.shape[0]=}."
61
+ )
62
+
63
+ if (seqlens_Q is None) ^ (seqlens_KV is None):
64
+ raise ValueError(
65
+ "Variable length Attention requires both of seqlens_Q and seqlens_KV to be set, got "
66
+ f"{seqlens_Q=}, {seqlens_KV=}."
67
+ )
68
+
69
+ if seqlens_Q is None and seqlens_KV is None:
70
+ # Not varlen
71
+ return None, None, 0, 0
72
+
73
+ assert seqlens_Q is not None
74
+ assert seqlens_KV is not None
75
+
76
+ if not isinstance(seqlens_Q, Tensor) or not isinstance(seqlens_KV, Tensor):
77
+ raise ValueError("seqlens_Q and seqlens_KV must both be tensors.")
78
+
79
+ if seqlens_Q.device != query.device or seqlens_KV.device != query.device:
80
+ raise ValueError(
81
+ "seqlens_Q and seqlens_KV must be on the same device as QKV, but "
82
+ f"{seqlens_Q.device=}, {seqlens_KV.device=}, {query.device=}."
83
+ )
84
+
85
+ if seqlens_Q.dtype != torch.int32 or seqlens_KV.dtype != torch.int32:
86
+ raise ValueError(
87
+ "seqlens_Q and seqlens_KV must both be torch.int32 tensors, got "
88
+ f"{seqlens_Q.dtype=}, {seqlens_KV.dtype=}."
89
+ )
90
+
91
+ if seqlens_Q.dim() != 1 or seqlens_KV.dim() != 1:
92
+ raise ValueError(
93
+ "seqlens_Q and seqlens_KV must both be 1-D tensors, got "
94
+ f"{seqlens_Q.dim()=}, {seqlens_KV.dim()=}."
95
+ )
96
+
97
+ if seqlens_Q.shape[0] != seqlens_KV.shape[0]:
98
+ raise ValueError(
99
+ "seqlens_Q and seqlens_KV must match in size, got "
100
+ f"{seqlens_Q.shape=}, {seqlens_KV.shape=}."
101
+ )
102
+
103
+ if seqlens_Q.shape[0] < 1:
104
+ raise ValueError(
105
+ "seqlens_Q and seqlens_KV must contain at least one element, got "
106
+ f"{seqlens_Q.shape=}, {seqlens_KV.shape=}."
107
+ )
108
+
109
+ if query.shape[0] != 1:
110
+ raise ValueError(
111
+ "Variable length attention only supports sequence-packed memory layout "
112
+ f"(batch = 1), got {query.shape[0]=}."
113
+ )
114
+
115
+ assert seqlens_Q.dim() == seqlens_KV.dim() == 1
116
+ assert seqlens_Q.shape[0] == seqlens_KV.shape[0] >= 1
117
+ assert seqlens_Q.dtype == seqlens_KV.dtype == torch.int32
118
+
119
+ max_seqlen_Q = seqlens_Q.max().item() # type: ignore
120
+ max_seqlen_KV = seqlens_KV.max().item() # type: ignore
121
+
122
+ # NOTE: we have to prepend with 0 manually :(
123
+ z = torch.tensor([0], dtype=torch.int32, device=seqlens_Q.device)
124
+ cumulative_seqlen_Q = torch.cat([z, seqlens_Q.cumsum(0).to(torch.int32)], dim=0)
125
+ cumulative_seqlen_KV = torch.cat([z, seqlens_KV.cumsum(0).to(torch.int32)], dim=0)
126
+
127
+ assert isinstance(max_seqlen_Q, int)
128
+ assert isinstance(max_seqlen_KV, int)
129
+
130
+ return (
131
+ cumulative_seqlen_Q,
132
+ cumulative_seqlen_KV,
133
+ max_seqlen_Q,
134
+ max_seqlen_KV,
135
+ )