Kernels:
Trusted publisher
Uploaded using `kernel-builder`.
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- build/torch211-cxx11-cu126-x86_64-linux/__init__.py +178 -0
- build/torch211-cxx11-cu126-x86_64-linux/_environment.py +59 -0
- build/torch211-cxx11-cu126-x86_64-linux/_libnatten/__init__.py +109 -0
- build/torch211-cxx11-cu126-x86_64-linux/_libnatten/torch_wrappers.py +1006 -0
- build/torch211-cxx11-cu126-x86_64-linux/_natten_cuda_3641131.abi3.so +3 -0
- build/torch211-cxx11-cu126-x86_64-linux/_ops.py +9 -0
- build/torch211-cxx11-cu126-x86_64-linux/_types.py +85 -0
- build/torch211-cxx11-cu126-x86_64-linux/attn_merge.py +292 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/__init__.py +254 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/blackwell_fmha.py +254 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/blackwell_fna.py +500 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/__init__.py +584 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/checks.py +750 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/__init__.py +422 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/backward_knobs.py +228 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_backward_128x128.py +304 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_backward_128x64.py +223 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_backward_64x64.py +168 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_forward_32x128.py +90 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_forward_64x128.py +82 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass/fna_forward_64x64.py +63 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass_blackwell/__init__.py +391 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/cutlass_hopper/__init__.py +522 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/configs/flex/__init__.py +210 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/flex.py +799 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/fmha.py +283 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/fna.py +417 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/hopper_fmha.py +261 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/hopper_fna.py +512 -0
- build/torch211-cxx11-cu126-x86_64-linux/backends/reference.py +343 -0
- build/torch211-cxx11-cu126-x86_64-linux/context.py +231 -0
- build/torch211-cxx11-cu126-x86_64-linux/functional.py +1151 -0
- build/torch211-cxx11-cu126-x86_64-linux/metadata.json +81 -0
- build/torch211-cxx11-cu126-x86_64-linux/metadata.json.sigstore +1 -0
- build/torch211-cxx11-cu126-x86_64-linux/modules.py +449 -0
- build/torch211-cxx11-cu126-x86_64-linux/natten/__init__.py +26 -0
- build/torch211-cxx11-cu126-x86_64-linux/token_permute/__init__.py +32 -0
- build/torch211-cxx11-cu126-x86_64-linux/token_permute/cutlass_impl.py +286 -0
- build/torch211-cxx11-cu126-x86_64-linux/token_permute/frontend.py +137 -0
- build/torch211-cxx11-cu126-x86_64-linux/token_permute/torch_impl.py +368 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/__init__.py +22 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/checks.py +726 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/device.py +50 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/dtype.py +36 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/environment.py +79 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/log.py +134 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/tensor.py +113 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/testing.py +149 -0
- build/torch211-cxx11-cu126-x86_64-linux/utils/tuples.py +51 -0
- 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 |
+
)
|