kernels-bot commited on
Commit
e0d830e
·
verified ·
1 Parent(s): 2f06ade

Uploaded using `kernel-builder`.

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +1 -1
  2. build/torch-cuda/__init__.py +2 -0
  3. build/torch-cuda/_ops.py +1 -1
  4. build/torch-cuda/{ops/backends/__init__.py → backends.py} +156 -114
  5. build/torch-cuda/layers.py +10 -0
  6. build/torch-cuda/metadata.json +264 -223
  7. build/torch-cuda/metadata.json.sigstore +1 -1
  8. build/torch-cuda/modules/__init__.py +86 -5
  9. build/{torch-xpu/ops/cp → torch-cuda/modules/activations}/__init__.py +28 -21
  10. build/torch-cuda/modules/{activations.py → activations/ops.py} +302 -359
  11. build/torch-cuda/modules/backends/__init__.py +0 -17
  12. build/torch-cuda/modules/backends/triton_ascend/__init__.py +0 -422
  13. build/torch-cuda/modules/backends/triton_ascend/activations.py +0 -931
  14. build/torch-cuda/modules/backends/triton_ascend/causal_conv1d.py +0 -1175
  15. build/torch-cuda/modules/backends/triton_ascend/fused_cross_entropy.py +0 -469
  16. build/torch-cuda/modules/backends/triton_ascend/fused_kl_div.py +0 -188
  17. build/torch-cuda/modules/backends/triton_ascend/fused_linear_cross_entropy.py +0 -347
  18. build/torch-cuda/modules/backends/triton_ascend/grpo.py +0 -266
  19. build/torch-cuda/modules/backends/triton_ascend/layernorm.py +0 -355
  20. build/torch-cuda/modules/backends/triton_ascend/rotary.py +0 -211
  21. build/{torch-xpu/modules/conv/triton → torch-cuda/modules/causal_conv1d}/__init__.py +10 -3
  22. build/torch-cuda/modules/causal_conv1d/backends/__init__.py +6 -0
  23. build/torch-cuda/modules/{conv → causal_conv1d/backends}/cuda/__init__.py +2 -6
  24. build/torch-cuda/modules/causal_conv1d/backends/cuda/cp.py +98 -0
  25. build/{torch-xpu/modules/conv → torch-cuda/modules/causal_conv1d/backends}/cuda/ops.py +68 -87
  26. build/torch-cuda/modules/causal_conv1d/backends/gluon.py +417 -0
  27. build/torch-cuda/modules/{conv/cp/ops.py → causal_conv1d/cp.py} +149 -77
  28. build/torch-cuda/modules/{conv/triton/kernels.py → causal_conv1d/ops.py} +766 -125
  29. build/torch-cuda/modules/conv/causal_conv1d.py +0 -129
  30. build/torch-cuda/modules/conv/cuda/ops.py +0 -233
  31. build/torch-cuda/modules/conv/short_conv.py +0 -250
  32. build/torch-cuda/modules/conv/triton/ops.py +0 -424
  33. build/torch-cuda/modules/convolution.py +0 -42
  34. build/torch-cuda/modules/feature_map.py +1 -2
  35. build/torch-cuda/modules/fused_bitlinear.py +76 -559
  36. build/torch-cuda/modules/fused_cross_entropy.py +0 -459
  37. build/torch-cuda/modules/fused_cross_entropy/__init__.py +15 -0
  38. build/torch-cuda/modules/fused_cross_entropy/gluon.py +402 -0
  39. build/torch-cuda/modules/fused_cross_entropy/ops.py +538 -0
  40. build/torch-cuda/modules/fused_kl_div/__init__.py +11 -0
  41. build/torch-cuda/modules/{fused_kl_div.py → fused_kl_div/ops.py} +63 -113
  42. build/torch-cuda/modules/fused_linear_cross_entropy/__init__.py +15 -0
  43. build/torch-cuda/modules/{fused_linear_cross_entropy.py → fused_linear_cross_entropy/ops.py} +211 -443
  44. build/torch-cuda/modules/fused_norm_gate.py +0 -1245
  45. build/{torch-xpu/modules/conv/cp → torch-cuda/modules/grpo}/__init__.py +3 -5
  46. build/torch-cuda/modules/{grpo.py → grpo/ops.py} +77 -117
  47. build/torch-cuda/modules/l2warp.py +5 -6
  48. build/torch-cuda/modules/{conv/long_conv.py → long_conv.py} +45 -63
  49. build/{torch-xpu/modules → torch-cuda/modules/norm}/__init__.py +20 -22
  50. build/{torch-xpu/modules → torch-cuda/modules/norm}/fused_norm_gate.py +197 -220
README.md CHANGED
@@ -11,7 +11,7 @@ This is the repository card of kernels-community/fla that has been pushed on the
11
  # make sure `kernels` is installed: `pip install -U kernels`
12
  from kernels import get_kernel
13
 
14
- kernel_module = get_kernel("kernels-community/fla", version=1)
15
  chunk_kda = kernel_module.chunk_kda
16
 
17
  chunk_kda(...)
 
11
  # make sure `kernels` is installed: `pip install -U kernels`
12
  from kernels import get_kernel
13
 
14
+ kernel_module = get_kernel("kernels-community/fla", version=2)
15
  chunk_kda = kernel_module.chunk_kda
16
 
17
  chunk_kda(...)
build/torch-cuda/__init__.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  from . import layers
2
  from .ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
3
  from .ops.kda import chunk_kda, fused_recurrent_kda
 
1
+ __version__ = "0.6.0"
2
+
3
  from . import layers
4
  from .ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
5
  from .ops.kda import chunk_kda, fused_recurrent_kda
build/torch-cuda/_ops.py CHANGED
@@ -25,7 +25,7 @@ def get_backend() -> str:
25
 
26
  def _find_ops_name() -> str:
27
  kernel_name = "fla"
28
- unique_id = "fe2bb66"
29
  backend = get_backend()
30
  return f"_{kernel_name}_{backend}_{unique_id}"
31
 
 
25
 
26
  def _find_ops_name() -> str:
27
  kernel_name = "fla"
28
+ unique_id = "faa8674"
29
  backend = get_backend()
30
  return f"_{kernel_name}_{backend}_{unique_id}"
31
 
build/torch-cuda/{ops/backends/__init__.py → backends.py} RENAMED
@@ -5,26 +5,25 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- """Generic backend dispatch system for FLA operations."""
9
 
10
  from __future__ import annotations
11
 
12
- import contextlib
13
  import logging
14
  import os
15
- import threading
16
  from collections.abc import Callable
17
  from functools import cache, wraps
 
18
  from typing import Any, ClassVar, TypeVar
19
 
20
  import torch
21
 
22
- from ...utils import find_spec_cached
23
 
24
  logger = logging.getLogger(__name__)
25
  F = TypeVar('F', bound=Callable)
26
 
27
-
28
  _DISPATCH_DISABLED = os.environ.get("FLA_DISABLE_BACKEND_DISPATCH") == "1"
29
  if _DISPATCH_DISABLED:
30
  logger.info("[FLA Backend] FLA_DISABLE_BACKEND_DISPATCH=1 — all dispatch bypassed")
@@ -36,6 +35,7 @@ class BaseBackend:
36
  Attributes:
37
  backend_type (str, Optional):
38
  Identifier for the backend type, used to distinguish different backend implementations.
 
39
  Default: `"base"`.
40
  package_name (str, Optional):
41
  Name of the external package required by the backend.
@@ -52,10 +52,20 @@ class BaseBackend:
52
 
53
  backend_type: ClassVar[str] = "base"
54
  package_name: ClassVar[str | None] = None
55
- env_var: ClassVar[str | None] = None
56
  default_enable: ClassVar[bool] = True
57
- # Lower number = higher priority, default is 5
58
  priority: ClassVar[int] = 5
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  @classmethod
61
  def is_available(cls) -> bool:
@@ -63,161 +73,193 @@ class BaseBackend:
63
  return True
64
  return find_spec_cached(cls.package_name) is not None
65
 
66
- @classmethod
67
- def is_enabled(cls) -> bool:
68
- if cls.env_var is None:
 
69
  return True
70
- default_value = "1" if cls.default_enable else "0"
71
- return os.environ.get(cls.env_var, default_value) != "0"
72
 
73
  @classmethod
74
  @cache
75
  def can_use(cls) -> bool:
76
- return cls.is_available() and cls.is_enabled()
77
 
78
- def verify(self, func_name: str, *args, **kwargs) -> tuple[bool, str | None]:
79
  """Check if backend can handle the function call."""
80
- verifier_name = f"{func_name}_verifier"
81
- verifier = getattr(self, verifier_name, None)
82
  if verifier is None:
83
  return True, None
84
 
85
  try:
86
  return verifier(*args, **kwargs)
87
- except Exception as e:
88
- return False, str(e)
89
 
90
 
91
- _OPERATION_BACKEND_MODULES: dict[str, str] = {
92
- 'modules': 'fla.modules.backends',
93
- }
94
 
 
 
95
 
96
- class BackendRegistry:
97
- """Per-operation backend registry."""
 
 
98
 
99
- _registries: ClassVar[dict[str, BackendRegistry]] = {}
100
- _initialized: ClassVar[set[str]] = set()
101
- _init_lock: ClassVar[threading.Lock] = threading.Lock()
102
 
103
- def __init__(self, operation_name: str):
104
- self.operation_name = operation_name
105
- self._backends: dict[str, BaseBackend] = {}
106
- self._active: BaseBackend | None = None
107
- self._lock = threading.RLock()
108
- self._logged: set[str] = set()
109
- BackendRegistry._registries[operation_name] = self
110
 
111
- def register(self, backend: BaseBackend) -> None:
112
- """Register a backend."""
113
- with self._lock:
114
- self._backends[backend.backend_type] = backend
115
- # Update active backend based on priority
116
- self._update_active_backend()
117
 
118
- def _get_sorted_backends(self) -> list[BaseBackend]:
119
- """Get backends sorted by priority (lower number = higher priority).
 
120
 
121
- Backends with the same priority are sorted by registration order.
122
- """
123
- return sorted(
124
- self._backends.values(),
125
- key=lambda b: (b.priority, list(self._backends.values()).index(b))
126
- )
127
 
128
- def _update_active_backend(self) -> None:
129
- """Update active backend based on priority."""
130
- for backend in self._get_sorted_backends():
131
- if backend.can_use():
132
- self._active = backend
133
- return
134
 
135
- def get_active(self) -> BaseBackend | None:
136
- """Get active backend."""
137
- return self._active
 
138
 
139
  @classmethod
140
- def ensure_initialized(cls, operation: str) -> None:
141
- """Lazy-load backends on first use."""
142
- if operation in cls._initialized:
143
- return
144
 
145
- with cls._init_lock:
146
- if operation in cls._initialized:
147
- return
148
 
149
- # Import backend module to trigger registration
150
- module_path = _OPERATION_BACKEND_MODULES.get(
151
- operation,
152
- f'fla.ops.{operation}.backends',
153
- )
154
- with contextlib.suppress(ImportError):
155
- __import__(module_path, fromlist=[''])
156
 
157
- cls._initialized.add(operation)
 
 
 
158
 
 
 
 
159
 
160
- def dispatch(operation: str):
161
- """Dispatch decorator with verifier support.
 
162
 
163
- Iterates through all registered backends and selects the first one
164
- that passes the verifier for the given function call.
165
- """
166
- def decorator(func: F) -> F:
 
 
 
 
 
167
  if _DISPATCH_DISABLED:
168
- return func
169
- func_name = func.__name__
170
 
171
- @wraps(func)
172
  def wrapper(*args, **kwargs) -> Any:
173
- # Lazy initialization of backends
174
- BackendRegistry.ensure_initialized(operation)
175
-
176
- registry = BackendRegistry._registries.get(operation)
177
- if registry is None:
178
- return func(*args, **kwargs)
179
-
180
- # Iterate through all registered backends sorted by priority
181
- # to find one that can handle this call
182
- backends_list = registry._get_sorted_backends()
183
-
184
- for be in backends_list:
185
- # Avoid be.can_use(): its @cache wrapper breaks torch.compile tracing.
186
- if not (be.is_available() and be.is_enabled()):
187
  continue
188
 
189
- can_use, reason = be.verify(func_name, *args, **kwargs)
190
- if not can_use:
191
- fail_key = f"{operation}:{func_name}:{be.backend_type}:fail"
192
- if fail_key not in registry._logged:
193
- registry._logged.add(fail_key)
194
  logger.info(
195
- f"[FLA Backend] {operation}.{func_name} -> {be.backend_type} "
196
  f"rejected: {reason}"
197
  )
198
  continue
199
 
200
- impl = getattr(be, func_name, None)
201
- if impl is None:
202
  continue
203
 
204
- result = impl(*args, **kwargs)
205
 
206
- log_key = f"{operation}:{func_name}:{be.backend_type}"
207
- if log_key not in registry._logged:
208
- registry._logged.add(log_key)
209
- logger.info(f"[FLA Backend] {operation}.{func_name} -> {be.backend_type}")
210
 
211
  return result
212
 
213
- # No backend can handle this call, use default implementation
214
- return func(*args, **kwargs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
- # Dispatch performs runtime backend selection; keep it out of torch.compile graphs.
217
- wrapper = torch.compiler.disable(wrapper)
218
-
219
- return wrapper
220
  return decorator
221
 
222
 
223
- __all__ = ['BackendRegistry', 'BaseBackend', 'dispatch']
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ """Backend registration and runtime selection."""
9
 
10
  from __future__ import annotations
11
 
12
+ import importlib
13
  import logging
14
  import os
 
15
  from collections.abc import Callable
16
  from functools import cache, wraps
17
+ from inspect import unwrap
18
  from typing import Any, ClassVar, TypeVar
19
 
20
  import torch
21
 
22
+ from .utils import find_spec_cached, has_usable_nvcc
23
 
24
  logger = logging.getLogger(__name__)
25
  F = TypeVar('F', bound=Callable)
26
 
 
27
  _DISPATCH_DISABLED = os.environ.get("FLA_DISABLE_BACKEND_DISPATCH") == "1"
28
  if _DISPATCH_DISABLED:
29
  logger.info("[FLA Backend] FLA_DISABLE_BACKEND_DISPATCH=1 — all dispatch bypassed")
 
35
  Attributes:
36
  backend_type (str, Optional):
37
  Identifier for the backend type, used to distinguish different backend implementations.
38
+ `FLA_<BACKEND_TYPE>` enables all backends of this type, overriding individual switches set to `0`.
39
  Default: `"base"`.
40
  package_name (str, Optional):
41
  Name of the external package required by the backend.
 
52
 
53
  backend_type: ClassVar[str] = "base"
54
  package_name: ClassVar[str | None] = None
55
+ env_var: str | None = None
56
  default_enable: ClassVar[bool] = True
 
57
  priority: ClassVar[int] = 5
58
+ implementation: Callable | None = None
59
+ verifier: Callable | None = None
60
+
61
+ def __init__(self, *, env_var: str | None = None):
62
+ if env_var is not None:
63
+ self.env_var = env_var
64
+
65
+ def get_implementation(self, function_name: str) -> Callable | None:
66
+ if self.implementation is not None:
67
+ return self.implementation
68
+ return getattr(self, function_name, None)
69
 
70
  @classmethod
71
  def is_available(cls) -> bool:
 
73
  return True
74
  return find_spec_cached(cls.package_name) is not None
75
 
76
+ def is_enabled(self) -> bool:
77
+ if self.env_var is None:
78
+ return True
79
+ if os.environ.get(f"FLA_{self.backend_type.upper()}", "0") != "0":
80
  return True
81
+ default_value = "1" if self.default_enable else "0"
82
+ return os.environ.get(self.env_var, default_value) != "0"
83
 
84
  @classmethod
85
  @cache
86
  def can_use(cls) -> bool:
87
+ return cls.is_available() and cls().is_enabled()
88
 
89
+ def verify(self, function_name: str, *args, **kwargs) -> tuple[bool, str | None]:
90
  """Check if backend can handle the function call."""
91
+ verifier_name = f"{function_name}_verifier"
92
+ verifier = self.verifier or getattr(self, verifier_name, None)
93
  if verifier is None:
94
  return True, None
95
 
96
  try:
97
  return verifier(*args, **kwargs)
98
+ except Exception as error:
99
+ return False, str(error)
100
 
101
 
102
+ class TritonAscendBackend(BaseBackend):
103
+ """Shared availability and selection policy for Ascend implementations."""
 
104
 
105
+ backend_type = 'triton_ascend'
106
+ priority = 0
107
 
108
+ @classmethod
109
+ def is_available(cls) -> bool:
110
+ from .utils import IS_NPU
111
+ return IS_NPU
112
 
 
 
 
113
 
114
+ class TileLangBackend(BaseBackend):
115
+ """Shared availability and selection policy for TileLang implementations."""
 
 
 
 
 
116
 
117
+ backend_type = 'tilelang'
118
+ package_name = 'tilelang'
119
+ env_var = 'FLA_TILELANG'
 
 
 
120
 
121
+ @classmethod
122
+ def is_available(cls) -> bool:
123
+ return super().is_available() and has_usable_nvcc()
124
 
 
 
 
 
 
 
125
 
126
+ class GluonBackend(BaseBackend):
127
+ """NVIDIA GPU backend using Gluon kernels."""
 
 
 
 
128
 
129
+ backend_type = 'gluon'
130
+ package_name = 'triton.experimental.gluon'
131
+ env_var = 'FLA_GLUON'
132
+ default_enable = False
133
 
134
  @classmethod
135
+ def is_available(cls) -> bool:
136
+ from .utils import IS_NVIDIA
137
+ return IS_NVIDIA and super().is_available()
 
138
 
 
 
 
139
 
140
+ class BackendRegistry:
141
+ """Ordered backend candidates for an operation or entry point."""
 
 
 
 
 
142
 
143
+ def __init__(self, operation_name: str):
144
+ self.operation_name = operation_name
145
+ self._backends: dict[str, BaseBackend] = {}
146
+ self._logged: set[str] = set()
147
 
148
+ def register(self, backend: BaseBackend) -> None:
149
+ """Register or replace a backend by its type."""
150
+ self._backends[backend.backend_type] = backend
151
 
152
+ def _get_sorted_backends(self) -> list[BaseBackend]:
153
+ """Lower priorities come first; ties retain registration order."""
154
+ return sorted(self._backends.values(), key=lambda backend: backend.priority)
155
 
156
+ def get_active(self) -> BaseBackend | None:
157
+ """Return the first available and enabled backend, before call verification."""
158
+ for backend in self._get_sorted_backends():
159
+ if backend.is_available() and backend.is_enabled():
160
+ return backend
161
+ return None
162
+
163
+ def dispatch(self, default_function: F) -> F:
164
+ """Select the first eligible backend, falling back to the decorated function."""
165
  if _DISPATCH_DISABLED:
166
+ return default_function
167
+ function_name = default_function.__name__
168
 
169
+ @wraps(default_function)
170
  def wrapper(*args, **kwargs) -> Any:
171
+ for backend in self._get_sorted_backends():
172
+ # avoid backend.can_use(): its @cache wrapper breaks torch.compile tracing.
173
+ if not (backend.is_available() and backend.is_enabled()):
 
 
 
 
 
 
 
 
 
 
 
174
  continue
175
 
176
+ accepted, reason = backend.verify(function_name, *args, **kwargs)
177
+ if not accepted:
178
+ rejection_key = f"{self.operation_name}:{function_name}:{backend.backend_type}:fail"
179
+ if rejection_key not in self._logged:
180
+ self._logged.add(rejection_key)
181
  logger.info(
182
+ f"[FLA Backend] {self.operation_name}.{function_name} -> {backend.backend_type} "
183
  f"rejected: {reason}"
184
  )
185
  continue
186
 
187
+ implementation = backend.get_implementation(function_name)
188
+ if implementation is None:
189
  continue
190
 
191
+ result = implementation(*args, **kwargs)
192
 
193
+ log_key = f"{self.operation_name}:{function_name}:{backend.backend_type}"
194
+ if log_key not in self._logged:
195
+ self._logged.add(log_key)
196
+ logger.info(f"[FLA Backend] {self.operation_name}.{function_name} -> {backend.backend_type}")
197
 
198
  return result
199
 
200
+ return default_function(*args, **kwargs)
201
+
202
+ # runtime backend selection must stay outside torch.compile graphs.
203
+ return torch.compiler.disable(wrapper)
204
+
205
+
206
+ _operation_registries: dict[str, BackendRegistry] = {}
207
+ _function_registries: dict[Callable, BackendRegistry] = {}
208
+
209
+
210
+ def _get_operation_registry(operation: str) -> BackendRegistry:
211
+ if operation not in _operation_registries:
212
+ _operation_registries[operation] = BackendRegistry(operation)
213
+ return _operation_registries[operation]
214
+
215
+
216
+ def _load_operation_registry(operation: str) -> BackendRegistry:
217
+ # an existing registry does not guarantee that all of the owner's backends are loaded.
218
+ importlib.import_module(f'.ops.{operation}.backends', __package__)
219
+ return _get_operation_registry(operation=operation)
220
+
221
+
222
+ def register(
223
+ entry_point: Callable | str,
224
+ *,
225
+ backend: type[BaseBackend] | None = None,
226
+ verifier: Callable | None = None,
227
+ env_var: str | None = None,
228
+ ) -> Callable[[F], F]:
229
+ """Register an implementation or adapter for a dispatched function or operation name."""
230
+ def decorator(implementation: F) -> F:
231
+ if _DISPATCH_DISABLED and not isinstance(entry_point, str):
232
+ return implementation
233
+ if isinstance(implementation, type):
234
+ registered_backend = implementation()
235
+ if backend is not None and backend.backend_type != registered_backend.backend_type:
236
+ raise ValueError('The registration backend must match the adapter backend_type')
237
+ else:
238
+ registered_backend = backend() if backend is not None else BaseBackend()
239
+ registered_backend.implementation = implementation
240
+ registered_backend.verifier = verifier
241
+ if env_var is not None:
242
+ registered_backend.env_var = env_var
243
+ if isinstance(entry_point, str):
244
+ registry = _get_operation_registry(operation=entry_point)
245
+ else:
246
+ registry = _function_registries[unwrap(entry_point)]
247
+ registry.register(registered_backend)
248
+ return implementation
249
 
 
 
 
 
250
  return decorator
251
 
252
 
253
+ def dispatch(entry_point: F | str) -> F | Callable[[F], F]:
254
+ """Allow backends to register replacements while retaining the original fallback."""
255
+ if isinstance(entry_point, str):
256
+ return _load_operation_registry(operation=entry_point).dispatch
257
+ if _DISPATCH_DISABLED:
258
+ return entry_point
259
+ registry = BackendRegistry(entry_point.__module__)
260
+ _function_registries[unwrap(entry_point)] = registry
261
+
262
+ return registry.dispatch(entry_point)
263
+
264
+
265
+ __all__ = ['BackendRegistry', 'BaseBackend', 'dispatch', 'register']
build/torch-cuda/layers.py CHANGED
@@ -7,6 +7,8 @@ from .ops.kda import chunk_kda, fused_recurrent_kda
7
 
8
 
9
  class FusedRMSNormGated(nn.Module):
 
 
10
  def forward(self, hidden_states, gate=None):
11
  return rms_norm_gated(
12
  hidden_states,
@@ -22,6 +24,8 @@ class FusedRMSNormGated(nn.Module):
22
 
23
 
24
  class chunk_kimi_delta_attention(nn.Module):
 
 
25
  def forward(
26
  self,
27
  query,
@@ -54,6 +58,8 @@ class chunk_kimi_delta_attention(nn.Module):
54
 
55
 
56
  class recurrent_kimi_delta_attention(nn.Module):
 
 
57
  def forward(
58
  self, query, key, value, g, beta, initial_state, output_final_state, use_qk_l2norm_in_kernel=False, **kwargs,
59
  ):
@@ -75,6 +81,8 @@ class recurrent_kimi_delta_attention(nn.Module):
75
 
76
 
77
  class chunk_gated_delta_rule(nn.Module):
 
 
78
  def forward(
79
  self,
80
  query,
@@ -107,6 +115,8 @@ class chunk_gated_delta_rule(nn.Module):
107
 
108
 
109
  class recurrent_gated_delta_rule(nn.Module):
 
 
110
  def forward(
111
  self, query, key, value, g, beta, initial_state, output_final_state, use_qk_l2norm_in_kernel=False, **kwargs,
112
  ):
 
7
 
8
 
9
  class FusedRMSNormGated(nn.Module):
10
+ can_torch_compile = True
11
+
12
  def forward(self, hidden_states, gate=None):
13
  return rms_norm_gated(
14
  hidden_states,
 
24
 
25
 
26
  class chunk_kimi_delta_attention(nn.Module):
27
+ can_torch_compile = True
28
+
29
  def forward(
30
  self,
31
  query,
 
58
 
59
 
60
  class recurrent_kimi_delta_attention(nn.Module):
61
+ can_torch_compile = True
62
+
63
  def forward(
64
  self, query, key, value, g, beta, initial_state, output_final_state, use_qk_l2norm_in_kernel=False, **kwargs,
65
  ):
 
81
 
82
 
83
  class chunk_gated_delta_rule(nn.Module):
84
+ can_torch_compile = True
85
+
86
  def forward(
87
  self,
88
  query,
 
115
 
116
 
117
  class recurrent_gated_delta_rule(nn.Module):
118
+ can_torch_compile = True
119
+
120
  def forward(
121
  self, query, key, value, g, beta, initial_state, output_final_state, use_qk_l2norm_in_kernel=False, **kwargs,
122
  ):
build/torch-cuda/metadata.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "name": "fla",
3
- "id": "_fla_cuda_fe2bb66",
4
- "version": 1,
5
  "kernels-minver": "0.14.0",
6
  "license": "MIT",
7
  "python-depends": [
@@ -14,297 +14,338 @@
14
  "digest": {
15
  "algorithm": "sha256",
16
  "files": {
17
- "__init__.py": "bVRNYUhThvULeUaovvQFZCJ9IZZsC+0kU/HbnXdYT1A=",
18
- "_ops.py": "pXgvH5hf/dXVr6/amV+mRqgiBUGncbV3gbMubhmlOW4=",
19
- "layers.py": "/39XVQW4d4GHeNROqT67ujKbItdteLBtm0PT6zq41YQ=",
20
- "modules/__init__.py": "6ZjjRMt6Rf+2OegXDC8w58RD1IW3SBK+dxBoZa4Uohc=",
21
- "modules/activations.py": "Lp7xLUf7r785nt93NFhqoSQM+G7AK2e6kviE2/0TRJs=",
22
- "modules/backends/__init__.py": "l0tLqWyPFwwDRTdnHZYBzoux6J/LOFveJ53KREAKKDk=",
23
- "modules/backends/triton_ascend/__init__.py": "vbU1UZwQDjVfF75INGYuWlIhuUTzx2TuZHIvme/QSMw=",
24
- "modules/backends/triton_ascend/activations.py": "eqE/iXLZZEan7XHSa9ziz7mWNYwxTC5kIcJiwDwRr/Y=",
25
- "modules/backends/triton_ascend/causal_conv1d.py": "VRxcTWPBB221dgd0+eNHEzCi9Echi6B2XOINtNOjHsw=",
26
- "modules/backends/triton_ascend/fused_cross_entropy.py": "RITRIWoSOKhG8Mz7odkko27gESEd8M48ICc1IXRSVPg=",
27
- "modules/backends/triton_ascend/fused_kl_div.py": "jb7XzKT6AKmXl8NxjZ4/SNofjdD3ioi4Df/l4KsIAtI=",
28
- "modules/backends/triton_ascend/fused_linear_cross_entropy.py": "Z8/wQfPzJTg9ZICxdsABldRPsMa+mUZUTHhCJS2OeQc=",
29
- "modules/backends/triton_ascend/grpo.py": "Jy+RkxbJ4RgJsEbk0xNgnw/D3Gj/P+6xDaMfx3I7DPE=",
30
- "modules/backends/triton_ascend/layernorm.py": "7uKqeIraFdvcAVR0g6CLw+NJDCX8cLRMjNGHoFx1AnM=",
31
- "modules/backends/triton_ascend/rotary.py": "+CmvapJJ3AOlwHbRxxSEIeVDjmlFPRno+5aqdfAHznI=",
32
- "modules/conv/__init__.py": "WZKA4OJWHEN0bg2Yl2i2dvfkSp9GZKaD65abY8pSKbw=",
33
- "modules/conv/causal_conv1d.py": "sHN5nlzDzSQwgo40OPt9lzcLRzZZnPCFvS/pmfK8L9k=",
34
- "modules/conv/cp/__init__.py": "BKKfEjTjwyEAwDu66avCFolqnmrv9qHHNvxjfOHi3fY=",
35
- "modules/conv/cp/ops.py": "pv7PGhmkfBsFt4tY8Hd0UAHzUPqp6DhgjRJmUujWitU=",
36
- "modules/conv/cuda/__init__.py": "q35Kd12ITYtHp5JtaAhQJ6/YCEY95RWWveKoFPJ/VCU=",
37
- "modules/conv/cuda/ops.py": "YE2httgrnIB1iSE5Uw9h1wtnq0UtybPuouF3z2reUb0=",
38
- "modules/conv/long_conv.py": "h+wI2DdlBSQIZC335mwgBsjxGbByGaklb6Nv4WcQMMg=",
39
- "modules/conv/short_conv.py": "DdAbuCpegRP32xEECOQQ4Ty3gWAOiFUzJLCD2mxcU1w=",
40
- "modules/conv/triton/__init__.py": "fDLG2FdP68jqqsBxNdKifAIEjmvGzXNrLm/xDMrfbvg=",
41
- "modules/conv/triton/kernels.py": "EK+wcpRMAGXKGmFCrZzxEeuZJmWvzUqxdTBYLwrpFT8=",
42
- "modules/conv/triton/ops.py": "jIxuam5Ch5U3ErbT8D6Zn/5FqBoTq15kcJ9BGMD8exU=",
43
- "modules/convolution.py": "WREsoV3gCce7rMEdpuEsyBgvmfbhk2IFdpUrr4TMtBw=",
44
- "modules/feature_map.py": "sfoRvsdgvhtWkKuMYE/f1c7eyINwEaZ9TfItktWD+RE=",
45
- "modules/fused_bitlinear.py": "lWTe4Ym60A+rHZTdQ6cZqa1Cje5vG/xMWvzLSAzLpQc=",
46
- "modules/fused_cross_entropy.py": "si+ZNK/vu/TERnt/v2/Tm+ZBfn3poUoPDDTuJmrEuYM=",
47
- "modules/fused_kl_div.py": "IDl78EMB9KXWmbV2cflnbXmcQpzdoqkwlEj4tvAPsbA=",
48
- "modules/fused_linear_cross_entropy.py": "SruIxSe0Hhlf7n0upOldvn9HwOfL6XQ42N6qGZC6aOA=",
49
- "modules/fused_norm_gate.py": "LAOhNgUSEiIU3Y5Ya5EHRjMAxHT0StODm04AUcL4Nys=",
50
- "modules/grpo.py": "JPZnSGESIMmHDVkfYDSUzbm1qb2vY6dmDmFvSMI53RU=",
51
- "modules/l2norm.py": "LchUl4mEXyx2cgHPuNgUhkorbNeuN+P7SfJyGXX0OyI=",
52
- "modules/l2warp.py": "5oJlj6JRXBGe5ZANNnUA8GLIjlzXCnddtz2lwgKn6WA=",
53
- "modules/layernorm.py": "1QhCZYF7pFRmZTxCWC55oG1uB6Lak5RXYca8MF5q5SA=",
54
- "modules/layernorm_gated.py": "s/c/gRsZ3O6jXwY3b9owPaN1QAoOvsSevWuW2EXuJjE=",
55
  "modules/mlp.py": "q09Zw5Bttqq27hnlybbQ9ncr0jSw1UCrJH2Ewh9roV4=",
56
- "modules/parallel.py": "y3ZBLQp3FeDfBlHYmSerllqbfE3RgzX4k26lA15DPeg=",
57
- "modules/rotary.py": "qVsb20GcFf9RolZ3dChWzEi5S9rc5UaMdC3dlrhNUj4=",
58
- "modules/token_shift.py": "IWlvUPZ/Zm4IKW0dWSsx4p4r+ovbpPHQhk5Dp09P5Wg=",
59
- "modules/token_shift_cp.py": "Zvaa6d/GiuljKqNgLdkjX0vysktpIVV8KMSTOjdG3Hw=",
60
- "ops/__init__.py": "PsKzHYHoje01DfrVdltffiZFdS5CTVps5xl9dXr2RHo=",
 
 
 
 
 
 
 
61
  "ops/abc/__init__.py": "WwLiIkYi3xJ5RiG/bOCNNRaeeqmMuIPT6n1g23qfpDg=",
62
- "ops/abc/chunk.py": "sqvLGP2r+frjpL2qWK8GHCoWWPU5Df8IT5y20oaTZTE=",
63
  "ops/abc/naive.py": "AZ3uz9eDvycW3gTwZuv9J4k86KndfQMZiLBFyPmzsLw=",
 
 
 
 
64
  "ops/attn/__init__.py": "gU/67mJonbeXpzfxgi8K7HIG2a8Xpjd0gOuCk/Pu9EI=",
65
- "ops/attn/backends/__init__.py": "3xcuWH+iLBPRb3g4ZkyXBXm8wJFxQK4GHdUpmK8Nh88=",
66
- "ops/attn/decoding.py": "ScKiTKcbl4kcnYDNz6jXd2a1qB5ymDcz8YVkotRnO9Y=",
67
- "ops/attn/naive.py": "nBsWqmBWOMQdaQYvdFejDLoX0Pei9W8oMPrHZoCCin0=",
68
- "ops/attn/parallel.py": "+Vkkjy4OIwR+7SXBf6fCJqrPg740qc6a1+21RSNt9Cs=",
 
 
69
  "ops/attnres/__init__.py": "/ljBk1VNFjDa9XABPXqIiQ05vUdzRV6Dbx/KkQihrWs=",
70
- "ops/attnres/fused.py": "Q8RKiwlUtFZkHELCLV3wtQGf1cVDPoDNMK1waEwbxFQ=",
 
 
71
  "ops/attnres/naive.py": "nV4WPz/vfRwfRsIzF7pKV95fEx2dQP4BrrPoQCh0a0M=",
72
- "ops/backends/__init__.py": "3DU4e6t7dmPuZLlHjPnt4q/MAtDXtaMmAcSY4ycyytY=",
73
  "ops/based/__init__.py": "qdBLUigpgjQaIN3HTtq57uxoqAaBnQkZbFiEIf18Rvk=",
74
- "ops/based/fused_chunk.py": "Evgk49vwK6VxWWmw3KdwhiUV5N8RF/PcxZP8J1dlYR8=",
75
  "ops/based/naive.py": "YWIFSLeslp2ruyQDz/uCr5V4ao2cyRyeQH1wiVLMB8U=",
76
- "ops/based/parallel.py": "7hNqqI2UIeJNebDTF7RwW+Q9ST15DwMxJugiaVO9s/k=",
77
  "ops/comba/__init__.py": "FBKonbvPPFHY7an2Auh1sXVJLbcgwX8aAD5fxi2oecY=",
78
- "ops/comba/chunk.py": "o+3soCUQ27uZXe+YOZpYF1/aiLFEyTdbb+bmetY6O2c=",
79
- "ops/comba/fused_recurrent.py": "ujg1r9D2Jx+/sgli1msfkXoiCZb2gX9dL/OpOca3kGs=",
80
  "ops/comba/naive.py": "QH967c5rxlSSDs6J6jRtLjZ3iLiwgAYgY9+0m9rgD18=",
81
- "ops/comba/utils.py": "rcW3h8gjoEzSoLgY1HkrIwsn9XKEYtz274CGqr8wWzU=",
82
- "ops/comba/wy_fast.py": "EsJfyG3HDGY9rgJ97cn2SAq1DeqnXdgj5mzdOlWtz2I=",
83
- "ops/common/__init__.py": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
84
- "ops/common/backends/__init__.py": "aEhY8S2W0I6qbjedaCYjqLt4VaNumWM+bjKtPEJSAN0=",
85
- "ops/common/backends/intracard.py": "yXACesLmdhG1PwAFJuquz4F+hqSNtFvXwZ51YeEN8FM=",
86
- "ops/common/backends/tilelang/__init__.py": "2Zz/LkJfy8oOvqUcen6iQDMcf2prznZpYhHAc5QYW3E=",
87
- "ops/common/backends/tilelang/chunk_bwd.py": "5zXyBzqIXRgoZdZ8zjLNEIz3jBksHpaVxnqUk66ckjQ=",
88
- "ops/common/backends/tilelang/parallel_attn_bwd.py": "w+KjVu0voRjcFhmwWfIEYaSwmlk17oIu9EKRvjNIAro=",
89
- "ops/common/backends/tilelang/parallel_attn_fwd.py": "7INMyGs1d3H8BKcjRnMvu/NHMm9VeNFTETR5z7ZO15c=",
90
- "ops/common/chunk_delta_h.py": "QmkSsbbyOHai9cVaMB8O/8ep4NxiM6ieUVKmja6USw8=",
91
- "ops/common/chunk_h.py": "imyz71p9AD6nOtStpN+zzgE0yVbGnq4DO3FitrO3hvc=",
92
- "ops/common/chunk_h_parallel.py": "YGNR5a+eC7UShlVAvQF3fSLK+HXoP2Nc06H3p/PyzVg=",
93
- "ops/common/chunk_h_split.py": "zYoWf1Li3Q7MFPusp5BRf1F+OuGzxxTeiZ3hdunYnCY=",
94
- "ops/common/chunk_o.py": "1yUHQ+B2J1PQpxTFEa8R6JxpyV1k/TNXiXkOvFRfQAo=",
95
- "ops/common/chunk_scaled_dot_kkt.py": "MyoKTPKUUZlOzdC4FdP4etL6pPBGzaNL5DJQyH3603E=",
96
- "ops/common/fused_chunk.py": "nKIxBC8UD00KuY7WGFysC8P4ELNLEJhHmT60VcqyHbc=",
97
- "ops/common/fused_recurrent.py": "RnQdVD59iOcRa8sSK4XcQT59RhZJr42YplDaQrDt97o=",
98
- "ops/common/gate.py": "ExEV4w/h/y44Qb6L1cwOFEsMutGjs/t338KimPVjeHM=",
99
- "ops/common/intracard_cp.py": "R+wNrYC2m0gUn0jr5CpsxpCIVRCRkRnFXjBus9XbDE8=",
100
  "ops/cp/__init__.py": "uud2WkodJvH5U5DjBa5oSLJtZ1G8O4D4XEuspXyC9lA=",
101
- "ops/cp/chunk_delta_h.py": "+pPn7+hNImlTrCZLHNpaJKabA+OaoNXofYbX2F4fTD0=",
102
  "ops/cp/comm.py": "lqyQDwmK19vowTtGJGO+6SEn/neFFE0G8xFvlxLOmb0=",
103
- "ops/cp/context.py": "N/p87Th0z6m2cGhP6icCN7rEg3QPVua8ymP8oDD4qy0=",
 
 
 
 
 
 
104
  "ops/delta_rule/__init__.py": "1gIkBajhtbVwuJOgPQ+FYYdq4tYgNgib+veOnont+N4=",
105
- "ops/delta_rule/chunk.py": "r1b6g3/4OO1oDZ8n/MMyzagYHs0c5FPKU8//7KskU8w=",
106
  "ops/delta_rule/fused_chunk.py": "2NiUBRAl98YgRVGr2uzYJV22pQW3WNOe6euTX1Mdqyw=",
107
- "ops/delta_rule/fused_recurrent.py": "aerY1sPJ0y6IknruBVOwbu3v/pLz2PjP3aNEzsArp5M=",
108
  "ops/delta_rule/naive.py": "IK4DL04IAZtCmMyfzyw5fYcfqsD9HxgCQ9n2makZKWg=",
109
- "ops/delta_rule/parallel.py": "GMhpAZoEDuzyIjK0Qcpu+ku528WnLgDUujcsoRnXU6o=",
110
- "ops/delta_rule/wy_fast.py": "O0UuKiOw3mImzRQHFLKHifSewFAoqRJ9i9r0puiX9fw=",
111
  "ops/deltaformer/__init__.py": "51ZmNFoHy3P0lVn/2EHKu1QS2BMC6h+hnAwSIr9szYg=",
112
  "ops/deltaformer/invcum.py": "ren181AxlHBv2MoZIHmSGMmJ0sW6HDb3t8fqG7gDELU=",
113
- "ops/deltaformer/naive.py": "8b88rviKkDZpW+F9GwgEkVkwuSioECwgRJX//CVksnI=",
114
- "ops/deltaformer/parallel.py": "sDHrnr2fM4+0T2cyAJoI2VP9Lan3NmaaDxk9JLtyLWk=",
115
  "ops/dsa/__init__.py": "ZNyws889LPBiY8gwB5zTs2vQwJYV8bX5jg6Cldkkq9s=",
116
- "ops/dsa/naive.py": "lKgrdKOoE/qCdF4DtrI0uEkRdB28kUYbtn8hO63fzyo=",
117
  "ops/forgetting_attn/__init__.py": "A6ozg9zdEsz4lgUAXqEIUY8CeNp+5IZqYKOpChpj/A4=",
118
- "ops/forgetting_attn/naive.py": "RDPXw01ZnhTMV125OFvEVeDDMht3BYj5gQI4oaT+T1M=",
119
- "ops/forgetting_attn/parallel.py": "fcKF7Q84XXmeeKI2Tpv5XIfmxFovuXlmBov7Ie8OjKg=",
120
  "ops/gated_delta_product/__init__.py": "0MAzYdxP0ODZjN4WNQ+3HLwigokyfMv15rXUpTX+qnw=",
121
- "ops/gated_delta_product/chunk.py": "QNHYG+1YKzot3cQ2oWvzuv7nDoZjllEXEomCk11WUJM=",
122
- "ops/gated_delta_product/chunk_deltaproduct_h.py": "5IvD8ZZxaN216Wj8dop1cwJE9P5rvZ+Si98qB6kC5DQ=",
123
- "ops/gated_delta_product/chunk_deltaproduct_o.py": "nvMmsV+E9AG4Z9HA6UV69DdGMUYH2XcL8NAAubUtIHc=",
124
  "ops/gated_delta_product/chunk_ref.py": "R1dHdPy4rU+lTQQKodAn9h2xPLxmwI2z0T6pRvh8Llo=",
125
- "ops/gated_delta_product/naive.py": "Q907vtcavFpalFlttThkWSb/SaTa4vqAG+I/nWvKDno=",
126
  "ops/gated_delta_rule/__init__.py": "Xt0hhHr3CECyZDmBIZakdufk3bvvIa/V/iBmpkYwpnw=",
127
- "ops/gated_delta_rule/chunk.py": "DFEZWmp016p+65pnDynAMVOY+NsUTGOfVcXsAhQr1i4=",
128
- "ops/gated_delta_rule/chunk_fwd.py": "NCMKqzu9uM2yAGhvVqENRtoVLwoxVo/KaVChkvIJIDE=",
129
- "ops/gated_delta_rule/fused_recurrent.py": "M7x3/U1Xnf5mndUc+lvGpzxu4KDNc2Sfevv+RVZZFNk=",
130
- "ops/gated_delta_rule/gate.py": "H9JLPKjgZmorhyV+1IJFSMIa6x9admDn+EXXlwg167g=",
 
 
131
  "ops/gated_delta_rule/naive.py": "0c8XmSNJ/T6Ur5mbIuPTqBvkotGIHOW3CjRXJXFm4Ms=",
132
- "ops/gated_delta_rule/wy_fast.py": "kFbW7BX1I4DBBBhfV1yXGQR4Jb9tGM6yILNmTXNOhxo=",
133
  "ops/gated_oja_rule/__init__.py": "oShC/cL0jMy+xWR7YBV9QxkAdxt1xasXDCypGAU6Hgo=",
134
  "ops/gated_oja_rule/chunk.py": "lq+5BXEaJLfL4dqqcJb4TmtyF9OrP9Wh+JpRfpdzd2s=",
135
- "ops/gated_oja_rule/chunk_h.py": "kr6KBcPbFRrIQFBoQjmt1wsyvO1pyLeGW5v/k+NI87Q=",
136
- "ops/gated_oja_rule/chunk_kkt.py": "N/sU3E2i5+jQBXnFuWyaUAiQkAgWUxNjO9lxdlpEkDA=",
137
- "ops/gated_oja_rule/chunk_o.py": "4tPVh8KWj+D2onfXbIhnhaGWq35az5uJUwxTUbbRll0=",
138
- "ops/gated_oja_rule/fused_recurrent.py": "yPfAcIXWBlr50lvK59bbF2qkhKWt40eT+EXx6exojK0=",
139
- "ops/gated_oja_rule/wy_fast.py": "mS9GSN2RVUbEMkz2o1Hfi6j0nOhXtjLLSIccXkwmW3c=",
140
  "ops/gdn2/__init__.py": "xiEsgvVEfHhKcurQqL7ZKGGzEBW2BtGAylE9vxoNmAk=",
141
- "ops/gdn2/chunk.py": "zgJK/Mwhn1fA9PpvagLdy6yFuxo2c1Z8DmUY5FJp2kw=",
142
- "ops/gdn2/chunk_bwd.py": "Ay8kHSsnyD1wxGE1YcEfV3d4L3yae/8bDBMReKPbVBw=",
143
- "ops/gdn2/chunk_fwd.py": "7Z0d9SnKSby4yCAeXx5kKx6HIdYfaXl0ZhYDDAniClU=",
144
- "ops/gdn2/chunk_intra.py": "UjnmvfEg9EB41jGA9YHcZT9R2oHpBQI4PCq1Dwj1ZiQ=",
145
- "ops/gdn2/chunk_intra_token_parallel.py": "U1lf9FZ6mfW2iIm06YJ5qPX8IQeT4U/G+J7RlZoWutw=",
146
- "ops/gdn2/fused_recurrent.py": "3QsJl80MRQYrQXVN9lmgw4cVD1hLHrQXg6xcIgU5Bz8=",
 
147
  "ops/gdn2/naive.py": "amWAVAHrMA34W2KXYRoN0VjdGDQjzyvNXg8Ozd4+nno=",
148
- "ops/gdn2/wy_fast.py": "DNH9FhRaZl4nqZ/YrxH2SqCgZdC77bkJ24sFIemnkRM=",
149
  "ops/generalized_delta_rule/__init__.py": "cn6rKWEDrxWw428Dk6EEy4Y7nyWHpc8rtR+EbRO9ndA=",
150
  "ops/generalized_delta_rule/dplr/__init__.py": "F8TbaiZeRKjbKhlI4XYopNd4bfzV10feNO06i32h2Vo=",
151
- "ops/generalized_delta_rule/dplr/chunk.py": "qeR4srXoGT8NjNx93IQEyPGq3sqsK78CdOovbRwbntU=",
152
- "ops/generalized_delta_rule/dplr/chunk_A_bwd.py": "MDoA0HOWDti16rAh8Q4D4RJonJkmIN04dCwUsHOM+sk=",
153
- "ops/generalized_delta_rule/dplr/chunk_A_fwd.py": "wQSOE/PoqvjP6ugh3wSmbsst0ECSclxJEl5x/4NQ0KI=",
154
- "ops/generalized_delta_rule/dplr/chunk_h_bwd.py": "hAISrlVF9QETkcoDAgEKXRY0w9DSDKdohqTXfkg421E=",
155
- "ops/generalized_delta_rule/dplr/chunk_h_fwd.py": "vWtEs8L4DXX52UF5qt6Vuu/pOsHzKOFOIFtyezheWPY=",
156
- "ops/generalized_delta_rule/dplr/chunk_o_bwd.py": "CuHt6fYI2g/6m0k2mj7JSLhCQ+zcKrMUYc2LOmKuUUs=",
157
- "ops/generalized_delta_rule/dplr/chunk_o_fwd.py": "KOF8wWIEMIyhDfSL44bucPPsCp6u3AdYPlRDKK10KyQ=",
158
- "ops/generalized_delta_rule/dplr/fused_recurrent.py": "ABaz+l4n77U9FrE3bdsGx/5jexAnvJ1cJcI/XJkeIac=",
 
159
  "ops/generalized_delta_rule/dplr/naive.py": "4flLSZUtwaq5S/PH+fu04rzOM2+USdRX6Ofte4lanE8=",
160
- "ops/generalized_delta_rule/dplr/wy_fast_bwd.py": "HiMBn/U+a0vSCOVmn29UYLylI5x26qqualdl4E6ipd0=",
161
- "ops/generalized_delta_rule/dplr/wy_fast_fwd.py": "R+CamNa7b49LfvrNZQs4roi+TReWcLGvBcoT3DbhoLM=",
162
  "ops/generalized_delta_rule/iplr/__init__.py": "6Vg8MTc+Byrnmx3HJz2yySi9nk2kyKCSg7aL4CMJ+GA=",
163
- "ops/generalized_delta_rule/iplr/chunk.py": "BsCf/fiYn3wpS0eDPrR+mCrqsAihXD5v/QD2QkA5ATc=",
164
- "ops/generalized_delta_rule/iplr/fused_recurrent.py": "nNo+ww/QmydOqBLnpmxXHAbANGDPTeWGjFI7nA9Vrw0=",
165
  "ops/generalized_delta_rule/iplr/naive.py": "YotccLOueXwBcQys4ICmFAysSrX8fj9eRasXIpmpRMo=",
166
- "ops/generalized_delta_rule/iplr/wy_fast.py": "SoTcF2asZEIAnZS1qR9PXHGkHwnBspjuUsz6RpuqNiU=",
167
  "ops/gla/__init__.py": "PTUqqQlC4cXrhRhG/KRx+7X3m9WJKxGyzrIB/WJEXSo=",
168
- "ops/gla/chunk.py": "B7reIP0UzTvYgK8Ds50r0uABlaucJWm70JVKKdYe1L0=",
 
169
  "ops/gla/fused_chunk.py": "OjzKcjSQujVQ+Q+d1Lmg4tsTzZaK6xWs8AGuplRn5zo=",
170
- "ops/gla/fused_recurrent.py": "ZIzMstFj8WAXagDzTlZo3hFZy0k+egHNg8Qr61sb4vw=",
171
  "ops/gla/naive.py": "rEuH5bEAieya1CTvtZbebVFo2J5LdOX1ztL1WiDkZB0=",
172
  "ops/gsa/__init__.py": "9uygbV6orNtaTkZl9Hz+aAp7EvT6RuffawfeFtuUNEE=",
173
- "ops/gsa/chunk.py": "SsfwmTOpKRG2Fja/lArJkw1teaomk/Vgczjr2/Oa+l0=",
174
- "ops/gsa/fused_recurrent.py": "K7qBWOmh0uL9CO7D3pSlrxKFHy1fobTUyTFV5p7okXg=",
175
- "ops/gsa/naive.py": "rezNszsU38KD1iHBW+ErV/Z01T5LIPadEjJnWiXz9LI=",
 
 
 
176
  "ops/hgrn/__init__.py": "69DMv7FQYvobQTXqOFtu7muAt1k1k9nvsFXcMAmjQmg=",
177
- "ops/hgrn/chunk.py": "Br3GeHkw1EcOF/4X2BOvUTclhfLDZwkthIshoHTMMqg=",
178
- "ops/hgrn/fused_recurrent.py": "KvO/5l/3wnJiT0jT0UA9IYxQhGHJy8jC7lYaOOyLM1w=",
179
  "ops/hgrn/naive.py": "XKo+5zJ+G4elkgK5ARsoXm5WYkG1QidTKNIuPT4k588=",
180
- "ops/kda/__init__.py": "JFZPAQH4eiYFbpBh7Xccrw2PbZoAtNve1wHqdSW0Wss=",
181
- "ops/kda/backends/__init__.py": "pk1xLku49AblKggFmQjmGlr1oNqUgdzxggzzgx8yeZk=",
182
- "ops/kda/backends/flashkda.py": "g63Eq+K84JInsin5NVb428RIRPhL3wH+6Y4a+OHSFqk=",
183
- "ops/kda/backends/tilelang/__init__.py": "Y+1//U4pzuPg2mbHoCymDfs186j482cI85ZOPxi88E0=",
184
- "ops/kda/backends/tilelang/chunk_bwd_dqkg.py": "Fk1eT3M/KPFvfVRrJQ6C0158h5GaR72YK3tgSxkKoUo=",
185
- "ops/kda/chunk.py": "LnoIH4MI8gHR8613fOKLqJGVNQkbtzjEJ0R7qX8/kQE=",
186
- "ops/kda/chunk_bwd.py": "AahkP3AepxWsoyGCZHlwUGc7rGIHJh68Im5r/Zs3MK0=",
187
- "ops/kda/chunk_fwd.py": "wUTWWQVWLRGeDj9e7dO03+0IosaRuWAfYqsl2KBy0ZA=",
188
- "ops/kda/chunk_intra.py": "Ao7C9D/HzYJkiFxIL5s/LIlVE2YNgpL9yASJl5giPOs=",
189
- "ops/kda/chunk_intra_token_parallel.py": "ENjjhDAR0cK2mqoU4q7QpBMPHmICgoLO06e4dEnd5hY=",
190
- "ops/kda/fused_recurrent.py": "r6ZJMiLVmCRLpbYXS25mIX58KU2yKtDJqGwwHTXY0MY=",
191
- "ops/kda/gate.py": "5056ezuomW2CNvPE04MraGJtf0gHvGqicxin1H0DICg=",
192
  "ops/kda/naive.py": "YKMihdS2cGj/YztIu+irMQKAZtJPANJ+EhmaiPxz8BY=",
193
- "ops/kda/wy_fast.py": "9pMMqS5E/l0mFV/Tyap1QKY39CB5Iy94/Yn4s9WzjUw=",
 
 
 
194
  "ops/lightning_attn/__init__.py": "FPVVYNLjfO3GJQYIp+4HCQ4MdJtF8UZx5yktWDIatFA=",
195
- "ops/lightning_attn/chunk.py": "kFjfxpUOVj61TnH0vXWZhDnw318suRLhtT2Qo50pkKM=",
196
- "ops/lightning_attn/fused_recurrent.py": "SsJ+HC5opio7H9LsPWyrbnPOoK2EvOcxLuaq8Vp69GY=",
197
  "ops/linear_attn/__init__.py": "3OeyduLmyi1manyo7FQz1ZLSoLgEXAKbHKn1uqDcK90=",
198
  "ops/linear_attn/chunk.py": "5xqgOnPPA7nGPt/Rda4wOxSljFTD3Izw63VpuDLJNd8=",
199
  "ops/linear_attn/fused_chunk.py": "MD6FycgZSdn8jV88UDvxpioPAw6hCxvsSq49qNIKpmo=",
200
  "ops/linear_attn/fused_recurrent.py": "jUZ5gPuN2VngqbebHbN0lrIAOiZOYkxIku+C3Eh/9Vw=",
201
  "ops/linear_attn/naive.py": "46BX2exbemO1c8XXVhmkz2m6H1ywTmrvT4/odStEA8g=",
202
- "ops/linear_attn/utils.py": "7X94oxSkHAGSpk7SQ5izs5Q0rYEDZZzUV/RorFdzY1o=",
203
  "ops/log_linear_attn/__init__.py": "qsAhfXSfoQBDmP6Li6+CjNd9ysMDGjcbYLonvcZcrjU=",
204
- "ops/log_linear_attn/chunk.py": "NTaU1ErT6E0PzSt9JL9KayX8dQgLG++85PFwj3sRm1Q=",
205
- "ops/log_linear_attn/naive.py": "xmXXuoIdEkTucVYJ/s4dTCvMWuJuO8KP0299J4jdtEU=",
206
  "ops/mesa_net/__init__.py": "32w1RDjxggXJ9JobAR01dDnN2UZSG2Fui7qePyQT2J4=",
207
- "ops/mesa_net/chunk.py": "GTupTJ0BI3Ix2giVi7sMA2ocs71yvZ4uuKvf8rkUqKQ=",
208
- "ops/mesa_net/chunk_cg_solver_bwd.py": "MvXMHU8MSOTNG62zURV+iMpvkwEh4eCo7epde0DIvBc=",
209
- "ops/mesa_net/chunk_cg_solver_fwd.py": "KG4SXzD6dZomzRmYmqaWtmBoYVgSk6Fxda8uXjJc5go=",
210
- "ops/mesa_net/chunk_h_fwd.py": "F3AUgFAYjqUAxslqpjsv+zW1PjdxGnwaU/Qjh5tT0iw=",
211
- "ops/mesa_net/chunk_h_kk_intra_bwd.py": "17Le7w0rK0MrLUROFQqcZ9yCQfxpRYMhe5o3dRHGIfo=",
212
- "ops/mesa_net/chunk_h_kv_intra_bwd.py": "mQnvHOYVu3HMX6IA4rD2zl+9w5BOi2yzFsr0GSo5+Nw=",
213
- "ops/mesa_net/chunk_h_kv_intra_bwd_separate.py": "FOrwfCVxiMtLYaR9JIBqzxExwDWMWPVuOkaxgHD1AgY=",
214
- "ops/mesa_net/decoding_one_step.py": "ZFuKB5H06F+uDagQLPGiu+Qk/qpZxjRbKcnuqWtC6/0=",
215
  "ops/mesa_net/naive.py": "zv3jT+71UegNb0JyIKnPv8OddyzHu4weEETtt0nJvzA=",
216
  "ops/moba/__init__.py": "P7ARNbLLMsmTtxcxPk09NPmjcvHGwWCmooXPYvT6ANk=",
217
- "ops/moba/parallel.py": "8qUN2zo88Pldh809UyDowBVj8kCNplsS00fIxLRdSyQ=",
 
 
 
 
218
  "ops/nsa/__init__.py": "6Mw2zDl4Z74QXVjME1d88PMSKTSO/AYUa00lW/IK+eI=",
219
- "ops/nsa/compression.py": "XjFqvEXvuIEeCBrJU+YzhWRH5UAkPGcUX4Tz/xx2M6Y=",
220
- "ops/nsa/naive.py": "5A7lyoN39oK0Wc+Yzm/dQbNEtlaGmZfJsMDv8II+m7c=",
221
- "ops/nsa/parallel.py": "BLaPXXsO8tcAJ79ltV3xwX/1qTganF2Nh+Lto/3zvyQ=",
222
  "ops/nsa/utils.py": "lbhybLC0CqXTn6liY8d1sQfTdZ2Gl+++Y5Go53OX4xE=",
223
  "ops/parallax/__init__.py": "g2pTOybF7cyoyaMAku1gpjV5MBY6s/Ll1X2NT6/w/YM=",
224
- "ops/parallax/decode.py": "qHX0Le4hA66S2LLTcMa+p0VMB0uWvhYGI9urSPLrB4I=",
225
- "ops/parallax/naive.py": "XEvAU0cSJtunA3ElSy4GFs6idarjK7Vb+xkrDKSp2OQ=",
226
- "ops/parallax/parallel.py": "ZrLKoopSj60DSxmqbaS3MIf3AUyEpYh7evJPlTUN+AQ=",
227
  "ops/path_attn/__init__.py": "eyvdV5LkkxztzDTDYAl862vhXITUOcMtF6D8n6n0O/E=",
228
- "ops/path_attn/cumprod_householder_bwd.py": "EZ0t+c2hG8Z/0IvrVxMBLDgO7zLgJsI9Bc7L0WYgvh4=",
229
- "ops/path_attn/cumprod_householder_fwd.py": "UF1wvSIMRHeJmPf5qbFuxbC+qCm3ipAfeAVf3IEOA6A=",
230
- "ops/path_attn/intra_chunk_preprocess_bwd.py": "HMQYfpKCHEMmzomrzb60u3gJK419yOAg2kx9XolFf6U=",
231
- "ops/path_attn/intra_chunk_preprocess_bwd_prepare.py": "6XIZztbrFYFR/7XJSIy10RxKid3+6dcZ5c+A6XL2fBs=",
232
- "ops/path_attn/intra_chunk_preprocess_fwd.py": "U8P46fLq//0vDN0SiE3cwI4n6FbAO6/UGNhfOiXegRo=",
233
  "ops/path_attn/naive.py": "4QsKiZUcF0q4IJbrku4Xu/IR3YBg+n6Epio7FAOW9lk=",
234
  "ops/path_attn/parallel.py": "V1c/T2VKbs8MgNJ0AT2JA5ULpx49aLb5maoxizAbVjk=",
235
- "ops/path_attn/parallel_path_bwd_inter_dkv.py": "6/EOBq9NQdKaCrEGwPwbi69vKbOoNCsKU1q2vRv7YFc=",
236
- "ops/path_attn/parallel_path_bwd_inter_dqh.py": "fp1NEXRSVAqhkmGcvoNfQn2pt5dtD0plx6W0TGR2xDw=",
237
- "ops/path_attn/parallel_path_bwd_intra.py": "+3QE/gzK3fAYqgo/WNHNC5LV8NJrL4+UHjDN6wOd15w=",
238
- "ops/path_attn/parallel_path_fwd.py": "cx6fGpfZmEuxHhl81gsLOBAYa61ZnkI9usqTakOIV7A=",
239
- "ops/path_attn/prepare_k_cache.py": "LqEoXyDlVfIaJQhrmpoS3pmetIaavAm+SmIm1NKFohQ=",
240
- "ops/path_attn/transform_q.py": "uZ9u6JDzXFKkWiim4KUkXWoAHYKlSDK5L1fMBNFrEGY=",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  "ops/rebased/__init__.py": "pawYkzOJ/mQY5Qj0Ibuq81P3KNgO9Lv6swOxCjlDlGs=",
242
  "ops/rebased/naive.py": "XOrMn2Ohd7MDJ7QvP93dPfjv/6prnZW/ozxHtU0KqWQ=",
243
- "ops/rebased/parallel.py": "+3WRG3oxV0T78e8HWkN32Q8KBe0V2GMQ/OpxowWfXFI=",
244
  "ops/retention/__init__.py": "i6uHA1ndTR6T/fVfooceKo12wyBFGYLe1EAm0Mo/sg0=",
245
- "ops/retention/chunk.py": "x0fX3U8KTjr9WLQK9+2C6Mn7cYNtACQ8RhFuUoCGpBw=",
246
- "ops/retention/fused_chunk.py": "sAmIqFiH4IFM4yiM1G/pNvlsZxLtkORemYy5WW4i1pw=",
247
- "ops/retention/fused_recurrent.py": "beES/zDbxCUWWWL9HSCc72a1+DWVvDKpBIsjKjkUet0=",
248
  "ops/retention/naive.py": "MG107SmOiRKLTZ8ziqaWN85nbgIPj/GqNJJ7KMt5Tag=",
249
- "ops/retention/parallel.py": "gTnLcV5w17fZAOJyJttw52LGAe9XlkuS++lMGTZ3vRs=",
250
  "ops/rwkv4/__init__.py": "GnWakdtZiQJ0+fTYxqqIyosoPwjH6Yp8P1AbpHIDnts=",
251
- "ops/rwkv4/fused_recurrent.py": "0fvq/4+NZMq6tzq7oHMUCaPg10qOzteSKLj5cmQKhvk=",
252
  "ops/rwkv6/__init__.py": "wPYgC6wG+xc4I2vessGhv1EaZ4tL5yuVQVSwi1rEEUk=",
253
- "ops/rwkv6/chunk.py": "2VMAk6p0tKEudhieXLIg2bYg+89JbsGUiuhFpfvx6C8=",
 
254
  "ops/rwkv6/chunk_naive.py": "zyj0Og15jGxNMzmlD9FoEdf/odzu3tqio/jiceVMNk4=",
255
- "ops/rwkv6/fused_recurrent.py": "wMTP/Y17ayzQXxXw+fN8P/FB5KGefLB4bB0wJ9kDg6Q=",
256
  "ops/rwkv6/recurrent_naive.py": "/lCgua1vSaJDqA4tOWWUaVxol5Axi3cOcjI+mJj7OAM=",
257
  "ops/rwkv7/__init__.py": "dPKBISLNnwlKb+xp/8ejsc2peC9gKQ3NedtMT5wQ1sE=",
258
- "ops/rwkv7/channel_mixing.py": "+XrwuE2RjLxjsiKrdhssskBTvW/zL9RpyFp85uv5J5A=",
259
- "ops/rwkv7/chunk.py": "9+rVTjACj0FP2Mnwo30DHWIHkpZG6YfYN4gE6891Oa0=",
260
- "ops/rwkv7/fused_addcmul.py": "uUzh8wc75jBLJhCagQfeE/yor25Jw34d9kQQNsQWg04=",
261
- "ops/rwkv7/fused_k_update.py": "CETn2jde9U8K8kfVihcyjARgpcDT9nN09qw9wM1QQac=",
262
- "ops/rwkv7/fused_recurrent.py": "nnNrjgLS5nv9plvU/MuMjt4/vDcGD0Wax9Dfigvwmp8=",
263
- "ops/rwkv7/gate_output_correction.py": "Ah6eqJrlTgFGaGI711+pMmWlxCOpvHrExx8H9U+nO0Q=",
264
- "ops/simple_gla/__init__.py": "sSyIh8oSfhyZI+VnYUM/uhIp+HL+gDcQBReLn/nJ5OQ=",
265
- "ops/simple_gla/chunk.py": "eA/L+T+42GiIhYu1oG0fM66QFGnzW9Uz8oc2SnLTKig=",
266
- "ops/simple_gla/fused_chunk.py": "eCALWbi61vaG6zPewlPmATvjlidM9lso9xQC8H4xAlM=",
267
- "ops/simple_gla/fused_recurrent.py": "Ab98sk1dnnFQmBmueO3D95vUs0zK8PC30ZRJYEGaIOM=",
 
268
  "ops/simple_gla/naive.py": "g3fAN++vd0aWvZcdOW7Y0boMYcs2mOld8kePWRbl2R0=",
269
- "ops/simple_gla/parallel.py": "hB6Zq+allqAYg/H8/9kpk0l7bsZCK9IsITkfLIhqZQ8=",
 
 
 
270
  "ops/titans/__init__.py": "4t9OK1aYdW03I+sUlfUjj3ALTez1M/XFD/H6KzeZtGc=",
271
  "ops/titans/log_impl.py": "Y5tMWJgtVE+GX394DdxQHd6Yirrg3sLx6sinMdQvkAE=",
272
- "ops/titans/naive.py": "NUmchQQpsxFwNaEpSQbwZ/638zSr0tuy2xGa3DD07+A=",
273
  "ops/ttt/__init__.py": "cFaLRxAWRsKjcmXh01bJbh5U59ZaFaM7DwYgm/KBAbo=",
274
- "ops/ttt/chunk.py": "xk4Cw5LLMUKTauseXa+oybDIOp6A0e+McU/vqzp3KhM=",
275
- "ops/ttt/fused_chunk.py": "KDdTpBvHj3jcIm2EkkNZTxWkhGbf0qvSQYEFkTwf+d4=",
276
- "ops/ttt/naive.py": "IaJu/Zd1/PgFA5Bnbo9WQkpdz6BPZD0bl8iGKkWQlCo=",
277
- "ops/utils/__init__.py": "eUiV7OAFYzGDyI/WtrSNltVrfU3crATGfKFWo9U87Hc=",
278
- "ops/utils/backends/__init__.py": "Bfq8p7M9iRlUzfIfPTlvsKd/LMutWWtEzp2wfwOOK54=",
279
- "ops/utils/backends/triton_ascend/__init__.py": "rpIPSLMvhpuf3aqxxDUGQrrq6dKKF5Nk2qsJl+H34Lc=",
280
- "ops/utils/backends/triton_ascend/cumsum.py": "AOxEn57vvwiNwOaDcVK3KbP+n5IUGrsbUWkG+kkukBg=",
281
- "ops/utils/cache.py": "pksJ/9P1Gu1UetclWd7pTLmur9Kl8CxsjmCm4CAUepw=",
282
  "ops/utils/constant.py": "uVK9zwOd7kM/DPSQk8cllx5nkYeK0K/KZVyCu0ZFdPg=",
283
- "ops/utils/csr.py": "comXJRo/nULFkkZk556qHNxBfWeXk0EofLnQ69EIEjw=",
284
- "ops/utils/cumsum.py": "6oQ1YDSIgMFZOZRn2oX8VKy/O+B5mbVWXl6146frCmY=",
285
- "ops/utils/index.py": "BdattFUI0BkVExltE0Zxl/BNO9Ldp6hSUfU1zbM3710=",
286
- "ops/utils/logcumsumexp.py": "7uEgeqUuLxi06jN/hOcTHvqJEITDGXECjNzk5SKPce0=",
287
- "ops/utils/logsumexp.py": "7g4hdIjRKkO8CylqAs80XR02A5PyIWYxrKcc79qRcy4=",
288
- "ops/utils/matmul.py": "TOLhNlPSm2JJeFmVsgh5D3t9LPUjw30w+mq5TIHef5Y=",
289
- "ops/utils/op.py": "nFuDfGU88dXF1Pe8xrcxoda8QWZA5QTojiFsse/53fQ=",
290
- "ops/utils/pack.py": "XT3/YxB71p6VO9oXPkpD0te42kbwU0dFaG3aZawdBHQ=",
291
- "ops/utils/pooling.py": "LB2lncX1ThcjDIEWvMwiQpW2T8KE/AQ5dTqcJC/hNhs=",
292
- "ops/utils/softmax.py": "uq/rygFZaAImpnHgOmAN9D+jrq/m3WHTyu6QGQR/EoU=",
293
- "ops/utils/softplus.py": "tdlxnzZRHWEZWhVl9uFVl/EGQ6phOBMEEwlbCtu8jz0=",
294
- "ops/utils/solve_tril.py": "oy0zat1/r4IjvqQ0MkJbrn1MYoayluYORr3nvX7z9NE=",
 
 
 
 
 
295
  "ops/wall_attn/__init__.py": "r/FbDGjGO8WK1DaMbl9axWHpENUa3pzSc0v9qviHJ6I=",
296
- "ops/wall_attn/decode.py": "JphI1kWj0Wwu024gnwgUWrWmhkAnKloXi1ik6cDpRrY=",
297
  "ops/wall_attn/naive.py": "9YKwBSvQ9imy229T5KMYKpuVNb+xEzhXsXdMemRWqjQ=",
298
- "ops/wall_attn/parallel.py": "oa2UJtSi8ouSG8g6bUWHwOehbwLH74SSHIU8LUiwVrg=",
299
- "symbols.json": "0pgr+/ByS6ju3XG9D6VWYAcRncaie27x08XB4GIIXKQ=",
300
- "utils/__init__.py": "bdtrr5b2e7dzcO88Il6bH72LypiM103tHkbY2kPoaeE=",
301
- "utils/_compat.py": "kYAtqK4JdTczM1FpqT44T/mILN7DcnPb8C4uPnO4Ho0=",
302
- "utils/_config.py": "l1PBnf2C6SABltOAhs9GB0GTr0olQ1fA7d/bhzTb6IA=",
303
- "utils/_decorators.py": "8OxDii2b+D+deTCQLPK1sdFx7McS/Wi9+AO67mlLrYg=",
304
- "utils/_device.py": "yXcM5ZkBLCOvHl/Dh25SveTgbSemR6PkFAl/UabLYWE=",
305
- "utils/_testing.py": "eFchc/74QJDXWv5T1YmIZFzRRpFWh+3gaYpQ1doztzs=",
306
- "utils/ascend_ub_manager.py": "TcjVL6L7YiNwsa7YZ4+9KrIPQ2RmG7PJCfmXYc24uEc=",
307
- "utils/layers.py": "afB10gGysmmVfVRlPnduPpuvcdAeDAstmqPC0sYJbRw="
308
  }
309
  },
310
  "provenance": {
@@ -314,7 +355,7 @@
314
  "dirty": false
315
  },
316
  "kernel": {
317
- "commit": "fe2bb66fee6c2a77eccf08a0508dc57428d488ed",
318
  "dirty": false
319
  }
320
  }
 
1
  {
2
  "name": "fla",
3
+ "id": "_fla_cuda_faa8674",
4
+ "version": 2,
5
  "kernels-minver": "0.14.0",
6
  "license": "MIT",
7
  "python-depends": [
 
14
  "digest": {
15
  "algorithm": "sha256",
16
  "files": {
17
+ "__init__.py": "eSkze5EKF+DhheKdXmGOOQPa6oJCFDY0fw3vg7WX3mU=",
18
+ "_ops.py": "q78tXrH/5M5hfjOclPcmC6NXw+I2de7vH6gfnXW225o=",
19
+ "backends.py": "h+9RflmubYjIEs0Fqhadj8SzFXHr+ghgXBmqn5Tm9v4=",
20
+ "layers.py": "MogF8nJKBsgqJjVXLjVFJHBMakhJCkK2of1ykPWF0yA=",
21
+ "modules/__init__.py": "rq5MxYJ+DgZLmrKdBb/PB1Q6PcuZ+rP0btubKE7uMdc=",
22
+ "modules/activations/__init__.py": "kjYo6ny71n6YPTd5Y6FrBPehfjS1Tl6rZh1QYcflZ9k=",
23
+ "modules/activations/ops.py": "toY9DY+ZmrAgJL4e8QOVKpYuw7QSYLUTxxCU3m+8ZaQ=",
24
+ "modules/causal_conv1d/__init__.py": "Z439ju3G3EQsurHFMVv0MQkKktuFw8/drnyp8oflj0Q=",
25
+ "modules/causal_conv1d/backends/__init__.py": "ZxTK7vZktZ7TBQB/5oiJr+oIypQU1cgkbn/ybwJl450=",
26
+ "modules/causal_conv1d/backends/cuda/__init__.py": "erS68kB9gBCaE0rX1ES+sKr8FCKE9pH/EB3rPWCrYCs=",
27
+ "modules/causal_conv1d/backends/cuda/cp.py": "s1x3wm+BuGbY0uAVGw+cLUBOtuixmZH80EFiyYLEIvQ=",
28
+ "modules/causal_conv1d/backends/cuda/ops.py": "73qKlHa75wF92ugakgiqbNYthaEDltGtAqz7ClVMsu4=",
29
+ "modules/causal_conv1d/backends/gluon.py": "KtMSmMOGSiioJGcWT237Uj27ryoZrsaNMx8BoOqAf9U=",
30
+ "modules/causal_conv1d/cp.py": "UGe4T/utfJ/mjhPM1WDnCJL8q/I8c4rpBQUDbRFjXyg=",
31
+ "modules/causal_conv1d/ops.py": "vEvfb5BXlVWOqXzzf5Rt0WcpEbjnfw/fYnOKxi91NmY=",
32
+ "modules/feature_map.py": "1+D3QXgIAEspjPM3aZ4ugVRSPeYEWJ+eqN3djUyQofk=",
33
+ "modules/fused_bitlinear.py": "cUFUweFZFmJS/Zl+8Ex3Kit9EHJJkcWabyH85wuwtDg=",
34
+ "modules/fused_cross_entropy/__init__.py": "nLRzNtHqTxjxgQT0EIs9RzmwfAu6baobksBU/Znd7rA=",
35
+ "modules/fused_cross_entropy/gluon.py": "qO/HNquDhMHIqN+59VlJcC37FwcqDaSoIz3mZMoH2Po=",
36
+ "modules/fused_cross_entropy/ops.py": "EwWAwIVHIOFrB1QPh335jjlvDYfVPEN1K9FjVtGxFWk=",
37
+ "modules/fused_kl_div/__init__.py": "L6dw1Iru9kTpqpwGWP81uMBUCf719A5eF+7awXroNYs=",
38
+ "modules/fused_kl_div/ops.py": "BDp6i7k/mDtwCi57T/ZKCUFS0lJpVm0vktiGH76bTy4=",
39
+ "modules/fused_linear_cross_entropy/__init__.py": "h0f6nBT3ezaBQExjxH+0ZpIfV/+jzp0cHNfvTkLQXYw=",
40
+ "modules/fused_linear_cross_entropy/ops.py": "qzLHd+ePGPNxynsHJiZXuQ5fGxTbhOR/q/n3ACAEZgs=",
41
+ "modules/grpo/__init__.py": "/+3ocSnRFVx59pEfhKGRhjw5SQctEOle3KwGSHUmhrk=",
42
+ "modules/grpo/ops.py": "58V3OfIwt4dBXMCvqaiBH6om10HKECwVeSHJ0h+XuzE=",
43
+ "modules/l2warp.py": "Tq8EvtlI+6mDqVhjd5tYYZgzCEuBCL0LJWWNFdKjjRw=",
44
+ "modules/long_conv.py": "EdjKO2tZyPuJVu7uvCXQTHINGySbcUb7lu6PK5kiKnE=",
 
 
 
 
 
 
 
 
 
 
45
  "modules/mlp.py": "q09Zw5Bttqq27hnlybbQ9ncr0jSw1UCrJH2Ewh9roV4=",
46
+ "modules/norm/__init__.py": "pjZst4x0d6Cupu9pETPPzEv/YIZOg2s5a2IOEwpAbZA=",
47
+ "modules/norm/fused_norm_gate.py": "Ex+3MGO0fgNz34LBJPxE77oL9JTT5Kw9TEUG6YUNAqs=",
48
+ "modules/norm/l2norm.py": "eSt9ICshJk489wkyGAPtmFqv678u6Iqj02HXtB+BthQ=",
49
+ "modules/norm/layernorm.py": "xqeuqNBIH79m7UcrlPlPjAZCzTc7vmfNsqNFBLH8xXs=",
50
+ "modules/norm/layernorm_gated.py": "6bzsO2HBwOU32pUuiiFwDsobrMNhABGPJguGCGk7VTg=",
51
+ "modules/norm/layernorm_quant.py": "BZHD/kPfNszPl0JsW9j+ZUOnHQOH2LwdaRNj9pv/o/A=",
52
+ "modules/parallel.py": "so6JnuagTE/nZBIjhNs/mwhcZtAbRr+DEwDCT3yLB30=",
53
+ "modules/rotary/__init__.py": "Vijv0n40cGYaaX/x3M1CsnLG6gjVxsmjTflZLxzNAq4=",
54
+ "modules/rotary/ops.py": "EGgH28oKJM5lcMXl/5tTD+j4Yxfq/L7n8s/jZA7rAUk=",
55
+ "modules/token_shift.py": "SiiXroq05nOI+fYGTpMxP/J7M3VnGonWr6eF6bWspI8=",
56
+ "modules/token_shift_cp.py": "q0OchsRLmUl6mSEdDgtJUpT5Y3HA4wrpUiSWHY3Hcyg=",
57
+ "ops/__init__.py": "c1AozqIP1VhM9A/lV0mO/nrHrTkbgaNn2aSyM6y0CCc=",
58
  "ops/abc/__init__.py": "WwLiIkYi3xJ5RiG/bOCNNRaeeqmMuIPT6n1g23qfpDg=",
59
+ "ops/abc/chunk.py": "UiMPJJgp/whOIdGxHx9ZkfKgbM8FCMEb9rd4oXXQ3cQ=",
60
  "ops/abc/naive.py": "AZ3uz9eDvycW3gTwZuv9J4k86KndfQMZiLBFyPmzsLw=",
61
+ "ops/atk/__init__.py": "7D6lvGGxM+O5oAsRP2Rs9x9fFh48dBE4RuanHM0lCsM=",
62
+ "ops/atk/backends/__init__.py": "k/wccBdiH8LsLHjs/LB5l6utDxQ1xwxehzK1wC1Cdl0=",
63
+ "ops/atk/chunk_atk_bwd.py": "KP9Ek4fmTbKxre3TlqhoC3EzkdnI0DbVw55/gK8cndY=",
64
+ "ops/atk/chunk_atk_fwd.py": "BRYqeWa2M0n2gvkNlFfIenhyr0hcMP3BcoeiojEqUQg=",
65
  "ops/attn/__init__.py": "gU/67mJonbeXpzfxgi8K7HIG2a8Xpjd0gOuCk/Pu9EI=",
66
+ "ops/attn/backends/__init__.py": "YSU1FRHkTNYuPlHp5Ky5ZC7OgD0Ul32qARLiXPf6qoo=",
67
+ "ops/attn/backends/tilelang/__init__.py": "VJdfMdLIoy9BWt49a6z0mnyGiFtIBmzHFdfHRjiBYrI=",
68
+ "ops/attn/backends/tilelang/parallel.py": "MvrzoFaTR/z+yljm5MA1Piq5CFIT36ku1bMVYchAQrU=",
69
+ "ops/attn/decoding.py": "J1up9AtGqemUJel+bHCbY5tXQBn/p/dy9K0nwlV1EMM=",
70
+ "ops/attn/naive.py": "Ns1vl3PBVEcW7+4kwW87ATCccIcghu24uWr5fDTS0s4=",
71
+ "ops/attn/parallel.py": "bNsWwVHS7LNWtcpZ6F2aWLglgCVKcIjIA5P4puUDYMc=",
72
  "ops/attnres/__init__.py": "/ljBk1VNFjDa9XABPXqIiQ05vUdzRV6Dbx/KkQihrWs=",
73
+ "ops/attnres/backends/__init__.py": "R08rRiTtJKcbPZBFsllcEpnbT0bSzQk5NGRgn779j5g=",
74
+ "ops/attnres/backends/gluon.py": "3TwxEZvH8NccliN4Cu4PzsYYMiqObQwIqYOaKmmdpiI=",
75
+ "ops/attnres/fused.py": "RxdxfbEKzeZ0rvieg3/4nbnV+011tb9ZtVEOT46mnfc=",
76
  "ops/attnres/naive.py": "nV4WPz/vfRwfRsIzF7pKV95fEx2dQP4BrrPoQCh0a0M=",
 
77
  "ops/based/__init__.py": "qdBLUigpgjQaIN3HTtq57uxoqAaBnQkZbFiEIf18Rvk=",
78
+ "ops/based/fused_chunk.py": "gBng4smV8X5eHn0pgo677bsu8kvOZ+DNEkJbvegBxZo=",
79
  "ops/based/naive.py": "YWIFSLeslp2ruyQDz/uCr5V4ao2cyRyeQH1wiVLMB8U=",
80
+ "ops/based/parallel.py": "daBATK+vtrog3XYFx2WkTfyg0QPH0gGb/6zlaTZCeIs=",
81
  "ops/comba/__init__.py": "FBKonbvPPFHY7an2Auh1sXVJLbcgwX8aAD5fxi2oecY=",
82
+ "ops/comba/chunk.py": "dtxGTXfgVY0c8TMq6uSBDUBjaP9tNk5WsnScEyVDVpI=",
83
+ "ops/comba/fused_recurrent.py": "sVNBWn5bq7PI7QIpCGbDMGBfl0CMzDzv767eR+5jCiw=",
84
  "ops/comba/naive.py": "QH967c5rxlSSDs6J6jRtLjZ3iLiwgAYgY9+0m9rgD18=",
85
+ "ops/comba/utils.py": "1nCCwG8f9KZlupZOwb1M3zH8MkUKILB44GaLFxI8cUw=",
86
+ "ops/comba/wy_fast.py": "9cijtR5+g55508HeHN0nedcRcBu5doCZad/6b+r+NsM=",
87
+ "ops/common/__init__.py": "yN9sU6JLmt6oHmOBursxys8xjykn/RubQlhzf+F4hfo=",
88
+ "ops/common/backends/__init__.py": "51Y0UR2Ro+8SU+rWoHNKpGNcBVC+F0CjQ4KjKMHA9cg=",
89
+ "ops/common/backends/intracard.py": "mExR+X9YFuNXkoM3x5t/78kdzUk/ypZhBq+0XWmEZfc=",
90
+ "ops/common/backends/tilelang/__init__.py": "N4GWBK2DAUoBSuxrTKD4b1Qsb1BtGHQZqBpXy57WhF4=",
91
+ "ops/common/backends/tilelang/chunk_bwd.py": "v6bPgT1lCK1MBdsarkfulW9rEQHEVYcZdyWh7k4F16k=",
92
+ "ops/common/chunk_delta_h.py": "bwziSs86L78fXsQNPMnXOSCVL1h/g80uWd3dvwtZuV0=",
93
+ "ops/common/chunk_h.py": "nR4KBApZUosIPbKg+rOZ/fL1C7vKJqowY6tUJy1pRu8=",
94
+ "ops/common/chunk_h_parallel.py": "vLG7BiBe4WhuB5M8x30rAeqFquGerzwIkRB2V5IXwVw=",
95
+ "ops/common/chunk_h_split.py": "bNGwtZ1QAA/qhJ6mpgDWMcIK/3043cTzyMMKUyFCNpo=",
96
+ "ops/common/chunk_o.py": "gmMl3/PC5JD+VjhlmnbcEivMAB9qsMvjhys6LFg+5r4=",
97
+ "ops/common/chunk_scaled_dot_kkt.py": "IHtve82Yy5FmP3N8Tq4FvCrlmoqzF5+4fjr7rRYi89M=",
98
+ "ops/common/fused_chunk.py": "R5PRqQBs8L+LDM3u15SXusRYnrJiqjYII168tWNbkDQ=",
99
+ "ops/common/fused_recurrent.py": "ZtFhTDcrB4kzHOEnqV4glzLCUf1x6RO14C+rYorz++Q=",
100
+ "ops/common/gate.py": "WOCYhSul4pkQvNUX7Lco3RDFsf4Dt6Qk6UN1f5Rc8WY=",
101
+ "ops/common/intracard_cp.py": "FUJFbWiaKt/APVmS0TQyMrHZEVAaAi6eu/VM4jhBqRk=",
 
 
102
  "ops/cp/__init__.py": "uud2WkodJvH5U5DjBa5oSLJtZ1G8O4D4XEuspXyC9lA=",
103
+ "ops/cp/chunk_delta_h.py": "OHQD+CubC1RfbNqxsRewZVh/NSNqUMEOuplaNm9FI7U=",
104
  "ops/cp/comm.py": "lqyQDwmK19vowTtGJGO+6SEn/neFFE0G8xFvlxLOmb0=",
105
+ "ops/cp/context.py": "VS/fHKQ3dAhML1NkHpjch48iC1KJjE2KTBkL4rVlbF4=",
106
+ "ops/cyfa/__init__.py": "OAx8kAAAHpFVQxe3+SV2psaScEIJ5kbnAcOJkOrWo0s=",
107
+ "ops/cyfa/chunk.py": "QgjD/Bv7TlL0qLd17SLVWFs4xas+w61XQ+Zfq1yoPJ8=",
108
+ "ops/cyfa/fused_recurrent.py": "XWLzv2l5TOjGiEcx46f94VVcErCOOMO0+vDO+dn7GdA=",
109
+ "ops/cyfa/naive.py": "95GVYqF/wXwBfLsMf9x9lwJLiSk9LfPWHQyl1QGOYn8=",
110
+ "ops/cyfa/qk_norm.py": "phhdmmvyB6qRosmZC/ybLBKsb6UjXQj/QFU7gjoYe04=",
111
+ "ops/cyfa/utils.py": "U+R2Biu1tPo5GoIEngh2T1GebIgI1yK6gEDfX8qqNGw=",
112
  "ops/delta_rule/__init__.py": "1gIkBajhtbVwuJOgPQ+FYYdq4tYgNgib+veOnont+N4=",
113
+ "ops/delta_rule/chunk.py": "ueCVdrAgOpCxVVb7gDgihDRLQck9dY0dOQB/qdxHxGc=",
114
  "ops/delta_rule/fused_chunk.py": "2NiUBRAl98YgRVGr2uzYJV22pQW3WNOe6euTX1Mdqyw=",
115
+ "ops/delta_rule/fused_recurrent.py": "WEg6WYVHkdvmncKPtXfYky4skqIe5whzjrX9rE3PAHo=",
116
  "ops/delta_rule/naive.py": "IK4DL04IAZtCmMyfzyw5fYcfqsD9HxgCQ9n2makZKWg=",
117
+ "ops/delta_rule/parallel.py": "wR9GWtXrKRwFP+i1ewrvSuO9OJRq+HwYeAkF2pwlchM=",
118
+ "ops/delta_rule/wy_fast.py": "voSaPCMCS/G8crBpowSqB1KI1lQZZQdNuyRlmDTiGVI=",
119
  "ops/deltaformer/__init__.py": "51ZmNFoHy3P0lVn/2EHKu1QS2BMC6h+hnAwSIr9szYg=",
120
  "ops/deltaformer/invcum.py": "ren181AxlHBv2MoZIHmSGMmJ0sW6HDb3t8fqG7gDELU=",
121
+ "ops/deltaformer/naive.py": "W78CzYUyuxLY9fEkKghSh2dnjux0NOJ6pAVhIN7M4PU=",
122
+ "ops/deltaformer/parallel.py": "XMKE9sLFJl4UVspOXQxhefztzykDEUp7+S9GyUhmOwo=",
123
  "ops/dsa/__init__.py": "ZNyws889LPBiY8gwB5zTs2vQwJYV8bX5jg6Cldkkq9s=",
124
+ "ops/dsa/naive.py": "JKyMUzwK44XrwlKUWfLhU/iK5DlCsXceJuxtXrzaCQ8=",
125
  "ops/forgetting_attn/__init__.py": "A6ozg9zdEsz4lgUAXqEIUY8CeNp+5IZqYKOpChpj/A4=",
126
+ "ops/forgetting_attn/naive.py": "rjmhL74ja9e060lOrcRgMpAuwO5xDqBKbXw+YoE26+8=",
127
+ "ops/forgetting_attn/parallel.py": "nm4vnTE92QQIvgF+q80TGjweTw8YFX0tyN4Ip4vE7ls=",
128
  "ops/gated_delta_product/__init__.py": "0MAzYdxP0ODZjN4WNQ+3HLwigokyfMv15rXUpTX+qnw=",
129
+ "ops/gated_delta_product/chunk.py": "jbQM/fXoRX/nzNZdSgUB5NBB7DYzhQcabsDpuMUUbHE=",
130
+ "ops/gated_delta_product/chunk_deltaproduct_h.py": "w/+GVM+h+ffj5shd1n/rPqhhwIfccadOiUQPU6slGYs=",
131
+ "ops/gated_delta_product/chunk_deltaproduct_o.py": "HAmJ5oygn3R7gHe7Ur5kWAFf9sV3qtS57ZjML69FU34=",
132
  "ops/gated_delta_product/chunk_ref.py": "R1dHdPy4rU+lTQQKodAn9h2xPLxmwI2z0T6pRvh8Llo=",
133
+ "ops/gated_delta_product/naive.py": "bJ1Cc3mljHcAXjc4w1JDyxj+i4hmhCPsoELKrTkyvIM=",
134
  "ops/gated_delta_rule/__init__.py": "Xt0hhHr3CECyZDmBIZakdufk3bvvIa/V/iBmpkYwpnw=",
135
+ "ops/gated_delta_rule/backends/__init__.py": "iwpwhvQepjkDHC+SDA2b3SziHFeegB79Oz+pU+TsTl8=",
136
+ "ops/gated_delta_rule/backends/flash_qla.py": "b14UI75g/TZIsJLMQk5j71B4B2+JkmYL24zACdtDcRg=",
137
+ "ops/gated_delta_rule/chunk.py": "FJbbVRckUs3kIRXGbCppJdyOCP6/ThpF16ANRmSuiTk=",
138
+ "ops/gated_delta_rule/chunk_fwd.py": "35ZNk4cZW3CWG33fryWIcXLtJ4WGvQvKNonONIKCvWM=",
139
+ "ops/gated_delta_rule/fused_recurrent.py": "b4i7Wb0iuRjzwlXqmAb6AAZiW2o4hWVv5iDJ5dAuGpY=",
140
+ "ops/gated_delta_rule/gate.py": "qAq4nKirRZtlYtHYYbi/jXC8HZZiRaftCwu0haEgmhg=",
141
  "ops/gated_delta_rule/naive.py": "0c8XmSNJ/T6Ur5mbIuPTqBvkotGIHOW3CjRXJXFm4Ms=",
142
+ "ops/gated_delta_rule/wy_fast.py": "IayqzhuCGiM6lbjjWKAi1GU4IvgXnZr++xSNq5ZfSKY=",
143
  "ops/gated_oja_rule/__init__.py": "oShC/cL0jMy+xWR7YBV9QxkAdxt1xasXDCypGAU6Hgo=",
144
  "ops/gated_oja_rule/chunk.py": "lq+5BXEaJLfL4dqqcJb4TmtyF9OrP9Wh+JpRfpdzd2s=",
145
+ "ops/gated_oja_rule/chunk_h.py": "K6g7OMKN9/uUqboAelMpsu8jCwJ2opTKebyZiAEfIYE=",
146
+ "ops/gated_oja_rule/chunk_kkt.py": "9K2pSMxQ3FkmGgiH3JqbN90nixJymRK/x59ohwZYOKk=",
147
+ "ops/gated_oja_rule/chunk_o.py": "AxQIEqnIrWZQmuniM8gk1h+Ynd6d5saoXeJigp03ORw=",
148
+ "ops/gated_oja_rule/fused_recurrent.py": "iHdQaLJGJSLM8X6fYmOqtVe07o97YvidynXGewNG008=",
149
+ "ops/gated_oja_rule/wy_fast.py": "/+qHRfD2tvWfOfPHa7OMdFWezHLqjFnGJyhiUbF02u4=",
150
  "ops/gdn2/__init__.py": "xiEsgvVEfHhKcurQqL7ZKGGzEBW2BtGAylE9vxoNmAk=",
151
+ "ops/gdn2/backends/__init__.py": "d3Zf1qLfl/RHs8eAs1cGjN05HBvbxvoWdMke7s1BatQ=",
152
+ "ops/gdn2/chunk.py": "y4dx8rK1CBrPDQthDgnee9WAdWr3JYL5t+9W1/XKAbA=",
153
+ "ops/gdn2/chunk_bwd.py": "CGaUEbBKHR+yZJH8ILgOGDhbEg8S6i4SaI9hV/uXFow=",
154
+ "ops/gdn2/chunk_fwd.py": "GghmT44Q/8kQ1Jc10sGYeAcaKxmhldjJex2Gr/FSyec=",
155
+ "ops/gdn2/chunk_intra.py": "8wEbCvVZ/OTPPDyo9cu7R0WeRJZrqZqqLOAAFL0c2do=",
156
+ "ops/gdn2/chunk_intra_token_parallel.py": "W+tiIjr7D+nxq/4s1itDolgS7mya+LC3b9iJ1oDpMd8=",
157
+ "ops/gdn2/fused_recurrent.py": "TDAjD9IqdASKQX9FLLfSLFgsdTbDoE4jwnShmBQSpAQ=",
158
  "ops/gdn2/naive.py": "amWAVAHrMA34W2KXYRoN0VjdGDQjzyvNXg8Ozd4+nno=",
159
+ "ops/gdn2/wy_fast.py": "N0XZaivjPUDGmWPS8tyR/2tqedUcMtlHpaZRZQgn72M=",
160
  "ops/generalized_delta_rule/__init__.py": "cn6rKWEDrxWw428Dk6EEy4Y7nyWHpc8rtR+EbRO9ndA=",
161
  "ops/generalized_delta_rule/dplr/__init__.py": "F8TbaiZeRKjbKhlI4XYopNd4bfzV10feNO06i32h2Vo=",
162
+ "ops/generalized_delta_rule/dplr/backends/__init__.py": "VkuoS48+KY513u/s51Qpx/Qp4oSuzcMgoeqzc+9uqWE=",
163
+ "ops/generalized_delta_rule/dplr/chunk.py": "KF/hPswvDmMtN0+Q1OQR9TbpDOrMqAWSs5qPbuBOscw=",
164
+ "ops/generalized_delta_rule/dplr/chunk_A_bwd.py": "Sf4K4R7jzOqgTCo/fLhoT5evBHj03sn1qKuqYZLpt3s=",
165
+ "ops/generalized_delta_rule/dplr/chunk_A_fwd.py": "szc1lBh7LvMrdXft3OQWQ+vklQxDrqjmet3d+69VAr0=",
166
+ "ops/generalized_delta_rule/dplr/chunk_h_bwd.py": "/rB+pwDapn9SM8xqp1x3Ro+N4EuvgIoYhte9f1Wl0Jc=",
167
+ "ops/generalized_delta_rule/dplr/chunk_h_fwd.py": "KXBpSZVK4Qvp/2XTe8TVXQw6M8t06N3AHdlldP1+Yi4=",
168
+ "ops/generalized_delta_rule/dplr/chunk_o_bwd.py": "9NLZBaPZ8nlLSfFbbVpoUz3fM5H/VR9nRajBlY2Nx04=",
169
+ "ops/generalized_delta_rule/dplr/chunk_o_fwd.py": "2155nmxibRXn+jOz2MGsUnkdNhWsaHKFvOjmBAmcIq4=",
170
+ "ops/generalized_delta_rule/dplr/fused_recurrent.py": "QT9BmtI4OVj/Nh6Qo6UM3TYb9W+ZifSWHVnJFGKI4Uc=",
171
  "ops/generalized_delta_rule/dplr/naive.py": "4flLSZUtwaq5S/PH+fu04rzOM2+USdRX6Ofte4lanE8=",
172
+ "ops/generalized_delta_rule/dplr/wy_fast_bwd.py": "fcpJQxkNls5tEKkP9fxaSqrGkrdZyNzmruFfGpe7hCY=",
173
+ "ops/generalized_delta_rule/dplr/wy_fast_fwd.py": "aNodADVGx656FWS5f9e02Lb0lrp4syVsW8NR6zYZF1Y=",
174
  "ops/generalized_delta_rule/iplr/__init__.py": "6Vg8MTc+Byrnmx3HJz2yySi9nk2kyKCSg7aL4CMJ+GA=",
175
+ "ops/generalized_delta_rule/iplr/chunk.py": "YMAVurj1Jmp97+sWuTlzfUOaCUeKlhQzbSGXTzobwW4=",
176
+ "ops/generalized_delta_rule/iplr/fused_recurrent.py": "u5LKf/1u7v9MSnb3mixwWa6/5h7GdDBJ5CHf35Tv1gQ=",
177
  "ops/generalized_delta_rule/iplr/naive.py": "YotccLOueXwBcQys4ICmFAysSrX8fj9eRasXIpmpRMo=",
178
+ "ops/generalized_delta_rule/iplr/wy_fast.py": "hqVfLtmdlhQnlIeU7LrByAxBcNYtVSehVFPZW/o8ZhY=",
179
  "ops/gla/__init__.py": "PTUqqQlC4cXrhRhG/KRx+7X3m9WJKxGyzrIB/WJEXSo=",
180
+ "ops/gla/backends/__init__.py": "3LkZ6RPx/+FnCqaj6NQC8FovyO0XKlLVkrKopGEt+nk=",
181
+ "ops/gla/chunk.py": "5xTGVCzeQzxbSf8gg3HdcpBPCqOfMykToxXU1NI/S1g=",
182
  "ops/gla/fused_chunk.py": "OjzKcjSQujVQ+Q+d1Lmg4tsTzZaK6xWs8AGuplRn5zo=",
183
+ "ops/gla/fused_recurrent.py": "ToICyjG/Jl9CPbYKh/8XCmd+tC0Wuk+zQRrDczQ1yCE=",
184
  "ops/gla/naive.py": "rEuH5bEAieya1CTvtZbebVFo2J5LdOX1ztL1WiDkZB0=",
185
  "ops/gsa/__init__.py": "9uygbV6orNtaTkZl9Hz+aAp7EvT6RuffawfeFtuUNEE=",
186
+ "ops/gsa/chunk.py": "lAt7ynwdo/OuOmueHQR05KhPgCO+RtZzpPgQf8pz73Y=",
187
+ "ops/gsa/chunk_bwd.py": "zph8XOxa2WmwkAIUpIuLTjpSubQSp8uNiphJeeW+kfM=",
188
+ "ops/gsa/chunk_fwd.py": "goDc5m91bXe1spiElAX1fM5R04qTQgbbvdM7GSc+rsU=",
189
+ "ops/gsa/fused_recurrent.py": "BOxAXEEw6TwbMVpVLR98bB590Y6CrEOfP7/xFu1Ov3Q=",
190
+ "ops/gsa/gate.py": "+oIWuMo420dfL5QIwk21VCjkohKKmwtmoIEVeBdrk9A=",
191
+ "ops/gsa/naive.py": "8ECW81ylA8gLsR3svJsPYd6pAzW8ELmSMuwZzkCJmeM=",
192
  "ops/hgrn/__init__.py": "69DMv7FQYvobQTXqOFtu7muAt1k1k9nvsFXcMAmjQmg=",
193
+ "ops/hgrn/chunk.py": "odEz3XdQ1whBWOhYQfCVvMS+zy6QZ36hb9USuqjwV6w=",
194
+ "ops/hgrn/fused_recurrent.py": "SddIcmnP1KUcGWTk2rJGtPEoM5CnuV+zzMOUmK4pc98=",
195
  "ops/hgrn/naive.py": "XKo+5zJ+G4elkgK5ARsoXm5WYkG1QidTKNIuPT4k588=",
196
+ "ops/kda/__init__.py": "OsCs1naQ1tZfkuT3f/9QXuufPv4RKCM+Gya7U8easqU=",
197
+ "ops/kda/backends/__init__.py": "TtEq1fz8FfYrdqbSdZHPA9KNZXCgERZ8sINxDCYZxN8=",
198
+ "ops/kda/backends/flash_kda.py": "ncJlqPB9icS0hxx1gLWACHX1u9gWK9Zuz2OnLW7qSp8=",
199
+ "ops/kda/chunk.py": "Tc5Wt6dCdvv3AyIjn4HyiSNQ+PEhlz4vXd9oQOyDCro=",
200
+ "ops/kda/chunk_bwd.py": "/LwpwY5YXbqM7QLO2t0PogkAptPrR0y/tvRqVd0E20o=",
201
+ "ops/kda/chunk_fwd.py": "yPLK8XehVLVWe2dl5sX9eFjjxN5G1YX1rSKRyu8zLhI=",
202
+ "ops/kda/chunk_intra.py": "0iplpMZqGD//G1sLbMdY/hoksjAJu3qv0aBoOfHrNio=",
203
+ "ops/kda/chunk_intra_token_parallel.py": "a3dvImIfizoopyQ2i+fKtetL7001HavfOQENgqw16/I=",
204
+ "ops/kda/fused_recurrent.py": "KWsqM6LZXU2n7faYHMYMqwR/N1byanrjWc0R3Eogt6M=",
205
+ "ops/kda/gate.py": "sHa64RkSgT8vMYUiG0fYxmYQJMvt4mG2TKYCm+pa+nI=",
 
 
206
  "ops/kda/naive.py": "YKMihdS2cGj/YztIu+irMQKAZtJPANJ+EhmaiPxz8BY=",
207
+ "ops/kda/wy_fast.py": "PasjSQorAguMGZkPwmVtosiTUlZ8x9jLiCln/UtX7bw=",
208
+ "ops/lightnet/__init__.py": "xI7MhtSqpNXoMRr9nmXTmq8swM+XjxBEsMH+9VKqwqA=",
209
+ "ops/lightnet/chunk.py": "7He3VzwLRiJ7avtCMje5Oz+nuRkMmtMEzdFUxLyEh04=",
210
+ "ops/lightnet/fused_recurrent.py": "hzn/dfLsBVrfpO44OQhreQIzysWT0LYHBoIubvphmVc=",
211
  "ops/lightning_attn/__init__.py": "FPVVYNLjfO3GJQYIp+4HCQ4MdJtF8UZx5yktWDIatFA=",
212
+ "ops/lightning_attn/chunk.py": "w43Qdzp+ogeMkGAOL8ZvxVIFV/nBtlcN+5vn/oBhZCA=",
213
+ "ops/lightning_attn/fused_recurrent.py": "w98eqzLvLE4Gs8TR4iZvMl7X8mGjmL2+jSqTu0h3B6A=",
214
  "ops/linear_attn/__init__.py": "3OeyduLmyi1manyo7FQz1ZLSoLgEXAKbHKn1uqDcK90=",
215
  "ops/linear_attn/chunk.py": "5xqgOnPPA7nGPt/Rda4wOxSljFTD3Izw63VpuDLJNd8=",
216
  "ops/linear_attn/fused_chunk.py": "MD6FycgZSdn8jV88UDvxpioPAw6hCxvsSq49qNIKpmo=",
217
  "ops/linear_attn/fused_recurrent.py": "jUZ5gPuN2VngqbebHbN0lrIAOiZOYkxIku+C3Eh/9Vw=",
218
  "ops/linear_attn/naive.py": "46BX2exbemO1c8XXVhmkz2m6H1ywTmrvT4/odStEA8g=",
219
+ "ops/linear_attn/utils.py": "tjqU7ST991YBV68Zs83Fy/li6sUSMeKNPGE3VcYMvfo=",
220
  "ops/log_linear_attn/__init__.py": "qsAhfXSfoQBDmP6Li6+CjNd9ysMDGjcbYLonvcZcrjU=",
221
+ "ops/log_linear_attn/chunk.py": "21c+pLRtfBGC6RPf30KHsqafmCMBOcvL1SbjvNVUT9w=",
222
+ "ops/log_linear_attn/naive.py": "Ju7vJ7L6j1xSt2OrDHb1U8lXQ/G5c8TplDVwv2OGH6U=",
223
  "ops/mesa_net/__init__.py": "32w1RDjxggXJ9JobAR01dDnN2UZSG2Fui7qePyQT2J4=",
224
+ "ops/mesa_net/chunk.py": "53VxbqZux86q96Jk7ZT7iHkLBlcADKp9yZuvBDovRhA=",
225
+ "ops/mesa_net/chunk_cg_solver_bwd.py": "6dUL+GS455oU+M2QOvROmJTlkGIyiNV2qD2Z/KmlY8I=",
226
+ "ops/mesa_net/chunk_cg_solver_fwd.py": "2xUtPW36YmUfqpj1qQblJpyYlzLO/LXnPWbH4cHyLoE=",
227
+ "ops/mesa_net/chunk_h_fwd.py": "kUhk8XVi9GPrfuViwL4shPieN3ZCZs9hvqaT7L7Bf7g=",
228
+ "ops/mesa_net/chunk_h_kk_intra_bwd.py": "TzV3RiE5k3/C38J30lgLcoHHj1fWiT+39iHlZPrNLvM=",
229
+ "ops/mesa_net/chunk_h_kv_intra_bwd.py": "b4s2qkF5ANeggNE8HrNZT8LEfd02FqTLo1+KY4wJKos=",
230
+ "ops/mesa_net/chunk_h_kv_intra_bwd_separate.py": "6g3MpZX8313sLrlMhGuwA7MY2e+wVX56xgwqoWj3OZA=",
231
+ "ops/mesa_net/decoding_one_step.py": "D3QQNRDvQkfFnQ5CV77pK32GqM4nI8h5QqBgH95uBdI=",
232
  "ops/mesa_net/naive.py": "zv3jT+71UegNb0JyIKnPv8OddyzHu4weEETtt0nJvzA=",
233
  "ops/moba/__init__.py": "P7ARNbLLMsmTtxcxPk09NPmjcvHGwWCmooXPYvT6ANk=",
234
+ "ops/moba/parallel.py": "vlJbZdAJkBvuqbgjTD03lQvB5BK4kagpC1PCMYQdO0c=",
235
+ "ops/momentum_delta_rule/__init__.py": "5OaHW/StrtV3AXsAel5gY1tFYo/vIktNiATrRNV6C3Q=",
236
+ "ops/momentum_delta_rule/chunk.py": "PMijn0zk+fco7T3jTPV3AiGJjeL3c9wyxYGozOFRU4E=",
237
+ "ops/momentum_delta_rule/fused_recurrent.py": "gUZQM29isdhTDKa4eKL/lvzr2fZwe/iu2saM6hvanJo=",
238
+ "ops/momentum_delta_rule/naive.py": "p6i6fC2NbwyGyAa7BroNb//NKET5cWPuciubVEYq2Ws=",
239
  "ops/nsa/__init__.py": "6Mw2zDl4Z74QXVjME1d88PMSKTSO/AYUa00lW/IK+eI=",
240
+ "ops/nsa/compression.py": "XxTFQ7YicVLJ/RajeCgkQKUPKDq+t03lMtjgirTk7Mc=",
241
+ "ops/nsa/naive.py": "zJ1lQnQEvnF6G6j/n1HF31DDYPQOENL/fJfBlWM4a+k=",
242
+ "ops/nsa/parallel.py": "SQEcy8p6KEX+MbIRY+/o9IblXJPRTcMOMdJk9i4kSdo=",
243
  "ops/nsa/utils.py": "lbhybLC0CqXTn6liY8d1sQfTdZ2Gl+++Y5Go53OX4xE=",
244
  "ops/parallax/__init__.py": "g2pTOybF7cyoyaMAku1gpjV5MBY6s/Ll1X2NT6/w/YM=",
245
+ "ops/parallax/decode.py": "RMgM4i3NxaiktyPLxuF2wgPytE6Os7vbywv9DovgQVM=",
246
+ "ops/parallax/naive.py": "Bul9UJn1oJgW3Q2R04b/aQ87GIBx8Pu+DMalkXdmf8g=",
247
+ "ops/parallax/parallel.py": "U1/dvoFO4sXS3c13BHbMzbdUdM8rACR0aoGuS7cXxGw=",
248
  "ops/path_attn/__init__.py": "eyvdV5LkkxztzDTDYAl862vhXITUOcMtF6D8n6n0O/E=",
249
+ "ops/path_attn/cumprod_householder_bwd.py": "aheXzKc/5z9ug392HO9RvnRLfuoB2IqMDqJGCoQHdW0=",
250
+ "ops/path_attn/cumprod_householder_fwd.py": "Z+994gihtCIHIVOQDpsZG40KAY2h3QoPhRWSZ5xTM88=",
251
+ "ops/path_attn/intra_chunk_preprocess_bwd.py": "vznrWNVjKSVqJnroNX0rdTzZubF6gzBfIo30UKH/EAs=",
252
+ "ops/path_attn/intra_chunk_preprocess_bwd_prepare.py": "m8JxIsdrK/0RQgoA4pCpdvjTEz61TI1yZ2ga7H6NZfE=",
253
+ "ops/path_attn/intra_chunk_preprocess_fwd.py": "Dl+JNpXxX2WYfFWC6pGWSDnN1yJPJdHVLoJ41t/tgN4=",
254
  "ops/path_attn/naive.py": "4QsKiZUcF0q4IJbrku4Xu/IR3YBg+n6Epio7FAOW9lk=",
255
  "ops/path_attn/parallel.py": "V1c/T2VKbs8MgNJ0AT2JA5ULpx49aLb5maoxizAbVjk=",
256
+ "ops/path_attn/parallel_path_bwd_inter_dkv.py": "mtVBfPgeTWxQWjcQfee4Kk/KRmck4kEwADFuT+PfcOg=",
257
+ "ops/path_attn/parallel_path_bwd_inter_dqh.py": "moIY1PhioPDmbWQc7T37Tx25kKn/1OrDGPsBOai1xTg=",
258
+ "ops/path_attn/parallel_path_bwd_intra.py": "w1sn58vCEin95acLyFmq+XQDQbGiiIU6vM8J/Xp5faw=",
259
+ "ops/path_attn/parallel_path_fwd.py": "uRUFU10eJB9ZCDcVn4AYM5+JdTRdKPKh7z5cpEEE+Ng=",
260
+ "ops/path_attn/prepare_k_cache.py": "DOuhtk1NY0PG0WSTxQcc8EvY3zFvTFRRLkyd9pztOaI=",
261
+ "ops/path_attn/transform_q.py": "qpu6Y3/BBj9K4dYP7Nj9QB/9zVOc0FHZY0NumyoyZLo=",
262
+ "ops/precond_gated_delta_rule/__init__.py": "E05uI9A1ocZvCySI4+nu41mCPr4MJmZA1upU7ruAHM8=",
263
+ "ops/precond_gated_delta_rule/backends/__init__.py": "hoBAVLZQ+jOiVYJWE5G4A1doL5gqeqP/Dz0istkBoeg=",
264
+ "ops/precond_gated_delta_rule/chunk.py": "d6aY4870cAoSyBkY063zvl57SHM9G31OZ6OUYL80OaE=",
265
+ "ops/precond_gated_delta_rule/chunk_precond_kkt_fwd.py": "4rEy83SMB79qql+FGOl5aQ66OYTj6D5vtNEgjcJEBrA=",
266
+ "ops/precond_gated_delta_rule/chunk_precond_wy_bwd.py": "eNSvTIyY+LfG5O3PLIasH3+vEsVg9uFYeo5f2Wjlwmw=",
267
+ "ops/precond_gated_delta_rule/fused_recurrent.py": "xRZ3yREutRIZ2qFVeS5u4QCy3byrePYRhL859UenVG0=",
268
+ "ops/precond_gated_delta_rule/naive.py": "O6pnpfNdxyMGmOPxdh7IZ1ExMYwV1KJ+1/L84R6Eteg=",
269
+ "ops/precond_kda/__init__.py": "KA5gjXPBlmGs6vc1Srg4zngR+5FXv+O2a0VJJDHrY0M=",
270
+ "ops/precond_kda/chunk.py": "KUpUtIX0ogoKnxatK5SMPdJ7Ms3lX5zvbSd4Y70Jm10=",
271
+ "ops/precond_kda/chunk_bwd.py": "pwNynFCythreiWjz6ezuDjZ2eqNdTmYj1PqysBdWCSc=",
272
+ "ops/precond_kda/chunk_intra.py": "gcm/6QihmSSHoN4DhSqNGuY2RPrbKp60ENBODhsi30o=",
273
+ "ops/precond_kda/chunk_intra_token_parallel.py": "ayBWo/wqF/AiU9qLC7jHmZbk04iGBcd9/FNeY7CuHDQ=",
274
+ "ops/precond_kda/fused_recurrent.py": "+RTPNLwCt8JVTL54hHmRqvEHLLJeLYn+a/9VKrzD3vY=",
275
+ "ops/precond_kda/naive.py": "7Bj6uTWYmCxBRHuOCzs+PhkWtxlFsTGkVXAzZGzhh8U=",
276
+ "ops/precond_kda/wy_fast.py": "pj8DLVEG5/XHWFj9SICxTKphtgmrz4Zvu+giMq8NFaY=",
277
  "ops/rebased/__init__.py": "pawYkzOJ/mQY5Qj0Ibuq81P3KNgO9Lv6swOxCjlDlGs=",
278
  "ops/rebased/naive.py": "XOrMn2Ohd7MDJ7QvP93dPfjv/6prnZW/ozxHtU0KqWQ=",
279
+ "ops/rebased/parallel.py": "cBnn/KTq9FlWJIpc6Jj9uJ7J14VfGFadrxABo4bsoQc=",
280
  "ops/retention/__init__.py": "i6uHA1ndTR6T/fVfooceKo12wyBFGYLe1EAm0Mo/sg0=",
281
+ "ops/retention/chunk.py": "LyunHUb/Y0ItUN9EDTaix4N3Zd2e/7D9AoOQTSXyAMM=",
282
+ "ops/retention/fused_chunk.py": "Nzn6nYHcMyjL8uOWkwD10Htbn28xhomLMG4XZ0p7ru0=",
283
+ "ops/retention/fused_recurrent.py": "WGV6AAdpoGG79XgHWkjDkFWY9ZpvE99xmqwre64KuZA=",
284
  "ops/retention/naive.py": "MG107SmOiRKLTZ8ziqaWN85nbgIPj/GqNJJ7KMt5Tag=",
285
+ "ops/retention/parallel.py": "WRBtqBxCWpIZ/Lm2u3bXwnbeLbwwQ9ZrrajeYeNBoTg=",
286
  "ops/rwkv4/__init__.py": "GnWakdtZiQJ0+fTYxqqIyosoPwjH6Yp8P1AbpHIDnts=",
287
+ "ops/rwkv4/fused_recurrent.py": "LG9PdTsubmJvgqvbDvT8N+9y5SWQZZXfTxC0Hg1jU5s=",
288
  "ops/rwkv6/__init__.py": "wPYgC6wG+xc4I2vessGhv1EaZ4tL5yuVQVSwi1rEEUk=",
289
+ "ops/rwkv6/backends/__init__.py": "3Hgm1GsMIil45jzcS+4PB9iZi77lM/W+P4hBlV1+fj8=",
290
+ "ops/rwkv6/chunk.py": "scQIA9mDVrEFjX5IoVNG7WRDmr9ss+cZBTqLDVkiEKk=",
291
  "ops/rwkv6/chunk_naive.py": "zyj0Og15jGxNMzmlD9FoEdf/odzu3tqio/jiceVMNk4=",
292
+ "ops/rwkv6/fused_recurrent.py": "Nriss1l8NCBYj3y/tIJJLEScxNo5BUXQcqOYe2T4Hac=",
293
  "ops/rwkv6/recurrent_naive.py": "/lCgua1vSaJDqA4tOWWUaVxol5Axi3cOcjI+mJj7OAM=",
294
  "ops/rwkv7/__init__.py": "dPKBISLNnwlKb+xp/8ejsc2peC9gKQ3NedtMT5wQ1sE=",
295
+ "ops/rwkv7/channel_mixing.py": "xJAzrxM8dRByDCC9DdLRi2Q56AzxDKWPcpg5nhyo4wU=",
296
+ "ops/rwkv7/chunk.py": "jiQCm20KhWEfM3MT99MCmm3LJZyWQSetiASzBEg5P4U=",
297
+ "ops/rwkv7/fused_addcmul.py": "ZQ437Ks75Hw3qa7F+iV+dx76TKl7nGcyJd0IEvkAEIk=",
298
+ "ops/rwkv7/fused_k_update.py": "NuR4O/dI+4UHVqwKLxgBBsXC7SCaCpTAed6cY2cy4qE=",
299
+ "ops/rwkv7/fused_recurrent.py": "EUJaU6go5WjhYB9WrAhOriB04cr5nuvCJwGuxNDp/1U=",
300
+ "ops/rwkv7/gate_output_correction.py": "rwkLmmd3vqW8dvoJIR7f7uz4iR8PgEWBMnQbLb582k4=",
301
+ "ops/simple_gla/__init__.py": "qHTqgCNXrTUUrTSo+ZG6B553XTCDonl3K58OpQ7YcMg=",
302
+ "ops/simple_gla/backends/__init__.py": "ZxTK7vZktZ7TBQB/5oiJr+oIypQU1cgkbn/ybwJl450=",
303
+ "ops/simple_gla/chunk.py": "wmKvR5zeIRZZal2YiPRSXZVp6bJoc5brUfAV1fjbKjw=",
304
+ "ops/simple_gla/fused_chunk.py": "gN1IUd+GV72DsTw+q9Ye2cMDCrfm9ZYv7dN1/+NHuPs=",
305
+ "ops/simple_gla/fused_recurrent.py": "UukysuAEhW46kvKCjDNfyfAjNFT2LF4QqZZMvtg+GMc=",
306
  "ops/simple_gla/naive.py": "g3fAN++vd0aWvZcdOW7Y0boMYcs2mOld8kePWRbl2R0=",
307
+ "ops/simple_gla/parallel.py": "6jkeEIyN0tHrMGG/PMcPpSHfJK1Gg6OpGpguPhjqulk=",
308
+ "ops/stickbreaking_attn/__init__.py": "A3p1HEdTYG4f0TLRWGSWQTySjsfOVHbX+Eg7KFyXpQM=",
309
+ "ops/stickbreaking_attn/naive.py": "P4jkKUVtTg7ImxWWfxbZzFsq//W5jfgeLM7gYVQTClk=",
310
+ "ops/stickbreaking_attn/parallel.py": "jqY7jl1yRSzCfTO1QtYmXPqKQfS+UdiyVQ1XCFCOKWg=",
311
  "ops/titans/__init__.py": "4t9OK1aYdW03I+sUlfUjj3ALTez1M/XFD/H6KzeZtGc=",
312
  "ops/titans/log_impl.py": "Y5tMWJgtVE+GX394DdxQHd6Yirrg3sLx6sinMdQvkAE=",
313
+ "ops/titans/naive.py": "0Y4R5926mX2CgYI55MjxVfn6p2X4iM4pABCzmVZ9d5c=",
314
  "ops/ttt/__init__.py": "cFaLRxAWRsKjcmXh01bJbh5U59ZaFaM7DwYgm/KBAbo=",
315
+ "ops/ttt/chunk.py": "Pyon33URTqVwxnprbp9AC7yZcY99MXBWTOxnf1iAxow=",
316
+ "ops/ttt/fused_chunk.py": "uVBNMzpMgEozAEuzis/xYVpBrDrxf3mcnWyfw3Lr9Yo=",
317
+ "ops/ttt/naive.py": "oSWlwYF9ngQShNodpdkaIwb3ndI4RH0jB2jssaKv89s=",
318
+ "ops/utils/__init__.py": "3NBO3K+KTaQeR4ebY6LYdLOhdUxGFOPSqdAQPLhJt/I=",
319
+ "ops/utils/cache.py": "qP/aHKYJ7682YgeoetUAIlPTn9S14f2URjRSRdEQ5vk=",
 
 
 
320
  "ops/utils/constant.py": "uVK9zwOd7kM/DPSQk8cllx5nkYeK0K/KZVyCu0ZFdPg=",
321
+ "ops/utils/csr.py": "3s/+zgzuhMNIRo9UyriDR+Z94AOfAnx0VAD2VdmCszQ=",
322
+ "ops/utils/cumsum.py": "0Y9Lu/0qZLxT05txmpYuwvbmuxlgAsF6mnejbOp84xY=",
323
+ "ops/utils/graph.py": "uj5R0DV8gy3tW/VH9pT4xPn3Zb0CxYJWpH4zaU8KS6E=",
324
+ "ops/utils/index.py": "KeykCsBnOSNMGGgphH5OKFD/uqiva2rPGKUuqyj/8xk=",
325
+ "ops/utils/logcumsumexp.py": "ZpYz3i9FZI712XtDqIxCus8A4WamVFlYI8b6tHVbkXQ=",
326
+ "ops/utils/logsumexp.py": "qklce3jy76dN9WrNT1Bzw5F6Ime+mo00deKYryTMLTk=",
327
+ "ops/utils/matmul.py": "1X2PdPmFwXEf3ruaLZHBLFrYBwgmh5k+GOP5fuKM5Jw=",
328
+ "ops/utils/op.py": "Y/Kcks768kujwclindhHtbnQxyJje7mMBVReDvegK1Q=",
329
+ "ops/utils/pack.py": "92z4AmmkdpJ5rryTNHjB+BkSmlsBU5ssXJqPoLXqdFc=",
330
+ "ops/utils/pooling.py": "mtioM2xOtxMCEJDBfR+lVxhdg1x3NdBV8PVK+zOb/6Q=",
331
+ "ops/utils/sinkhorn/__init__.py": "7AY27ZaP2G/gGqo/equFKp9hLHUcAp6Mmi8xE3UGBGc=",
332
+ "ops/utils/sinkhorn/gluon.py": "bJ9FjAXod5fuweW1T6pP3C1Q6esQRDiS/MchqGZKtIU=",
333
+ "ops/utils/sinkhorn/naive.py": "WZYZzlJ/atDmXzRnJFFMWfB/t5JIytpzxLz98palzq8=",
334
+ "ops/utils/sinkhorn/ops.py": "2UH9sL5DsBPRcr+vUgahTvOaI9Myg2cnnZv7oclXx/s=",
335
+ "ops/utils/softmax.py": "aCTHGnayRqCA6PAd9edtVeb9EoFNfzJdBmDtRZiMZN0=",
336
+ "ops/utils/softplus.py": "Ykhyi0d+jM2zEyFp8fQqmFNqQmsU5XzUbUpn+jr/Rn0=",
337
+ "ops/utils/solve_tril.py": "4j2C13HQCr+F5URwwLUO4t3Dag6upnlpGwNew17nphs=",
338
  "ops/wall_attn/__init__.py": "r/FbDGjGO8WK1DaMbl9axWHpENUa3pzSc0v9qviHJ6I=",
339
+ "ops/wall_attn/decode.py": "o5q0fsJTiz+0XW0RPFFI1hBXz6PXSBMSTvog8Bc+sGA=",
340
  "ops/wall_attn/naive.py": "9YKwBSvQ9imy229T5KMYKpuVNb+xEzhXsXdMemRWqjQ=",
341
+ "ops/wall_attn/parallel.py": "k1DMCeZ/dLyUrpaLt1eWphG3WA0STXIwofUU2/lZx1g=",
342
+ "symbols.json": "XeDbgUU3taJ6OhnIi2xuHQpY5Guf0MQ5YL5XFWYzxl0=",
343
+ "utils/__init__.py": "+7iYDYAJEDL9y/TTl/bNr5/O0LpSylNyE6J7c1OCAxs=",
344
+ "utils/decorators.py": "Xy1DCGkQ7HRyffUHEXsVLhn6LE7xgdIY9Yob1GFm/f8=",
345
+ "utils/env.py": "LJZbQTALlKpcS2dwt7JBv/AdlZ8Yh7LC7tCDDJs0D2M=",
346
+ "utils/hardware.py": "2AA4GxTL1WNYIzRJQF3OXCCuATnobx928o6bjXD0U04=",
347
+ "utils/layers.py": "afB10gGysmmVfVRlPnduPpuvcdAeDAstmqPC0sYJbRw=",
348
+ "utils/testing.py": "KXWwCDYOxKLEYFxzZgFfh/3kxzMm+12Vq4IsxJaoPlM="
 
 
349
  }
350
  },
351
  "provenance": {
 
355
  "dirty": false
356
  },
357
  "kernel": {
358
+ "commit": "faa8674647aa5ebaab5e997cd11e6e3808d090a9",
359
  "dirty": false
360
  }
361
  }
build/torch-cuda/metadata.json.sigstore CHANGED
@@ -1 +1 @@
1
- {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"certificate":{"rawBytes":"MIIHLTCCBrOgAwIBAgIUQ629eN4hUTIWupwbogDv3RA6AyEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYxMDE5MTMxODIzWhcNMjYxMDE5MTMyODIzWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpl17UO4EUwHQu4gYHIYYLubpp8Zzl5lYFeFTp4LxxeYjaO9yg8RCS6mWZc2nLEcA3HNjkFi4C6oGo/oG5gs1KaOCBdIwggXOMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSMEGDAWgBTf0+nPViQRlvmo2OkoVaLGLhhkPzBrBgNVHREBAf8EYTBfhl1odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkvLmdpdGh1Yi93b3JrZmxvd3MvYnVpbGQueWFtbEByZWZzL2hlYWRzL21haW4wOQYKKwYBBAGDvzABAQQraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTAfBgorBgEEAYO/MAECBBF3b3JrZmxvd19kaXNwYXRjaDA2BgorBgEEAYO/MAEDBChmZTJiYjY2ZmVlNmMyYTc3ZWNjZjA4YTA1MDhkYzU3NDI4ZDQ4OGVkMBMGCisGAQQBg78wAQQEBUJ1aWxkMCsGCisGAQQBg78wAQUEHWh1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MB0GCisGAQQBg78wAQYED3JlZnMvaGVhZHMvbWFpbjA7BgorBgEEAYO/MAEIBC0MK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wbQYKKwYBBAGDvzABCQRfDF1odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkvLmdpdGh1Yi93b3JrZmxvd3MvYnVpbGQueWFtbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABCgQqDChmZTJiYjY2ZmVlNmMyYTc3ZWNjZjA4YTA1MDhkYzU3NDI4ZDQ4OGVkMBsGCisGAQQBg78wAQsEDQwLc2VsZi1ob3N0ZWQwQAYKKwYBBAGDvzABDAQyDDBodHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkwOAYKKwYBBAGDvzABDQQqDChmZTJiYjY2ZmVlNmMyYTc3ZWNjZjA4YTA1MDhkYzU3NDI4ZDQ4OGVkMB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTA3MTQ3NTUyOTAuBgorBgEEAYO/MAEQBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZTAYBgorBgEEAYO/MAERBAoMCDI1NzIwNzQzMG0GCisGAQQBg78wARIEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoZmUyYmI2NmZlZTZjMmE3N2VjY2YwOGEwNTA4ZGM1NzQyOGQ0ODhlZDAhBgorBgEEAYO/MAEUBBMMEXdvcmtmbG93X2Rpc3BhdGNoMGQGCisGAQQBg78wARUEVgxUaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5L2FjdGlvbnMvcnVucy8zOTIzOTU0OTYzNS9hdHRlbXB0cy8xMBYGCisGAQQBg78wARYECAwGcHVibGljMEYGCisGAQQBg78wARgEOAw2cmVwbzpodWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTpyZWY6cmVmcy9oZWFkcy9tYWluMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGhVFAuVgAABAMASDBGAiEAtnEys30x7Ya9hsVCz7RWdn6nYXSqgMVDe8yHwixSiiYCIQCdcOvWSnZSR+oCBuEqRzBjvDTsmBBOA7bQNQdRM+tFVjAKBggqhkjOPQQDAwNoADBlAjB44h8gf51LU8aLNgDS8WUfSC7PpbdOXA/MDkX3XPLQa7tTAnTW9IgnghlRkSCVIdUCMQDgufgY6hRhjInveoC0gxGEucYH3gdJZ7qJkooniD5e5OD64vpL9D2zpvlaZC+N+Y8="},"tlogEntries":[{"logIndex":"3321382366","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"hashedrekord","version":"0.0.1"},"integratedTime":"1792415903","inclusionPromise":{"signedEntryTimestamp":"MEQCIA8Pb2l/03SWHSHoQlVPnuD3qoB1EObRvw8TSvdRmXHCAiBLQW9zMHeh1g3bHLP1y44z5xADQsM8tsSXvzGp0GMBYA=="},"inclusionProof":{"logIndex":"3199478104","rootHash":"4tHHO4qqAF/BE6oVRgF0boQq/yTfvz0KIX8pAsRo3G0=","treeSize":"3199478328","hashes":["boitsHdOY++pKowb8ip8eWyv+giOG6c3LLDf4d59mB0=","qcf970yQgX2TVWwq1E7iCbQrykhEhaftOyMyVe+h/N4=","EslsDaCqZixXslWYzeNOIgKnH6afg5sQlM2aEpV4jq0=","FtY90QTdG+QWhCzKV56/dsHTXqKp04sOsj4tjwrBhI8=","1Du/xr+ro1zVy3zI5m0v/GyxJcdIShmHLA8zKq/JJrc=","6akUiAwNlsFQQrqdMshpY6Nr7bcHUIKiutTPw9YSiOY=","eJJgNMmRIOUXkgOpxCA8t3Iu0nU2BvsYyb5Fuo48Z/s=","gqCLyzcTY8N5+rych9c4HRspuMPAmRkae5U4ACwbpaM=","r4uw63XA1OsTh4/HhkQY/Veqn7iADlv2k7521RDZtlw=","mIeT670PcvMS00qGZ9uqtj9ZBspJzyCniPsKBf9FQ1M=","OXA2SiOGgZ4Atk3yCE3v98reKXHty7rZAo82yZi0yls=","eE+LYckVfbRCjJTwQL99tuBkmhXXjKkrIh1lVDPm+hs=","WF6g44nOcJ5AK5U4U6CvcX9NPmGRLXdvfv+hcf011hw=","kHa2Ke03zePMosAfwPNg2y4kk1ZwjdZi4MKNVX7epiE=","uCZQe757l2sS1c05Y7TzOwEm+X8MeGLIUCkEOmFVOz4=","nyQV/+cWQUG5Hb2PKjQQNkQIJmz2qzAZKBQlkHGIknM=","G+nk7mgE5h6dUmv2UY7NDJrY3WvrZY31raaO1AETt+o=","qlTWNDzERnTBQVcHHXXVPjiUP0SsikGxfcxVlgqVFDo=","gg9GNApNke3+zNhl4tsWtmIycCFrOMCB3lys6XuSQNQ=","NlOclldmKPA1tMKIskqHAwuQDYHp31YwpK+OOMrMccc=","qxzHanAzz57SDdmJe0B7bJK72NTIBbwEMGBBKvDOROw=","xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="],"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n3199478328\n4tHHO4qqAF/BE6oVRgF0boQq/yTfvz0KIX8pAsRo3G0=\n\n— rekor.sigstore.dev wNI9ajBEAiANt/PrcbDQqVmXByPf3zWgItoN7m+bQariPJGNVfk0hwIgNtxR5MYteCwgnROrvIy4TnFG1I1+jhaTEfNh7ViG12U=\n"}},"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwZTE2NWUzMmY5ZDdlNzEyNWVkMjJhYWZlMTY1YzNiYjEyNjA4MThmN2E3YjUwZDdhNTkyOGE4MTI5OWQ1ZjRjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJR3VFSllNbGZJcGZCZXBqMFN1Nk5iLzMxSEdibVpuZzVVRnRUajJwWStPSUFpRUE4R1d4RDlNOXJVdU1od0pMVU53RERFR1ZWVHJ0MXViUHFsOUl1b2R5SDdvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaE1WRU5EUW5KUFowRjNTVUpCWjBsVlVUWXlPV1ZPTkdoVlZFbFhkWEIzWW05blJIWXpVa0UyUVhsRmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFFUlRWTlZFMTRUMFJKZWxkb1kwNU5hbGw0VFVSRk5VMVVUWGxQUkVsNlYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ3YkRFM1ZVODBSVlYzU0ZGMU5HZFpTRWxaV1V4MVluQndPRnA2YkRWc1dVWmxSbFFLY0RSTWVIaGxXV3BoVHpsNVp6aFNRMU0yYlZkYVl6SnVURVZqUVROSVRtcHJSbWswUXpadlIyOHZiMGMxWjNNeFMyRlBRMEprU1hkbloxaFBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFuSkNaMDVXU0ZKRlFrRm1PRVZaVkVKbWFHd3hiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWMllUSldlV0p0Vm5OamVURnFZakl4ZEdSWE5YQmtTR3QyVEcxa2NHUkhhREZaYVRrellqTktjZ3BhYlhoMlpETk5kbGx1Vm5CaVIxRjFaVmRHZEdKRlFubGFWMXA2VERKb2JGbFhVbnBNTWpGb1lWYzBkMDlSV1V0TGQxbENRa0ZIUkhaNlFVSkJVVkZ5Q21GSVVqQmpTRTAyVEhrNU1HSXlkR3hpYVRWb1dUTlNjR0l5TlhwTWJXUndaRWRvTVZsdVZucGFXRXBxWWpJMU1GcFhOVEJNYlU1MllsUkJaa0puYjNJS1FtZEZSVUZaVHk5TlFVVkRRa0pHTTJJelNuSmFiWGgyWkRFNWEyRllUbmRaV0ZKcVlVUkJNa0puYjNKQ1owVkZRVmxQTDAxQlJVUkNRMmh0V2xSS2FRcFphbGt5V20xV2JFNXRUWGxaVkdNeldsZE9hbHBxUVRSWlZFRXhUVVJvYTFsNlZUTk9SRWswV2tSUk5FOUhWbXROUWsxSFEybHpSMEZSVVVKbk56aDNDa0ZSVVVWQ1ZVb3hZVmQ0YTAxRGMwZERhWE5IUVZGUlFtYzNPSGRCVVZWRlNGZG9NVm95WkhCaWJXUnRXVmRPYkV3eWRHeGpiVFZzWWtoTmRGa3lPWFFLWWxoV2RXRllValZOUWpCSFEybHpSMEZSVVVKbk56aDNRVkZaUlVRelNteGFiazEyWVVkV2FGcElUWFppVjBad1ltcEJOMEpuYjNKQ1owVkZRVmxQTHdwTlFVVkpRa013VFVzeWFEQmtTRUo2VDJrNGRtUkhPWEphVnpSMVdWZE9NR0ZYT1hWamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcUNtSXlNSGRpVVZsTFMzZFpRa0pCUjBSMmVrRkNRMUZTWmtSR01XOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaFNGWnVXakpzZFZveVdtZ0tXVEpWZG1FeVZubGliVlp6WTNreGFtSXlNWFJrVnpWd1pFaHJka3h0WkhCa1IyZ3hXV2s1TTJJelNuSmFiWGgyWkROTmRsbHVWbkJpUjFGMVpWZEdkQXBpUlVKNVdsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1EyZFJjVVJEYUcxYVZFcHBXV3BaTWxwdFZteE9iVTE1Q2xsVVl6TmFWMDVxV21wQk5GbFVRVEZOUkdocldYcFZNMDVFU1RSYVJGRTBUMGRXYTAxQ2MwZERhWE5IUVZGUlFtYzNPSGRCVVhORlJGRjNUR015Vm5NS1dta3hiMkl6VGpCYVYxRjNVVUZaUzB0M1dVSkNRVWRFZG5wQlFrUkJVWGxFUkVKdlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5XcGlNakIyWVVoV2JncGFNbXgxV2pKYWFGa3lWWFpoTWxaNVltMVdjMk41TVdwaU1qRjBaRmMxY0dSSWEzZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JGRlJjVVJEYUcxYVZFcHBDbGxxV1RKYWJWWnNUbTFOZVZsVVl6TmFWMDVxV21wQk5GbFVRVEZOUkdocldYcFZNMDVFU1RSYVJGRTBUMGRXYTAxQ09FZERhWE5IUVZGUlFtYzNPSGNLUVZFMFJVVlJkMUJqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxQ2IwZERhWE5IUVZGUlFtYzNPSGRCVVRoRlJFRjNTMDFVUVROTlZGRXpUbFJWZVFwUFZFRjFRbWR2Y2tKblJVVkJXVTh2VFVGRlVVSkRRVTFJYldnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx2WkZka2JtRlhOVzVhYlVacUNscFVRVmxDWjI5eVFtZEZSVUZaVHk5TlFVVlNRa0Z2VFVORVNURk9la2wzVG5wUmVrMUhNRWREYVhOSFFWRlJRbWMzT0hkQlVrbEZXSGQ0WkdGSVVqQUtZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW1neFdqSmtjR0p0WkcxWlYwNXNUREowYkdOdE5XeGlTRTEwV1RJNWRHSllWblZoV0ZJMVRIazFiZ3BoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3eVNqRmhWM2hyVEc1c2FHSlhlRUZqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxRVowZERhWE5IQ2tGUlVVSm5OemgzUVZKTlJVdG5kMjlhYlZWNVdXMUpNazV0V214YVZGcHFUVzFGTTA0eVZtcFpNbGwzVDBkRmQwNVVRVFJhUjAweFRucFJlVTlIVVRBS1QwUm9iRnBFUVdoQ1oyOXlRbWRGUlVGWlR5OU5RVVZWUWtKTlRVVllaSFpqYlhSdFlrYzVNMWd5VW5Cak0wSm9aRWRPYjAxSFVVZERhWE5IUVZGUlFncG5OemgzUVZKVlJWWm5lRlZoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlV3eVJtcGtSMngyWW01TmRtTnVWblZqZVRoNlQxUkplazlVVlRCUFZGbDZUbE01YUdSSVVteGlXRUl3WTNrNGVFMUNXVWNLUTJselIwRlJVVUpuTnpoM1FWSlpSVU5CZDBkalNGWnBZa2RzYWsxRldVZERhWE5IUVZGUlFtYzNPSGRCVW1kRlQwRjNNbU50Vm5kaWVuQnZaRmRrYmdwaFZ6VnVXbTFHYWxwVE9YSmFXRXAxV2xkNGVreFhUblppVnpFeFltMXNNR1ZVY0hsYVYxazJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOU1VkTUNrSm5iM0pDWjBWRlFXUmFOVUZuVVVOQ1NEQkZaWGRDTlVGSVkwRXpWREIzWVhOaVNFVlVTbXBIVWpSamJWZGpNMEZ4U2t0WWNtcGxVRXN6TDJnMGNIa0taME00Y0Rkdk5FRkJRVWRvVmtaQmRWWm5RVUZDUVUxQlUwUkNSMEZwUlVGMGJrVjVjek13ZURkWllUbG9jMVpEZWpkU1YyUnVObTVaV0ZOeFowMVdSQXBsT0hsSWQybDRVMmxwV1VOSlVVTmtZMDkyVjFOdVdsTlNLMjlEUW5WRmNWSjZRbXAyUkZSemJVSkNUMEUzWWxGT1VXUlNUU3QwUmxacVFVdENaMmR4Q21ocmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtJME5HZzRaMlkxTVV4Vk9HRk1UbWRFVXpoWFZXWlRRemRRY0dKa1QxaEJMMDFFYTFneldGQk1VV0UzZEZRS1FXNVVWemxKWjI1bmFHeFNhMU5EVmtsa1ZVTk5VVVJuZFdabldUWm9VbWhxU1c1MlpXOURNR2Q0UjBWMVkxbElNMmRrU2xvM2NVcHJiMjl1YVVRMVpRbzFUMFEyTkhad1REbEVNbnB3ZG14aFdrTXJUaXRaT0QwS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9fX19"}],"timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyjADAgEAMIICwQYJKoZIhvcNAQcCoIICsjCCAq4CAQMxDTALBglghkgBZQMEAgEwgbcGCyqGSIb3DQEJEAEEoIGnBIGkMIGhAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgM+jtpRT7VTayyXNU6XeuWevLOHSgymjIR3AhSqAF5ZoCFDPzZCiwDE+g/BiHrxxd5OfIKibdGA8yMDI2MTAxOTEzMTgyM1owAwIBAaAypDAwLjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MRUwEwYDVQQDEwxzaWdzdG9yZS10c2GgADGCAdwwggHYAgEBMFEwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZAIUOhNULwyQYe68wUMvy4qOiyojiwwwCwYJYIZIAWUDBAIBoIH8MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYxMDE5MTMxODIzWjAvBgkqhkiG9w0BCQQxIgQgChdtG/mcWGdAqUjlHsL98rTG5klQTLXZofRCueCmlcMwgY4GCyqGSIb3DQEJEAIvMX8wfTB7MHkEIIX5J7wHq2LKw7RDVsEO/IGyxog/2nq55thw2dE6zQW3MFUwPaQ7MDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAoGCCqGSM49BAMCBGgwZgIxANvTfbN0SFidKmyitQtHD4Zqb8S9dKWiNCxfM34Oh3cGtp11St3BpK8lBFE6kVV5ygIxAPF6xXECkECEvGUpxdM+YCyEz16v/A6sZHjToLqQ52fY6uNObrkl1Lyv7ghcHDtBQQ=="}]}},"messageSignature":{"messageDigest":{"algorithm":"SHA2_256","digest":"DhZeMvnX5xJe0iqv4WXDuxJggY96e1DXpZKKgSmdX0w="},"signature":"MEUCIGuEJYMlfIpfBepj0Su6Nb/31HGbmZng5UFtTj2pY+OIAiEA8GWxD9M9rUuMhwJLUNwDDEGVVTrt1ubPql9IuodyH7o="}}
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHLTCCBrKgAwIBAgIUW8juOVkziLnnecCKBESbrqbEgJEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYxMDIzMTEzNjAyWhcNMjYxMDIzMTE0NjAyWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnvx47gGnlRzCN/4OkJ0a7wqYEsIFmCINUHZaFbz5VXCd4BWhf0QldM2OnXd4Ogo0yCWW1dQCX18MRUBj9CRGX6OCBdEwggXNMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSMEGDAWgBTf0+nPViQRlvmo2OkoVaLGLhhkPzBrBgNVHREBAf8EYTBfhl1odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkvLmdpdGh1Yi93b3JrZmxvd3MvYnVpbGQueWFtbEByZWZzL2hlYWRzL21haW4wOQYKKwYBBAGDvzABAQQraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTAfBgorBgEEAYO/MAECBBF3b3JrZmxvd19kaXNwYXRjaDA2BgorBgEEAYO/MAEDBChmYWE4Njc0NjQ3YWE1ZWJhYWI1ZTk5N2NkMTFlNmUzODA4ZDA5MGE5MBMGCisGAQQBg78wAQQEBUJ1aWxkMCsGCisGAQQBg78wAQUEHWh1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MB0GCisGAQQBg78wAQYED3JlZnMvaGVhZHMvbWFpbjA7BgorBgEEAYO/MAEIBC0MK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wbQYKKwYBBAGDvzABCQRfDF1odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkvLmdpdGh1Yi93b3JrZmxvd3MvYnVpbGQueWFtbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABCgQqDChmYWE4Njc0NjQ3YWE1ZWJhYWI1ZTk5N2NkMTFlNmUzODA4ZDA5MGE5MBsGCisGAQQBg78wAQsEDQwLc2VsZi1ob3N0ZWQwQAYKKwYBBAGDvzABDAQyDDBodHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHkwOAYKKwYBBAGDvzABDQQqDChmYWE4Njc0NjQ3YWE1ZWJhYWI1ZTk5N2NkMTFlNmUzODA4ZDA5MGE5MB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBoGCisGAQQBg78wAQ8EDAwKMTA3MTQ3NTUyOTAuBgorBgEEAYO/MAEQBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZTAYBgorBgEEAYO/MAERBAoMCDI1NzIwNzQzMG0GCisGAQQBg78wARIEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoZmFhODY3NDY0N2FhNWViYWFiNWU5OTdjZDExZTZlMzgwOGQwOTBhOTAhBgorBgEEAYO/MAEUBBMMEXdvcmtmbG93X2Rpc3BhdGNoMGQGCisGAQQBg78wARUEVgxUaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5L2FjdGlvbnMvcnVucy8zOTk0NjY4MTMwNi9hdHRlbXB0cy8xMBYGCisGAQQBg78wARYECAwGcHVibGljMEYGCisGAQQBg78wARgEOAw2cmVwbzpodWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTpyZWY6cmVmcy9oZWFkcy9tYWluMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGhaIvpVQAABAMARzBFAiAoSbfUgNXfezTQtR5qo/CAMwLW8dHV4YbAmcGsMcaFGAIhAMOHSfyt8UUJmPp+JbjTW2xG1BtB/lVIRWOl5oJ/2vZMMAoGCCqGSM49BAMDA2kAMGYCMQCh4ra7/gf8kr1+MdulHjJAXsUd6o0Y9UypE3gFlmHTIgsa/PX9g9NIQP2SRERurOICMQDs43kZxEJ3O3bKz90jbuYM8eNXViJ/TqCHiYc2lWLDQ3rLJWze8xhx7Ks+OwnIgBQ="}, "tlogEntries":[{"logIndex":"3419000459", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"hashedrekord", "version":"0.0.1"}, "integratedTime":"1792755362", "inclusionPromise":{"signedEntryTimestamp":"MEUCICKD7ZdvH1zemVgy07V5zNzmAv0zBIb/8XOEU9QuaaUEAiEAlc/U/9oh4oaQkyOWIzDOALZtdsEroH6lxTDoz9oWPSE="}, "inclusionProof":{"logIndex":"3297096197", "rootHash":"JEwIlEh4C2tvEMwtPOSql67Ts10gAHO7oKqbcfoRsd8=", "treeSize":"3297096204", "hashes":["eKPdjKmW06i/+OgD5Op1qaQr5ljD9icDNR9hKFkKoGs=", "7rIrg/UOeaKdEevZpZHUL3OkluOm/jIeaO923YSkHYo=", "DFFxkRWq9l1mck3McpCgX5nsFudwuRn4U349BXtS7Po=", "S1S/TJWDErr9PeC72czEVORauVEpucJfdtz2vOI5UII=", "06+aOxxaVl4XdwkhaYb3Dh+97wX+oKPonkJn+6nGgmE=", "4iY88O6LVkiDY854scCm32PtSUJYHIsmX33LyfcvBNs=", "DAFTN+3Ntn00j3uqF6DuxpoDvddLg+hpOGsbC8FJr/g=", "sH3iRJ+YSpMgcl9MQvq5mOnxKnX0UOS6Qxva+JrjED8=", "mIjtw1KA13/4jqXfKlJiFrODS7dNF+hqEUNlXprnzTE=", "pNenO6BoAxc3BwcFEO9IJI4uZmwdkynjP9MI8OiYc50=", "1GPTGQWDthSzUsPF7t/XQmpI2SH4KZm3gaVrhMs6T2k=", "YYCr61A8azar3dfO8Ct+pri7LqDxkUlUkfs7fiqYErU=", "1DaDKzrCNNBhmSdY5vFjz+NpAmxmYfYEgBCdPLrMP1A=", "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n3297096204\nJEwIlEh4C2tvEMwtPOSql67Ts10gAHO7oKqbcfoRsd8=\n\n— rekor.sigstore.dev wNI9ajBFAiEA3J9FLPnuB38IvvWseghTN705Z1/I82uJ9pjXiomV8lcCIF/CifFOxrfvDzHBxDm21wUegDzbni28WhNQ9fftJoeE\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwZGIyMmFhMzI4ZWUzZjcwOTAwYTFmMTM4MzRmYjFiMmFiNDE5NTMwODY0N2MzNWM5NjFmMTAzMGQyNzljMGJhIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUURDL2YydTZCS013bFJ5U3Z0RWdUdUsrbUVjTGFQT1lYNmRYUkFwNWdSMytRSWhBS1p3bXRKZ2d2TDNVMEF3SU5kM3NWYXRsN2xCK3oxRjY3V0ptcm1IOVpiQSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaE1WRU5EUW5KTFowRjNTVUpCWjBsVlZ6aHFkVTlXYTNwcFRHNXVaV05EUzBKRlUySnljV0pGWjBwRmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFFU1hwTlZFVjZUbXBCZVZkb1kwNU5hbGw0VFVSSmVrMVVSVEJPYWtGNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ1ZG5nME4yZEhibXhTZWtOT0x6UlBhMG93WVRkM2NWbEZjMGxHYlVOSlRsVklXbUVLUm1KNk5WWllRMlEwUWxkb1pqQlJiR1JOTWs5dVdHUTBUMmR2TUhsRFYxY3haRkZEV0RFNFRWSlZRbW81UTFKSFdEWlBRMEprUlhkbloxaE9UVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFuSkNaMDVXU0ZKRlFrRm1PRVZaVkVKbWFHd3hiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWMllUSldlV0p0Vm5OamVURnFZakl4ZEdSWE5YQmtTR3QyVEcxa2NHUkhhREZaYVRrellqTktjZ3BhYlhoMlpETk5kbGx1Vm5CaVIxRjFaVmRHZEdKRlFubGFWMXA2VERKb2JGbFhVbnBNTWpGb1lWYzBkMDlSV1V0TGQxbENRa0ZIUkhaNlFVSkJVVkZ5Q21GSVVqQmpTRTAyVEhrNU1HSXlkR3hpYVRWb1dUTlNjR0l5TlhwTWJXUndaRWRvTVZsdVZucGFXRXBxWWpJMU1GcFhOVEJNYlU1MllsUkJaa0puYjNJS1FtZEZSVUZaVHk5TlFVVkRRa0pHTTJJelNuSmFiWGgyWkRFNWEyRllUbmRaV0ZKcVlVUkJNa0puYjNKQ1owVkZRVmxQTDAxQlJVUkNRMmh0V1ZkRk5BcE9hbU13VG1wUk0xbFhSVEZhVjBwb1dWZEpNVnBVYXpWT01rNXJUVlJHYkU1dFZYcFBSRUUwV2tSQk5VMUhSVFZOUWsxSFEybHpSMEZSVVVKbk56aDNDa0ZSVVVWQ1ZVb3hZVmQ0YTAxRGMwZERhWE5IUVZGUlFtYzNPSGRCVVZWRlNGZG9NVm95WkhCaWJXUnRXVmRPYkV3eWRHeGpiVFZzWWtoTmRGa3lPWFFLWWxoV2RXRllValZOUWpCSFEybHpSMEZSVVVKbk56aDNRVkZaUlVRelNteGFiazEyWVVkV2FGcElUWFppVjBad1ltcEJOMEpuYjNKQ1owVkZRVmxQTHdwTlFVVkpRa013VFVzeWFEQmtTRUo2VDJrNGRtUkhPWEphVnpSMVdWZE9NR0ZYT1hWamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcUNtSXlNSGRpVVZsTFMzZFpRa0pCUjBSMmVrRkNRMUZTWmtSR01XOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaFNGWnVXakpzZFZveVdtZ0tXVEpWZG1FeVZubGliVlp6WTNreGFtSXlNWFJrVnpWd1pFaHJka3h0WkhCa1IyZ3hXV2s1TTJJelNuSmFiWGgyWkROTmRsbHVWbkJpUjFGMVpWZEdkQXBpUlVKNVdsZGFla3d5YUd4WlYxSjZUREl4YUdGWE5IZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1EyZFJjVVJEYUcxWlYwVTBUbXBqTUU1cVVUTlpWMFV4Q2xwWFNtaFpWMGt4V2xSck5VNHlUbXROVkVac1RtMVZlazlFUVRSYVJFRTFUVWRGTlUxQ2MwZERhWE5IUVZGUlFtYzNPSGRCVVhORlJGRjNUR015Vm5NS1dta3hiMkl6VGpCYVYxRjNVVUZaUzB0M1dVSkNRVWRFZG5wQlFrUkJVWGxFUkVKdlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5XcGlNakIyWVVoV2JncGFNbXgxV2pKYWFGa3lWWFpoTWxaNVltMVdjMk41TVdwaU1qRjBaRmMxY0dSSWEzZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JGRlJjVVJEYUcxWlYwVTBDazVxWXpCT2FsRXpXVmRGTVZwWFNtaFpWMGt4V2xSck5VNHlUbXROVkVac1RtMVZlazlFUVRSYVJFRTFUVWRGTlUxQ09FZERhWE5IUVZGUlFtYzNPSGNLUVZFMFJVVlJkMUJqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxQ2IwZERhWE5IUVZGUlFtYzNPSGRCVVRoRlJFRjNTMDFVUVROTlZGRXpUbFJWZVFwUFZFRjFRbWR2Y2tKblJVVkJXVTh2VFVGRlVVSkRRVTFJYldnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx2WkZka2JtRlhOVzVhYlVacUNscFVRVmxDWjI5eVFtZEZSVUZaVHk5TlFVVlNRa0Z2VFVORVNURk9la2wzVG5wUmVrMUhNRWREYVhOSFFWRlJRbWMzT0hkQlVrbEZXSGQ0WkdGSVVqQUtZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW1neFdqSmtjR0p0WkcxWlYwNXNUREowYkdOdE5XeGlTRTEwV1RJNWRHSllWblZoV0ZJMVRIazFiZ3BoV0ZKdlpGZEpkbVF5T1hsaE1scHpZak5rZWt3eVNqRmhWM2hyVEc1c2FHSlhlRUZqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxRVowZERhWE5IQ2tGUlVVSm5OemgzUVZKTlJVdG5kMjlhYlVab1QwUlpNMDVFV1RCT01rWm9UbGRXYVZsWFJtbE9WMVUxVDFSa2FscEVSWGhhVkZwc1RYcG5kMDlIVVhjS1QxUkNhRTlVUVdoQ1oyOXlRbWRGUlVGWlR5OU5RVVZWUWtKTlRVVllaSFpqYlhSdFlrYzVNMWd5VW5Cak0wSm9aRWRPYjAxSFVVZERhWE5IUVZGUlFncG5OemgzUVZKVlJWWm5lRlZoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlV3eVJtcGtSMngyWW01TmRtTnVWblZqZVRoNlQxUnJNRTVxV1RSTlZFMTNUbWs1YUdSSVVteGlXRUl3WTNrNGVFMUNXVWNLUTJselIwRlJVVUpuTnpoM1FWSlpSVU5CZDBkalNGWnBZa2RzYWsxRldVZERhWE5IUVZGUlFtYzNPSGRCVW1kRlQwRjNNbU50Vm5kaWVuQnZaRmRrYmdwaFZ6VnVXbTFHYWxwVE9YSmFXRXAxV2xkNGVreFhUblppVnpFeFltMXNNR1ZVY0hsYVYxazJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOU1VkTENrSm5iM0pDWjBWRlFXUmFOVUZuVVVOQ1NIZEZaV2RDTkVGSVdVRXpWREIzWVhOaVNFVlVTbXBIVWpSamJWZGpNMEZ4U2t0WWNtcGxVRXN6TDJnMGNIa0taME00Y0Rkdk5FRkJRVWRvWVVsMmNGWlJRVUZDUVUxQlVucENSa0ZwUVc5VFltWlZaMDVZWm1WNlZGRjBValZ4Ynk5RFFVMTNURmM0WkVoV05GbGlRUXB0WTBkelRXTmhSa2RCU1doQlRVOUlVMlo1ZERoVlZVcHRVSEFyU21KcVZGY3llRWN4UW5SQ0wyeFdTVkpYVDJ3MWIwb3ZNblphVFUxQmIwZERRM0ZIQ2xOTk5EbENRVTFFUVRKclFVMUhXVU5OVVVOb05ISmhOeTluWmpocmNqRXJUV1IxYkVocVNrRlljMVZrTm04d1dUbFZlWEJGTTJkR2JHMUlWRWxuYzJFS0wxQllPV2M1VGtsUlVESlRVa1ZTZFhKUFNVTk5VVVJ6TkROclduaEZTak5QTTJKTGVqa3dhbUoxV1UwNFpVNVlWbWxLTDFSeFEwaHBXV015YkZkTVJBcFJNM0pNU2xkNlpUaDRhSGczUzNNclQzZHVTV2RDVVQwS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9fX19"}], "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyzADAgEAMIICwgYJKoZIhvcNAQcCoIICszCCAq8CAQMxDTALBglghkgBZQMEAgEwgbgGCyqGSIb3DQEJEAEEoIGoBIGlMIGiAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQg3bpS2VtJbbj+NwI4jCmvshiu/17s+rjceNrg2WK85ewCFQC8epRj5pgyFllwm2wpZ1CZZQQv6hgPMjAyNjEwMjMxMTM2MDJaMAMCAQGgMqQwMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhoAAxggHcMIIB2AIBATBRMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAsGCWCGSAFlAwQCAaCB/DAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MTAyMzExMzYwMlowLwYJKoZIhvcNAQkEMSIEICpkIWImbVOed+fyc52Sr18JzDELBbbea0FI1ljUg31hMIGOBgsqhkiG9w0BCRACLzF/MH0wezB5BCCF+Se8B6tiysO0Q1bBDvyBssaIP9p6uebYcNnROs0FtzBVMD2kOzA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDAKBggqhkjOPQQDAgRoMGYCMQD2N3QvdKUrb7mICl3pBDsE03F8HuaqvKzbzOufuc/onqDuhozEGeCO7WtAfnBimSYCMQDaZJiF4IvNVlXG7SnHGbx6kg6iHsKj9VRRlszRA/TNd923wrJtH7f1Kck8QNXzJgU="}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"DbIqoyjuP3CQCh8Tg0+xsqtBlTCGR8Nclh8QMNJ5wLo="}, "signature":"MEYCIQDC/f2u6BKMwlRySvtEgTuK+mEcLaPOYX6dXRAp5gR3+QIhAKZwmtJggvL3U0AwINd3sVatl7lB+z1F67WJmrmH9ZbA"}}
build/torch-cuda/modules/__init__.py CHANGED
@@ -5,25 +5,106 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- from ..modules.convolution import ImplicitLongConvolution, LongConvolution, ShortConvolution
 
 
 
 
 
9
  from ..modules.fused_bitlinear import BitLinear, FusedBitLinear
10
  from ..modules.fused_cross_entropy import FusedCrossEntropyLoss
11
  from ..modules.fused_kl_div import FusedKLDivLoss
12
  from ..modules.fused_linear_cross_entropy import FusedLinearCrossEntropyLoss
13
- from ..modules.fused_norm_gate import (
 
 
14
  FusedLayerNormGated,
15
  FusedLayerNormSwishGate,
16
  FusedLayerNormSwishGateLinear,
17
  FusedRMSNormGated,
18
  FusedRMSNormSwishGate,
19
  FusedRMSNormSwishGateLinear,
 
 
 
 
 
 
 
20
  )
21
- from ..modules.l2norm import L2Norm
22
- from ..modules.layernorm import GroupNorm, GroupNormLinear, LayerNorm, LayerNormLinear, RMSNorm, RMSNormLinear
23
- from ..modules.mlp import GatedMLP
24
  from ..modules.rotary import RotaryEmbedding
25
  from ..modules.token_shift import TokenShift
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  __all__ = [
28
  'BitLinear',
29
  'FusedBitLinear',
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ import sys
9
+ from importlib import import_module
10
+ from importlib.machinery import ModuleSpec
11
+ from types import ModuleType
12
+
13
+ from ..modules.causal_conv1d import ShortConvolution
14
  from ..modules.fused_bitlinear import BitLinear, FusedBitLinear
15
  from ..modules.fused_cross_entropy import FusedCrossEntropyLoss
16
  from ..modules.fused_kl_div import FusedKLDivLoss
17
  from ..modules.fused_linear_cross_entropy import FusedLinearCrossEntropyLoss
18
+ from ..modules.long_conv import ImplicitLongConvolution, LongConvolution
19
+ from ..modules.mlp import GatedMLP
20
+ from ..modules.norm import (
21
  FusedLayerNormGated,
22
  FusedLayerNormSwishGate,
23
  FusedLayerNormSwishGateLinear,
24
  FusedRMSNormGated,
25
  FusedRMSNormSwishGate,
26
  FusedRMSNormSwishGateLinear,
27
+ GroupNorm,
28
+ GroupNormLinear,
29
+ L2Norm,
30
+ LayerNorm,
31
+ LayerNormLinear,
32
+ RMSNorm,
33
+ RMSNormLinear,
34
  )
 
 
 
35
  from ..modules.rotary import RotaryEmbedding
36
  from ..modules.token_shift import TokenShift
37
 
38
+ _MODULE_ALIASES = {
39
+ 'fused_norm_gate': ('norm.fused_norm_gate',),
40
+ 'l2norm': ('norm.l2norm',),
41
+ 'layernorm': ('norm.layernorm',),
42
+ 'layernorm_gated': ('norm.layernorm_gated',),
43
+ 'convolution': ('causal_conv1d', 'long_conv', 'causal_conv1d.cp', 'causal_conv1d.backends.cuda'),
44
+ 'conv': ('causal_conv1d', 'long_conv'),
45
+ 'conv.causal_conv1d': ('causal_conv1d.ops',),
46
+ 'conv.short_conv': ('causal_conv1d.ops',),
47
+ 'conv.long_conv': ('long_conv',),
48
+ 'conv.cp': ('causal_conv1d.cp',),
49
+ 'conv.cp.ops': ('causal_conv1d.cp',),
50
+ 'conv.cuda': ('causal_conv1d.backends.cuda',),
51
+ 'conv.cuda.ops': ('causal_conv1d.backends.cuda.ops',),
52
+ 'conv.triton': ('causal_conv1d.ops',),
53
+ 'conv.triton.ops': ('causal_conv1d.ops',),
54
+ 'conv.triton.kernels': ('causal_conv1d.ops',),
55
+ }
56
+
57
+ for _old, _targets in _MODULE_ALIASES.items():
58
+ _fullname = f'{__name__}.{_old}'
59
+ if len(_targets) == 1:
60
+ _module = import_module(f'{__name__}.{_targets[0]}')
61
+ else:
62
+ _module = ModuleType(_fullname)
63
+ _module.__spec__ = ModuleSpec(_fullname, loader=None, is_package=True)
64
+ _module.__path__ = []
65
+ for _target in _targets:
66
+ _source = import_module(f'{__name__}.{_target}')
67
+ for _symbol in getattr(_source, '__all__', vars(_source)):
68
+ if not _symbol.startswith('_'):
69
+ vars(_module).setdefault(_symbol, getattr(_source, _symbol))
70
+ _module.__all__ = [name for name in vars(_module) if not name.startswith('_')]
71
+ sys.modules[_fullname] = _module
72
+ # conv.causal_conv1d is a function on the old package.
73
+ if _old != 'conv.causal_conv1d':
74
+ _parent, _, _child = _fullname.rpartition('.')
75
+ setattr(sys.modules[_parent], _child, _module)
76
+
77
+ # defer GRPO's compile policy until it is explicitly imported
78
+ for _name in ('activations', 'rotary', 'fused_cross_entropy', 'fused_kl_div', 'fused_linear_cross_entropy'):
79
+ _module = import_module(f'{__name__}.{_name}')
80
+ for _symbol, _value in vars(import_module(f'{__name__}.{_name}.ops')).items():
81
+ if not _symbol.startswith('_'):
82
+ vars(_module).setdefault(_symbol, _value)
83
+
84
+ _SYMBOL_ALIASES = {
85
+ 'fused_bitlinear': {
86
+ 'layer_norm_fwd_quant': 'norm.layernorm_quant.layer_norm_quant_fwd',
87
+ 'layer_norm_bwd': 'norm.layernorm_quant.layer_norm_quant_bwd',
88
+ 'LayerNormLinearQuantFn': 'LayerNormLinearQuantFunction',
89
+ 'layer_norm_linear_quant_fn': 'layer_norm_linear_quant',
90
+ },
91
+ 'fused_cross_entropy': {
92
+ 'fused_cross_entropy_forward': 'cross_entropy_fwd',
93
+ 'CrossEntropyLossFunction': 'FusedCrossEntropyFunction',
94
+ },
95
+ 'fused_kl_div': {'fused_kl_div_forward': 'fused_kl_div_fwd', 'fused_kl_div_backward': 'fused_kl_div_bwd'},
96
+ 'fused_linear_cross_entropy': {
97
+ 'fused_linear_cross_entropy_forward': 'fused_linear_cross_entropy_fwd',
98
+ 'fused_linear_cross_entropy_backward': 'fused_linear_cross_entropy_bwd',
99
+ },
100
+ }
101
+ for _name, _aliases in _SYMBOL_ALIASES.items():
102
+ _module = sys.modules[f'{__name__}.{_name}']
103
+ for _old, _new in _aliases.items():
104
+ _target, _, _symbol = _new.rpartition('.')
105
+ _source = import_module(f'{__name__}.{_target}') if _target else _module
106
+ setattr(_module, _old, getattr(_source, _symbol))
107
+
108
  __all__ = [
109
  'BitLinear',
110
  'FusedBitLinear',
build/{torch-xpu/ops/cp → torch-cuda/modules/activations}/__init__.py RENAMED
@@ -5,28 +5,35 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- # Context Parallel operators and utilities
9
-
10
- from .comm import (
11
- all_gather_into_tensor,
12
- all_reduce_sum,
13
- conv_cp_send_recv_bwd,
14
- conv_cp_send_recv_fwd,
15
- send_recv_bwd,
16
- send_recv_fwd,
17
- )
18
- from .context import (
19
- FLACPContext,
20
- build_cp_context,
 
21
  )
22
 
23
  __all__ = [
24
- "FLACPContext",
25
- "all_gather_into_tensor",
26
- "all_reduce_sum",
27
- "build_cp_context",
28
- "conv_cp_send_recv_bwd",
29
- "conv_cp_send_recv_fwd",
30
- "send_recv_bwd",
31
- "send_recv_fwd",
 
 
 
 
 
32
  ]
 
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ from ...modules.activations.ops import (
9
+ ACT2FN,
10
+ elu_p1,
11
+ fast_gelu_impl,
12
+ logsigmoid,
13
+ powglu,
14
+ powglu_linear,
15
+ sigmoid,
16
+ sigmoidglu,
17
+ sigmoidglu_linear,
18
+ sqrelu,
19
+ swiglu,
20
+ swiglu_linear,
21
+ swish,
22
  )
23
 
24
  __all__ = [
25
+ 'ACT2FN',
26
+ 'elu_p1',
27
+ 'fast_gelu_impl',
28
+ 'logsigmoid',
29
+ 'powglu',
30
+ 'powglu_linear',
31
+ 'sigmoid',
32
+ 'sigmoidglu',
33
+ 'sigmoidglu_linear',
34
+ 'sqrelu',
35
+ 'swiglu',
36
+ 'swiglu_linear',
37
+ 'swish',
38
  ]
39
+
build/torch-cuda/modules/{activations.py → activations/ops.py} RENAMED
@@ -8,43 +8,58 @@
8
  """Fused activation kernels.
9
 
10
  The kernels address their inputs through the row stride instead of assuming a fully contiguous buffer.
11
- An inner-contiguous input — such as one half of ``x.chunk(2, dim=-1)`` — is therefore read in place, sparing the extra
12
- ``.contiguous()`` copy (and its memory traffic) that a plain flat element-wise kernel would force on every call.
13
  """
14
 
15
  import torch
16
  import torch.nn.functional as F
17
  import triton
18
  import triton.language as tl
19
-
20
- from ..modules.backends import dispatch
21
- from ..ops.utils.op import exp, log
22
- from ..utils import IS_AMD, autocast_custom_bwd, autocast_custom_fwd, autotune_cache_kwargs, input_guard
23
-
24
- NUM_WARPS_AUTOTUNE = [1, 2, 4, 8, 16] if IS_AMD else [1, 2, 4, 8, 16, 32]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
 
27
  def _get_stride(x: torch.Tensor) -> int:
28
  """Get the row stride for viewing a tensor as 2D (num_rows, D) where D = shape[-1].
29
 
30
  Returns stride(-2) if the tensor is at least 2D, or 0 for 1D tensors.
31
- The caller must ensure the tensor is "inner-contiguous" (stride(-1) == 1 and
32
- higher dims are contiguous relative to dim -2) before using this value.
33
  """
34
  if x.ndim < 2:
35
  return 0
 
 
36
  return x.stride(-2)
37
 
38
 
39
  def _is_inner_contiguous(x: torch.Tensor) -> bool:
40
  """Check if a tensor can be safely viewed as 2D (num_rows, D) with row stride = stride(-2).
41
 
42
- This holds when stride(-1) == 1 and all dimensions above -2 are contiguous
43
- with respect to the dimension below them.
44
  """
45
  ndim = x.ndim
46
  if ndim < 2:
47
- return True
48
  if x.stride(-1) != 1:
49
  return False
50
  if ndim == 2:
@@ -69,7 +84,9 @@ def _is_inner_contiguous(x: torch.Tensor) -> bool:
69
 
70
  def _ensure_inner_contiguous(x: torch.Tensor) -> torch.Tensor:
71
  """Make the tensor inner-contiguous if it isn't already."""
72
- if _is_inner_contiguous(x):
 
 
73
  return x
74
  return x.contiguous()
75
 
@@ -77,32 +94,17 @@ def _ensure_inner_contiguous(x: torch.Tensor) -> torch.Tensor:
77
  def _alloc_output(x: torch.Tensor, contiguous: bool = False) -> torch.Tensor:
78
  """Allocate the output: a fresh contiguous buffer, or ``empty_like`` otherwise.
79
 
80
- ``empty_like`` keeps the input's memory format only when it is dense; a non-dense
81
- strided view (e.g. a ``chunk`` slice) falls back to contiguous, not the input stride.
82
  """
83
  if contiguous:
84
  return x.new_empty(x.shape)
85
  return torch.empty_like(x)
86
 
87
 
88
- @triton.autotune(
89
- configs=[
90
- triton.Config({'B': bs}, num_warps=num_warps)
91
- for bs in [512, 1024, 2048, 4096, 8192]
92
- for num_warps in NUM_WARPS_AUTOTUNE
93
- ],
94
- key=['D'],
95
- **autotune_cache_kwargs,
96
- )
97
  @triton.jit(do_not_specialize=['T'])
98
- def sigmoid_fwd_kernel(
99
- x, y,
100
- stride_x_row,
101
- stride_y_row,
102
- T,
103
- D: tl.constexpr,
104
- B: tl.constexpr,
105
- ):
106
  i_n = tl.program_id(0).to(tl.int64)
107
  offs = i_n * B + tl.arange(0, B)
108
  mask = offs < T
@@ -113,25 +115,9 @@ def sigmoid_fwd_kernel(
113
  tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
114
 
115
 
116
- @triton.autotune(
117
- configs=[
118
- triton.Config({'B': bs}, num_warps=num_warps)
119
- for bs in [512, 1024, 2048, 4096, 8192]
120
- for num_warps in NUM_WARPS_AUTOTUNE
121
- ],
122
- key=['D'],
123
- **autotune_cache_kwargs,
124
- )
125
  @triton.jit(do_not_specialize=['T'])
126
- def sigmoid_bwd_kernel(
127
- x, dy, dx,
128
- stride_x_row,
129
- stride_dy_row,
130
- stride_dx_row,
131
- T,
132
- D: tl.constexpr,
133
- B: tl.constexpr,
134
- ):
135
  i_n = tl.program_id(0).to(tl.int64)
136
  offs = i_n * B + tl.arange(0, B)
137
  mask = offs < T
@@ -144,35 +130,35 @@ def sigmoid_bwd_kernel(
144
  tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
145
 
146
 
147
- @dispatch('modules')
148
  def sigmoid_fwd(x: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
149
- x = _ensure_inner_contiguous(x)
150
  T, D = x.numel(), x.shape[-1]
151
- y = _alloc_output(x, output_contiguous)
152
  sigmoid_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
153
  x=x,
154
  y=y,
155
- stride_x_row=_get_stride(x),
156
- stride_y_row=_get_stride(y),
157
  T=T,
158
  D=D,
159
  )
160
  return y
161
 
162
 
163
- @dispatch('modules')
164
  def sigmoid_bwd(x: torch.Tensor, dy: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
165
- x = _ensure_inner_contiguous(x)
166
- dy = _ensure_inner_contiguous(dy)
167
  T, D = x.numel(), x.shape[-1]
168
- dx = _alloc_output(x, output_contiguous)
169
  sigmoid_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
170
  x=x,
171
  dy=dy,
172
  dx=dx,
173
- stride_x_row=_get_stride(x),
174
- stride_dy_row=_get_stride(dy),
175
- stride_dx_row=_get_stride(dx),
176
  T=T,
177
  D=D,
178
  )
@@ -185,38 +171,111 @@ class SigmoidFunction(torch.autograd.Function):
185
  @input_guard(no_guard_contiguous=True)
186
  def forward(ctx, x):
187
  ctx.save_for_backward(x)
188
- return sigmoid_fwd(x)
189
 
190
  @staticmethod
191
  @input_guard(no_guard_contiguous=True)
192
  def backward(ctx, dout):
193
  x, = ctx.saved_tensors
194
- return sigmoid_bwd(x, dout)
195
 
196
 
197
  sigmoid = SigmoidFunction.apply
198
 
199
 
200
- @triton.autotune(
201
- configs=[
202
- triton.Config({'B': bs}, num_warps=num_warps)
203
- for bs in [512, 1024, 2048, 4096, 8192]
204
- for num_warps in NUM_WARPS_AUTOTUNE
205
- ],
206
- key=['D'],
207
- **autotune_cache_kwargs,
208
- )
209
  @triton.jit(do_not_specialize=['T'])
210
- def logsigmoid_fwd_kernel(
211
- x,
212
- y,
213
- stride_x_row,
214
- stride_y_row,
215
- temperature,
216
- T,
217
- D: tl.constexpr,
218
- B: tl.constexpr,
219
- ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  i_n = tl.program_id(0).to(tl.int64)
221
  offs = i_n * B + tl.arange(0, B)
222
  mask = offs < T
@@ -229,15 +288,7 @@ def logsigmoid_fwd_kernel(
229
  tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
230
 
231
 
232
- @triton.autotune(
233
- configs=[
234
- triton.Config({'B': bs}, num_warps=num_warps)
235
- for bs in [512, 1024, 2048, 4096, 8192]
236
- for num_warps in NUM_WARPS_AUTOTUNE
237
- ],
238
- key=['D'],
239
- **autotune_cache_kwargs,
240
- )
241
  @triton.jit(do_not_specialize=['T'])
242
  def logsigmoid_bwd_kernel(
243
  x,
@@ -262,16 +313,16 @@ def logsigmoid_bwd_kernel(
262
  tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
263
 
264
 
265
- @dispatch('modules')
266
  def logsigmoid_fwd(x: torch.Tensor, temperature: float = 1., output_contiguous: bool = False) -> torch.Tensor:
267
- x = _ensure_inner_contiguous(x)
268
  T, D = x.numel(), x.shape[-1]
269
- y = _alloc_output(x, output_contiguous)
270
  logsigmoid_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
271
  x=x,
272
  y=y,
273
- stride_x_row=_get_stride(x),
274
- stride_y_row=_get_stride(y),
275
  temperature=temperature,
276
  T=T,
277
  D=D,
@@ -279,24 +330,24 @@ def logsigmoid_fwd(x: torch.Tensor, temperature: float = 1., output_contiguous:
279
  return y
280
 
281
 
282
- @dispatch('modules')
283
  def logsigmoid_bwd(
284
  x: torch.Tensor,
285
  dy: torch.Tensor,
286
  temperature: float = 1.,
287
  output_contiguous: bool = False,
288
  ) -> torch.Tensor:
289
- x = _ensure_inner_contiguous(x)
290
- dy = _ensure_inner_contiguous(dy)
291
  T, D = x.numel(), x.shape[-1]
292
- dx = _alloc_output(x, output_contiguous)
293
  logsigmoid_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
294
  x=x,
295
  dy=dy,
296
  dx=dx,
297
- stride_x_row=_get_stride(x),
298
- stride_dy_row=_get_stride(dy),
299
- stride_dx_row=_get_stride(dx),
300
  temperature=temperature,
301
  T=T,
302
  D=D,
@@ -311,37 +362,22 @@ class LogSigmoidFunction(torch.autograd.Function):
311
  def forward(ctx, x, temperature):
312
  ctx.save_for_backward(x)
313
  ctx.temperature = temperature
314
- return logsigmoid_fwd(x, temperature)
315
 
316
  @staticmethod
317
  @input_guard(no_guard_contiguous=True)
318
  def backward(ctx, dy):
319
  x, = ctx.saved_tensors
320
- return logsigmoid_bwd(x, dy, ctx.temperature), None
321
 
322
 
323
  def logsigmoid(x: torch.Tensor, temperature: float = 1.) -> torch.Tensor:
324
  return LogSigmoidFunction.apply(x, temperature)
325
 
326
 
327
- @triton.autotune(
328
- configs=[
329
- triton.Config({'B': bs}, num_warps=num_warps)
330
- for bs in [512, 1024, 2048, 4096, 8192]
331
- for num_warps in NUM_WARPS_AUTOTUNE
332
- ],
333
- key=['D'],
334
- **autotune_cache_kwargs,
335
- )
336
  @triton.jit(do_not_specialize=['T'])
337
- def swish_fwd_kernel(
338
- x, y,
339
- stride_x_row,
340
- stride_y_row,
341
- T,
342
- D: tl.constexpr,
343
- B: tl.constexpr,
344
- ):
345
  i_n = tl.program_id(0).to(tl.int64)
346
  offs = i_n * B + tl.arange(0, B)
347
  mask = offs < T
@@ -352,25 +388,9 @@ def swish_fwd_kernel(
352
  tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
353
 
354
 
355
- @triton.autotune(
356
- configs=[
357
- triton.Config({'B': bs}, num_warps=num_warps)
358
- for bs in [512, 1024, 2048, 4096, 8192]
359
- for num_warps in NUM_WARPS_AUTOTUNE
360
- ],
361
- key=['D'],
362
- **autotune_cache_kwargs,
363
- )
364
  @triton.jit(do_not_specialize=['T'])
365
- def swish_bwd_kernel(
366
- x, dy, dx,
367
- stride_x_row,
368
- stride_dy_row,
369
- stride_dx_row,
370
- T,
371
- D: tl.constexpr,
372
- B: tl.constexpr,
373
- ):
374
  i_n = tl.program_id(0).to(tl.int64)
375
  offs = i_n * B + tl.arange(0, B)
376
  mask = offs < T
@@ -383,35 +403,35 @@ def swish_bwd_kernel(
383
  tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
384
 
385
 
386
- @dispatch('modules')
387
  def swish_fwd(x: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
388
- x = _ensure_inner_contiguous(x)
389
  T, D = x.numel(), x.shape[-1]
390
- y = _alloc_output(x, output_contiguous)
391
  swish_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
392
  x=x,
393
  y=y,
394
- stride_x_row=_get_stride(x),
395
- stride_y_row=_get_stride(y),
396
  T=T,
397
  D=D,
398
  )
399
  return y
400
 
401
 
402
- @dispatch('modules')
403
  def swish_bwd(x: torch.Tensor, dy: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
404
- x = _ensure_inner_contiguous(x)
405
- dy = _ensure_inner_contiguous(dy)
406
  T, D = x.numel(), x.shape[-1]
407
- dx = _alloc_output(x, output_contiguous)
408
  swish_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
409
  x=x,
410
  dy=dy,
411
  dx=dx,
412
- stride_x_row=_get_stride(x),
413
- stride_dy_row=_get_stride(dy),
414
- stride_dx_row=_get_stride(dx),
415
  T=T,
416
  D=D,
417
  )
@@ -424,13 +444,13 @@ class SwishFunction(torch.autograd.Function):
424
  @input_guard(no_guard_contiguous=True)
425
  def forward(ctx, x):
426
  ctx.save_for_backward(x)
427
- return swish_fwd(x)
428
 
429
  @staticmethod
430
  @input_guard(no_guard_contiguous=True)
431
  def backward(ctx, dout):
432
  x, = ctx.saved_tensors
433
- return swish_bwd(x, dout)
434
 
435
 
436
  swish = SwishFunction.apply
@@ -440,27 +460,20 @@ swish = SwishFunction.apply
440
  # sqrt(2/pi) -> 0.79788456
441
 
442
 
443
- # this function is tanh approximation of gelu
444
- # actual gelu is:
445
- # x * 0.5 * (1.0 + torch.erf(x * 0.70710678))
446
  @torch.compile
447
  def bias_gelu(y, bias):
448
  x = bias + y
449
  return (x * 0.5 * (1.0 + torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x)))).to(dtype=y.dtype)
450
 
451
 
452
- # gradient of tanh approximation of gelu
453
- # gradient of actual gelu is:
454
- # 0.5 * (1. + torch.erf(x * 0.70710678)) + 0.3989423 * x * torch.exp(-0.5 * x * x)
455
  @torch.compile
456
  def bias_gelu_bwd(g, y, bias):
457
- """Assume that y has shape (B, D=D) and bias has shape (D)"""
458
  x = bias + y
459
  tanh_out = torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x))
460
  # sqrt(2/pi) * 3 * 0.044715 -> 0.1070322243
461
- ff = 0.5 * x * ((1 - tanh_out * tanh_out) * (0.79788456 + 0.1070322243 * x * x)) + 0.5 * (
462
- 1 + tanh_out
463
- )
464
  grad_y = ff * g
465
  return grad_y.to(dtype=y.dtype), grad_y.sum(dim=(0), dtype=bias.dtype)
466
 
@@ -468,54 +481,43 @@ def bias_gelu_bwd(g, y, bias):
468
  class GeLUFunction(torch.autograd.Function):
469
 
470
  @staticmethod
471
- # bias is an optional argument
472
  def forward(ctx, input, bias):
473
  ctx.save_for_backward(input, bias)
474
- return bias_gelu(input, bias)
475
 
476
  @staticmethod
477
  def backward(ctx, grad_output):
478
  input, bias = ctx.saved_tensors
479
- return bias_gelu_bwd(grad_output, input, bias)
480
 
481
 
482
  bias_gelu_impl = GeLUFunction.apply
483
 
484
 
485
- # this function is tanh approximation of gelu
486
- # actual gelu is:
487
- # x * 0.5 * (1.0 + torch.erf(x * 0.70710678))
488
- @dispatch('modules')
489
  @torch.compile
490
  def gelu_fwd(x):
491
  return (x * 0.5 * (1.0 + torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x)))).to(dtype=x.dtype)
492
 
493
 
494
- # gradient of tanh approximation of gelu
495
- # gradient of actual gelu is:
496
- # 0.5 * (1. + torch.erf(x * 0.70710678)) + 0.3989423 * x * torch.exp(-0.5 * x * x)
497
- @dispatch('modules')
498
  @torch.compile
499
  def gelu_bwd(g, x):
500
  tanh_out = torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x))
501
  # sqrt(2/pi) * 3 * 0.044715 -> 0.1070322243
502
- ff = 0.5 * x * ((1 - tanh_out * tanh_out) * (0.79788456 + 0.1070322243 * x * x)) + 0.5 * (
503
- 1 + tanh_out
504
- )
505
  return (ff * g).to(dtype=x.dtype)
506
 
507
 
508
  class FastGeLUFunction(torch.autograd.Function):
509
  @staticmethod
510
- # bias is an optional argument
511
  def forward(ctx, input):
512
  ctx.save_for_backward(input)
513
- return gelu_fwd(input)
514
 
515
  @staticmethod
516
  def backward(ctx, grad_output):
517
  (input,) = ctx.saved_tensors
518
- tmp = gelu_bwd(grad_output, input)
519
  return tmp
520
 
521
 
@@ -527,14 +529,12 @@ def relu_bwd(g, x):
527
  return torch.where(x >= 0, g, 0.0).to(dtype=x.dtype)
528
 
529
 
530
- @dispatch('modules')
531
  @torch.compile
532
  def sqrelu_fwd(x):
533
  r = F.relu(x.float())
534
  return (r * r).to(dtype=x.dtype)
535
 
536
 
537
- @dispatch('modules')
538
  @torch.compile
539
  def sqrelu_bwd(g, x):
540
  return (2.0 * g * F.relu(x.float())).to(dtype=x.dtype)
@@ -545,36 +545,20 @@ class SquaredReLUFunction(torch.autograd.Function):
545
  @staticmethod
546
  def forward(ctx, input):
547
  ctx.save_for_backward(input)
548
- return sqrelu_fwd(input)
549
 
550
  @staticmethod
551
  def backward(ctx, grad_output):
552
  input, = ctx.saved_tensors
553
- return sqrelu_bwd(grad_output, input)
554
 
555
 
556
  sqrelu = SquaredReLUFunction.apply
557
 
558
 
559
- @triton.autotune(
560
- configs=[
561
- triton.Config({'B': bs}, num_warps=num_warps)
562
- for bs in [512, 1024, 2048, 4096, 8192]
563
- for num_warps in NUM_WARPS_AUTOTUNE
564
- ],
565
- key=['D'],
566
- **autotune_cache_kwargs,
567
- )
568
  @triton.jit(do_not_specialize=['T'])
569
- def swiglu_fwd_kernel(
570
- x, y, z,
571
- stride_x_row,
572
- stride_y_row,
573
- stride_z_row,
574
- T,
575
- D: tl.constexpr,
576
- B: tl.constexpr,
577
- ):
578
  i_n = tl.program_id(0).to(tl.int64)
579
  offs = i_n * B + tl.arange(0, B)
580
  mask = offs < T
@@ -586,21 +570,16 @@ def swiglu_fwd_kernel(
586
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
587
 
588
 
589
- @triton.heuristics({
590
- 'HAS_WEIGHT': lambda args: args['z'] is not None,
591
- })
592
- @triton.autotune(
593
- configs=[
594
- triton.Config({'B': bs}, num_warps=num_warps)
595
- for bs in [512, 1024, 2048, 4096, 8192]
596
- for num_warps in NUM_WARPS_AUTOTUNE
597
- ],
598
- key=['D'],
599
- **autotune_cache_kwargs,
600
- )
601
  @triton.jit(do_not_specialize=['T'])
602
  def swiglu_fwdbwd_kernel(
603
- x, y, g, dx, dy, z,
 
 
 
 
 
604
  stride_x_row,
605
  stride_y_row,
606
  stride_g_row,
@@ -633,27 +612,27 @@ def swiglu_fwdbwd_kernel(
633
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
634
 
635
 
636
- @dispatch('modules')
637
  def swiglu_fwd(x: torch.Tensor, y: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
638
  assert x.shape == y.shape, f"swiglu_fwd: shape mismatch x={x.shape} y={y.shape}"
639
- x = _ensure_inner_contiguous(x)
640
- y = _ensure_inner_contiguous(y)
641
  T, D = x.numel(), x.shape[-1]
642
- z = _alloc_output(x, output_contiguous)
643
  swiglu_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
644
  x=x,
645
  y=y,
646
  z=z,
647
- stride_x_row=_get_stride(x),
648
- stride_y_row=_get_stride(y),
649
- stride_z_row=_get_stride(z),
650
  T=T,
651
  D=D,
652
  )
653
  return z
654
 
655
 
656
- @dispatch('modules')
657
  def swiglu_fwdbwd(
658
  x: torch.Tensor,
659
  y: torch.Tensor,
@@ -662,14 +641,14 @@ def swiglu_fwdbwd(
662
  output_contiguous: bool = False,
663
  ):
664
  assert x.shape == y.shape == g.shape, f"swiglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
665
- x = _ensure_inner_contiguous(x)
666
- y = _ensure_inner_contiguous(y)
667
- g = _ensure_inner_contiguous(g)
668
  T, D = x.numel(), x.shape[-1]
669
- dx = _alloc_output(x, output_contiguous)
670
- dy = _alloc_output(y, output_contiguous)
671
  if use_weight:
672
- z = _alloc_output(x, output_contiguous)
673
  else:
674
  z = None
675
  swiglu_fwdbwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
@@ -679,12 +658,12 @@ def swiglu_fwdbwd(
679
  dx=dx,
680
  dy=dy,
681
  z=z,
682
- stride_x_row=_get_stride(x),
683
- stride_y_row=_get_stride(y),
684
- stride_g_row=_get_stride(g),
685
- stride_dx_row=_get_stride(dx),
686
- stride_dy_row=_get_stride(dy),
687
- stride_z_row=_get_stride(z) if z is not None else 0,
688
  T=T,
689
  D=D,
690
  )
@@ -705,13 +684,13 @@ class SwiGLUFunction(torch.autograd.Function):
705
  @input_guard(no_guard_contiguous=True)
706
  def forward(ctx, x, y):
707
  ctx.save_for_backward(x, y)
708
- return swiglu_fwd(x, y)
709
 
710
  @staticmethod
711
  @input_guard(no_guard_contiguous=True)
712
  def backward(ctx, dout):
713
  x, y = ctx.saved_tensors
714
- return swiglu_fwdbwd(x, y, dout)
715
 
716
 
717
  class SwiGLULinearFunction(torch.autograd.Function):
@@ -728,10 +707,9 @@ class SwiGLULinearFunction(torch.autograd.Function):
728
  @input_guard(no_guard_contiguous=True)
729
  @autocast_custom_fwd
730
  def forward(ctx, x, y, weight, bias):
731
- z = swiglu_fwd(x, y, output_contiguous=True)
732
  out = F.linear(z, weight, bias)
733
  ctx.save_for_backward(x, y, weight)
734
- ctx.linear_bias_is_none = bias is None
735
  return out
736
 
737
  @staticmethod
@@ -740,40 +718,32 @@ class SwiGLULinearFunction(torch.autograd.Function):
740
  def backward(ctx, dout, *args):
741
  x, y, weight = ctx.saved_tensors
742
  dout = dout.reshape(-1, dout.shape[-1])
743
- dz = F.linear(dout, weight.t()).view_as(x)
744
- dx, dy, z = swiglu_fwdbwd(x, y, dz, use_weight=True, output_contiguous=True)
745
- dlinear_weight = torch.einsum("bo,bi->oi", dout, z.reshape(-1, z.shape[-1]))
746
- dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
 
 
 
 
 
 
 
 
747
  return dx, dy, dlinear_weight, dlinear_bias
748
 
749
 
750
  swiglu = SwiGLUFunction.apply
751
 
752
 
753
- @dispatch('modules')
754
  def swiglu_linear(x, y, weight, bias):
755
  return SwiGLULinearFunction.apply(x, y, weight, bias)
756
 
757
 
758
- @triton.autotune(
759
- configs=[
760
- triton.Config({'B': bs}, num_warps=num_warps)
761
- for bs in [512, 1024, 2048, 4096, 8192]
762
- for num_warps in NUM_WARPS_AUTOTUNE
763
- ],
764
- key=['D'],
765
- **autotune_cache_kwargs,
766
- )
767
  @triton.jit(do_not_specialize=['T'])
768
- def sigmoidglu_fwd_kernel(
769
- x, y, z,
770
- stride_x_row,
771
- stride_y_row,
772
- stride_z_row,
773
- T,
774
- D: tl.constexpr,
775
- B: tl.constexpr,
776
- ):
777
  i_n = tl.program_id(0).to(tl.int64)
778
  offs = i_n * B + tl.arange(0, B)
779
  mask = offs < T
@@ -785,21 +755,16 @@ def sigmoidglu_fwd_kernel(
785
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
786
 
787
 
788
- @triton.heuristics({
789
- 'HAS_WEIGHT': lambda args: args['z'] is not None,
790
- })
791
- @triton.autotune(
792
- configs=[
793
- triton.Config({'B': bs}, num_warps=num_warps)
794
- for bs in [512, 1024, 2048, 4096, 8192]
795
- for num_warps in NUM_WARPS_AUTOTUNE
796
- ],
797
- key=['D'],
798
- **autotune_cache_kwargs,
799
- )
800
  @triton.jit(do_not_specialize=['T'])
801
  def sigmoidglu_fwdbwd_kernel(
802
- x, y, g, dx, dy, z,
 
 
 
 
 
803
  stride_x_row,
804
  stride_y_row,
805
  stride_g_row,
@@ -834,17 +799,17 @@ def sigmoidglu_fwdbwd_kernel(
834
  @torch.compiler.disable
835
  def sigmoidglu_fwd(x: torch.Tensor, y: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
836
  assert x.shape == y.shape, f"sigmoidglu_fwd: shape mismatch x={x.shape} y={y.shape}"
837
- x = _ensure_inner_contiguous(x)
838
- y = _ensure_inner_contiguous(y)
839
  T, D = x.numel(), x.shape[-1]
840
- z = _alloc_output(x, output_contiguous)
841
  sigmoidglu_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
842
  x=x,
843
  y=y,
844
  z=z,
845
- stride_x_row=_get_stride(x),
846
- stride_y_row=_get_stride(y),
847
- stride_z_row=_get_stride(z),
848
  T=T,
849
  D=D,
850
  )
@@ -860,14 +825,14 @@ def sigmoidglu_fwdbwd(
860
  output_contiguous: bool = False,
861
  ):
862
  assert x.shape == y.shape == g.shape, f"sigmoidglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
863
- x = _ensure_inner_contiguous(x)
864
- y = _ensure_inner_contiguous(y)
865
- g = _ensure_inner_contiguous(g)
866
  T, D = x.numel(), x.shape[-1]
867
- dx = _alloc_output(x, output_contiguous)
868
- dy = _alloc_output(y, output_contiguous)
869
  if use_weight:
870
- z = _alloc_output(x, output_contiguous)
871
  else:
872
  z = None
873
  sigmoidglu_fwdbwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
@@ -877,12 +842,12 @@ def sigmoidglu_fwdbwd(
877
  dx=dx,
878
  dy=dy,
879
  z=z,
880
- stride_x_row=_get_stride(x),
881
- stride_y_row=_get_stride(y),
882
- stride_g_row=_get_stride(g),
883
- stride_dx_row=_get_stride(dx),
884
- stride_dy_row=_get_stride(dy),
885
- stride_z_row=_get_stride(z) if z is not None else 0,
886
  T=T,
887
  D=D,
888
  )
@@ -903,13 +868,13 @@ class SigmoidGLUFunction(torch.autograd.Function):
903
  @input_guard(no_guard_contiguous=True)
904
  def forward(ctx, x, y):
905
  ctx.save_for_backward(x, y)
906
- return sigmoidglu_fwd(x, y)
907
 
908
  @staticmethod
909
  @input_guard(no_guard_contiguous=True)
910
  def backward(ctx, dout):
911
  x, y = ctx.saved_tensors
912
- return sigmoidglu_fwdbwd(x, y, dout)
913
 
914
 
915
  class SigmoidGLULinearFunction(torch.autograd.Function):
@@ -926,7 +891,7 @@ class SigmoidGLULinearFunction(torch.autograd.Function):
926
  @input_guard(no_guard_contiguous=True)
927
  @autocast_custom_fwd
928
  def forward(ctx, x, y, weight, bias):
929
- z = sigmoidglu_fwd(x, y, output_contiguous=True)
930
  out = F.linear(z, weight, bias)
931
  ctx.save_for_backward(x, y, weight)
932
  ctx.linear_bias_is_none = bias is None
@@ -939,7 +904,7 @@ class SigmoidGLULinearFunction(torch.autograd.Function):
939
  x, y, weight = ctx.saved_tensors
940
  dout = dout.reshape(-1, dout.shape[-1])
941
  dz = F.linear(dout, weight.t()).view_as(x)
942
- dx, dy, z = sigmoidglu_fwdbwd(x, y, dz, use_weight=True, output_contiguous=True)
943
  dlinear_weight = torch.einsum("bo,bi->oi", dout, z.reshape(-1, z.shape[-1]))
944
  dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
945
  return dx, dy, dlinear_weight, dlinear_bias
@@ -951,26 +916,9 @@ sigmoidglu = SigmoidGLUFunction.apply
951
  sigmoidglu_linear = SigmoidGLULinearFunction.apply
952
 
953
 
954
- @triton.autotune(
955
- configs=[
956
- triton.Config({'B': bs}, num_warps=num_warps)
957
- for bs in [512, 1024, 2048, 4096, 8192]
958
- for num_warps in NUM_WARPS_AUTOTUNE
959
- ],
960
- key=['D'],
961
- **autotune_cache_kwargs,
962
- )
963
  @triton.jit(do_not_specialize=['T'])
964
- def powglu_fwd_kernel(
965
- x, y, z,
966
- stride_x_row,
967
- stride_y_row,
968
- stride_z_row,
969
- m,
970
- T,
971
- D: tl.constexpr,
972
- B: tl.constexpr,
973
- ):
974
  i_n = tl.program_id(0).to(tl.int64)
975
  offs = i_n * B + tl.arange(0, B)
976
  mask = offs < T
@@ -990,21 +938,16 @@ def powglu_fwd_kernel(
990
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
991
 
992
 
993
- @triton.heuristics({
994
- 'HAS_WEIGHT': lambda args: args['z'] is not None,
995
- })
996
- @triton.autotune(
997
- configs=[
998
- triton.Config({'B': bs}, num_warps=num_warps)
999
- for bs in [512, 1024, 2048, 4096, 8192]
1000
- for num_warps in NUM_WARPS_AUTOTUNE
1001
- ],
1002
- key=['D'],
1003
- **autotune_cache_kwargs,
1004
- )
1005
  @triton.jit(do_not_specialize=['T'])
1006
  def powglu_fwdbwd_kernel(
1007
- x, y, g, dx, dy, z,
 
 
 
 
 
1008
  stride_x_row,
1009
  stride_y_row,
1010
  stride_g_row,
@@ -1054,20 +997,20 @@ def powglu_fwdbwd_kernel(
1054
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
1055
 
1056
 
1057
- @dispatch('modules')
1058
  def powglu_fwd(x: torch.Tensor, y: torch.Tensor, power: float = 3.0, output_contiguous: bool = False) -> torch.Tensor:
1059
  assert x.shape == y.shape, f"powglu_fwd: shape mismatch x={x.shape} y={y.shape}"
1060
- x = _ensure_inner_contiguous(x)
1061
- y = _ensure_inner_contiguous(y)
1062
  T, D = x.numel(), x.shape[-1]
1063
- z = _alloc_output(x, output_contiguous)
1064
  powglu_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
1065
  x=x,
1066
  y=y,
1067
  z=z,
1068
- stride_x_row=_get_stride(x),
1069
- stride_y_row=_get_stride(y),
1070
- stride_z_row=_get_stride(z),
1071
  m=power,
1072
  T=T,
1073
  D=D,
@@ -1075,7 +1018,7 @@ def powglu_fwd(x: torch.Tensor, y: torch.Tensor, power: float = 3.0, output_cont
1075
  return z
1076
 
1077
 
1078
- @dispatch('modules')
1079
  def powglu_fwdbwd(
1080
  x: torch.Tensor,
1081
  y: torch.Tensor,
@@ -1085,14 +1028,14 @@ def powglu_fwdbwd(
1085
  output_contiguous: bool = False,
1086
  ):
1087
  assert x.shape == y.shape == g.shape, f"powglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
1088
- x = _ensure_inner_contiguous(x)
1089
- y = _ensure_inner_contiguous(y)
1090
- g = _ensure_inner_contiguous(g)
1091
  T, D = x.numel(), x.shape[-1]
1092
- dx = _alloc_output(x, output_contiguous)
1093
- dy = _alloc_output(y, output_contiguous)
1094
  if use_weight:
1095
- z = _alloc_output(x, output_contiguous)
1096
  else:
1097
  z = None
1098
  powglu_fwdbwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
@@ -1102,12 +1045,12 @@ def powglu_fwdbwd(
1102
  dx=dx,
1103
  dy=dy,
1104
  z=z,
1105
- stride_x_row=_get_stride(x),
1106
- stride_y_row=_get_stride(y),
1107
- stride_g_row=_get_stride(g),
1108
- stride_dx_row=_get_stride(dx),
1109
- stride_dy_row=_get_stride(dy),
1110
- stride_z_row=_get_stride(z) if z is not None else 0,
1111
  m=power,
1112
  T=T,
1113
  D=D,
@@ -1125,8 +1068,8 @@ class PowGLUFunction(torch.autograd.Function):
1125
  \text{PowGLU}(x, y) = g(x) * y,\quad
1126
  g(x) = \begin{cases} x^{power/(\sqrt{x}+1)}\,\sigma(x) & x > 0 \\ x\,\sigma(x) & x \le 0 \end{cases}
1127
 
1128
- For ``x <= 0`` the gate reduces to swish, matching SwiGLU; for large ``x > 0`` it saturates instead of
1129
- growing, replacing SwiGLU's quadratic amplification with bounded growth (Power Linear Unit, arXiv:2605.25704).
1130
  """
1131
 
1132
  @staticmethod
@@ -1134,13 +1077,13 @@ class PowGLUFunction(torch.autograd.Function):
1134
  def forward(ctx, x, y, power):
1135
  ctx.save_for_backward(x, y)
1136
  ctx.power = power
1137
- return powglu_fwd(x, y, power)
1138
 
1139
  @staticmethod
1140
  @input_guard(no_guard_contiguous=True)
1141
  def backward(ctx, dout):
1142
  x, y = ctx.saved_tensors
1143
- dx, dy = powglu_fwdbwd(x, y, dout, ctx.power)
1144
  return dx, dy, None
1145
 
1146
 
@@ -1158,7 +1101,7 @@ class PowGLULinearFunction(torch.autograd.Function):
1158
  @input_guard(no_guard_contiguous=True)
1159
  @autocast_custom_fwd
1160
  def forward(ctx, x, y, weight, bias, power):
1161
- z = powglu_fwd(x, y, power, output_contiguous=True)
1162
  out = F.linear(z, weight, bias)
1163
  ctx.save_for_backward(x, y, weight)
1164
  ctx.linear_bias_is_none = bias is None
@@ -1172,7 +1115,7 @@ class PowGLULinearFunction(torch.autograd.Function):
1172
  x, y, weight = ctx.saved_tensors
1173
  dout = dout.reshape(-1, dout.shape[-1])
1174
  dz = F.linear(dout, weight.t()).view_as(x)
1175
- dx, dy, z = powglu_fwdbwd(x, y, dz, ctx.power, use_weight=True, output_contiguous=True)
1176
  dlinear_weight = torch.einsum("bo,bi->oi", dout, z.reshape(-1, z.shape[-1]))
1177
  dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
1178
  return dx, dy, dlinear_weight, dlinear_bias, None
@@ -1182,7 +1125,7 @@ def powglu(x: torch.Tensor, y: torch.Tensor, power: float = 3.0) -> torch.Tensor
1182
  return PowGLUFunction.apply(x, y, power)
1183
 
1184
 
1185
- @dispatch('modules')
1186
  def powglu_linear(
1187
  x: torch.Tensor,
1188
  y: torch.Tensor,
 
8
  """Fused activation kernels.
9
 
10
  The kernels address their inputs through the row stride instead of assuming a fully contiguous buffer.
11
+ An inner-contiguous input — such as one half of ``x.chunk(2, dim=-1)`` — is therefore read in place,
12
+ avoiding the ``.contiguous()`` copy and memory traffic that a flat element-wise kernel would require on every call.
13
  """
14
 
15
  import torch
16
  import torch.nn.functional as F
17
  import triton
18
  import triton.language as tl
19
+ from triton.language.extra import libdevice
20
+
21
+ from ...backends import dispatch
22
+ from ...ops.utils.op import exp, log
23
+ from ...utils import IS_AMD, IS_INTEL, IS_NPU, autocast_custom_bwd, autocast_custom_fwd, autotune_cache_kwargs, input_guard
24
+
25
+
26
+ def _activation_autotune_configs():
27
+ """Return autotune configs scaled to the device's scratch-space budget."""
28
+ if IS_INTEL:
29
+ # Intel/XPU has a smaller per-thread scratch space (PTSS) limit; smaller
30
+ # block sizes and warp counts avoid register spilling on larger D.
31
+ bs_list = [512, 1024]
32
+ nw_list = [1, 2, 4, 8]
33
+ elif IS_AMD:
34
+ bs_list = [512, 1024, 2048, 4096, 8192]
35
+ nw_list = [1, 2, 4, 8, 16]
36
+ else:
37
+ bs_list = [512, 1024, 2048, 4096, 8192]
38
+ nw_list = [1, 2, 4, 8, 16, 32]
39
+ return [triton.Config({'B': bs}, num_warps=nw) for bs in bs_list for nw in nw_list]
40
 
41
 
42
  def _get_stride(x: torch.Tensor) -> int:
43
  """Get the row stride for viewing a tensor as 2D (num_rows, D) where D = shape[-1].
44
 
45
  Returns stride(-2) if the tensor is at least 2D, or 0 for 1D tensors.
46
+ The caller must ensure stride(-1) == 1, with higher dimensions contiguous relative to dimension -2.
 
47
  """
48
  if x.ndim < 2:
49
  return 0
50
+ if torch.compiler.is_compiling():
51
+ return x.shape[-1]
52
  return x.stride(-2)
53
 
54
 
55
  def _is_inner_contiguous(x: torch.Tensor) -> bool:
56
  """Check if a tensor can be safely viewed as 2D (num_rows, D) with row stride = stride(-2).
57
 
58
+ This holds when stride(-1) == 1 and all dimensions above -2 are contiguous with respect to the dimension below them.
 
59
  """
60
  ndim = x.ndim
61
  if ndim < 2:
62
+ return x.is_contiguous()
63
  if x.stride(-1) != 1:
64
  return False
65
  if ndim == 2:
 
84
 
85
  def _ensure_inner_contiguous(x: torch.Tensor) -> torch.Tensor:
86
  """Make the tensor inner-contiguous if it isn't already."""
87
+ if torch.compiler.is_compiling():
88
+ return x.contiguous()
89
+ if _is_inner_contiguous(x=x):
90
  return x
91
  return x.contiguous()
92
 
 
94
  def _alloc_output(x: torch.Tensor, contiguous: bool = False) -> torch.Tensor:
95
  """Allocate the output: a fresh contiguous buffer, or ``empty_like`` otherwise.
96
 
97
+ ``empty_like`` keeps the input's memory format only when it is dense;
98
+ a non-dense strided view (e.g. a ``chunk`` slice) falls back to contiguous, not the input stride.
99
  """
100
  if contiguous:
101
  return x.new_empty(x.shape)
102
  return torch.empty_like(x)
103
 
104
 
105
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
106
  @triton.jit(do_not_specialize=['T'])
107
+ def sigmoid_fwd_kernel(x, y, stride_x_row, stride_y_row, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
108
  i_n = tl.program_id(0).to(tl.int64)
109
  offs = i_n * B + tl.arange(0, B)
110
  mask = offs < T
 
115
  tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
116
 
117
 
118
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
119
  @triton.jit(do_not_specialize=['T'])
120
+ def sigmoid_bwd_kernel(x, dy, dx, stride_x_row, stride_dy_row, stride_dx_row, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
 
121
  i_n = tl.program_id(0).to(tl.int64)
122
  offs = i_n * B + tl.arange(0, B)
123
  mask = offs < T
 
130
  tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
131
 
132
 
133
+ @dispatch
134
  def sigmoid_fwd(x: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
135
+ x = _ensure_inner_contiguous(x=x)
136
  T, D = x.numel(), x.shape[-1]
137
+ y = _alloc_output(x=x, contiguous=output_contiguous)
138
  sigmoid_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
139
  x=x,
140
  y=y,
141
+ stride_x_row=_get_stride(x=x),
142
+ stride_y_row=_get_stride(x=y),
143
  T=T,
144
  D=D,
145
  )
146
  return y
147
 
148
 
149
+ @dispatch
150
  def sigmoid_bwd(x: torch.Tensor, dy: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
151
+ x = _ensure_inner_contiguous(x=x)
152
+ dy = _ensure_inner_contiguous(x=dy)
153
  T, D = x.numel(), x.shape[-1]
154
+ dx = _alloc_output(x=x, contiguous=output_contiguous)
155
  sigmoid_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
156
  x=x,
157
  dy=dy,
158
  dx=dx,
159
+ stride_x_row=_get_stride(x=x),
160
+ stride_dy_row=_get_stride(x=dy),
161
+ stride_dx_row=_get_stride(x=dx),
162
  T=T,
163
  D=D,
164
  )
 
171
  @input_guard(no_guard_contiguous=True)
172
  def forward(ctx, x):
173
  ctx.save_for_backward(x)
174
+ return sigmoid_fwd(x=x)
175
 
176
  @staticmethod
177
  @input_guard(no_guard_contiguous=True)
178
  def backward(ctx, dout):
179
  x, = ctx.saved_tensors
180
+ return sigmoid_bwd(x=x, dy=dout)
181
 
182
 
183
  sigmoid = SigmoidFunction.apply
184
 
185
 
186
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
187
  @triton.jit(do_not_specialize=['T'])
188
+ def elu_p1_fwd_kernel(x, y, stride_x_row, stride_y_row, T, D: tl.constexpr, B: tl.constexpr):
189
+ i_n = tl.program_id(0).to(tl.int64)
190
+ offs = i_n * B + tl.arange(0, B)
191
+ mask = offs < T
192
+ row = offs // D
193
+ col = offs % D
194
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
195
+ # libdevice preserves representable subnormal exponentials
196
+ b_y = tl.where(b_x >= 0, b_x + 1., libdevice.exp(tl.minimum(b_x, 0.)))
197
+ tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
198
+
199
+
200
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
201
+ @triton.jit(do_not_specialize=['T'])
202
+ def elu_p1_bwd_kernel(x, dy, dx, stride_x_row, stride_dy_row, stride_dx_row, T, D: tl.constexpr, B: tl.constexpr):
203
+ i_n = tl.program_id(0).to(tl.int64)
204
+ offs = i_n * B + tl.arange(0, B)
205
+ mask = offs < T
206
+ row = offs // D
207
+ col = offs % D
208
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
209
+ b_dy = tl.load(dy + row * stride_dy_row + col, mask=mask, other=0.).to(tl.float32)
210
+ b_dx = b_dy * libdevice.exp(tl.minimum(b_x, 0.))
211
+ tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
212
+
213
+
214
+ @dispatch
215
+ def elu_p1_fwd(x: torch.Tensor) -> torch.Tensor:
216
+ x = x.contiguous() if x.ndim < 2 else _ensure_inner_contiguous(x=x)
217
+ T, D = x.numel(), x.shape[-1] if x.ndim else 1
218
+ y = _alloc_output(x=x)
219
+ if T > 0:
220
+ elu_p1_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
221
+ x=x,
222
+ y=y,
223
+ stride_x_row=_get_stride(x=x),
224
+ stride_y_row=_get_stride(x=y),
225
+ T=T,
226
+ D=D,
227
+ )
228
+ return y
229
+
230
+
231
+ @dispatch
232
+ def elu_p1_bwd(x: torch.Tensor, dy: torch.Tensor) -> torch.Tensor:
233
+ x = x.contiguous() if x.ndim < 2 else _ensure_inner_contiguous(x=x)
234
+ dy = dy.contiguous() if dy.ndim < 2 else _ensure_inner_contiguous(x=dy)
235
+ T, D = x.numel(), x.shape[-1] if x.ndim else 1
236
+ dx = _alloc_output(x=x)
237
+ if T > 0:
238
+ elu_p1_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
239
+ x=x,
240
+ dy=dy,
241
+ dx=dx,
242
+ stride_x_row=_get_stride(x=x),
243
+ stride_dy_row=_get_stride(x=dy),
244
+ stride_dx_row=_get_stride(x=dx),
245
+ T=T,
246
+ D=D,
247
+ )
248
+ return dx
249
+
250
+
251
+ class ELUPlusOneFunction(torch.autograd.Function):
252
+
253
+ @staticmethod
254
+ @input_guard(no_guard_contiguous=True)
255
+ @autocast_custom_fwd
256
+ def forward(ctx, x):
257
+ ctx.save_for_backward(x)
258
+ return elu_p1_fwd(x=x)
259
+
260
+ @staticmethod
261
+ @input_guard(no_guard_contiguous=True)
262
+ @autocast_custom_bwd
263
+ def backward(ctx, dout):
264
+ x, = ctx.saved_tensors
265
+ return elu_p1_bwd(x=x, dy=dout)
266
+
267
+
268
+ def elu_p1(x: torch.Tensor) -> torch.Tensor:
269
+ """Compute ELU + 1 without cancellation in the negative branch."""
270
+ # triton-ascend 3.2.2 does not implement libdevice.exp
271
+ if IS_NPU or x.device.type == 'cpu' or x.dtype == torch.float64:
272
+ return torch.where(x >= 0, x + 1, x.clamp_max(0).exp()).to(x.dtype)
273
+ return ELUPlusOneFunction.apply(x)
274
+
275
+
276
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
277
+ @triton.jit(do_not_specialize=['T'])
278
+ def logsigmoid_fwd_kernel(x, y, stride_x_row, stride_y_row, temperature, T, D: tl.constexpr, B: tl.constexpr):
279
  i_n = tl.program_id(0).to(tl.int64)
280
  offs = i_n * B + tl.arange(0, B)
281
  mask = offs < T
 
288
  tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
289
 
290
 
291
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
292
  @triton.jit(do_not_specialize=['T'])
293
  def logsigmoid_bwd_kernel(
294
  x,
 
313
  tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
314
 
315
 
316
+ @dispatch
317
  def logsigmoid_fwd(x: torch.Tensor, temperature: float = 1., output_contiguous: bool = False) -> torch.Tensor:
318
+ x = _ensure_inner_contiguous(x=x)
319
  T, D = x.numel(), x.shape[-1]
320
+ y = _alloc_output(x=x, contiguous=output_contiguous)
321
  logsigmoid_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
322
  x=x,
323
  y=y,
324
+ stride_x_row=_get_stride(x=x),
325
+ stride_y_row=_get_stride(x=y),
326
  temperature=temperature,
327
  T=T,
328
  D=D,
 
330
  return y
331
 
332
 
333
+ @dispatch
334
  def logsigmoid_bwd(
335
  x: torch.Tensor,
336
  dy: torch.Tensor,
337
  temperature: float = 1.,
338
  output_contiguous: bool = False,
339
  ) -> torch.Tensor:
340
+ x = _ensure_inner_contiguous(x=x)
341
+ dy = _ensure_inner_contiguous(x=dy)
342
  T, D = x.numel(), x.shape[-1]
343
+ dx = _alloc_output(x=x, contiguous=output_contiguous)
344
  logsigmoid_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
345
  x=x,
346
  dy=dy,
347
  dx=dx,
348
+ stride_x_row=_get_stride(x=x),
349
+ stride_dy_row=_get_stride(x=dy),
350
+ stride_dx_row=_get_stride(x=dx),
351
  temperature=temperature,
352
  T=T,
353
  D=D,
 
362
  def forward(ctx, x, temperature):
363
  ctx.save_for_backward(x)
364
  ctx.temperature = temperature
365
+ return logsigmoid_fwd(x=x, temperature=temperature)
366
 
367
  @staticmethod
368
  @input_guard(no_guard_contiguous=True)
369
  def backward(ctx, dy):
370
  x, = ctx.saved_tensors
371
+ return logsigmoid_bwd(x=x, dy=dy, temperature=ctx.temperature), None
372
 
373
 
374
  def logsigmoid(x: torch.Tensor, temperature: float = 1.) -> torch.Tensor:
375
  return LogSigmoidFunction.apply(x, temperature)
376
 
377
 
378
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
379
  @triton.jit(do_not_specialize=['T'])
380
+ def swish_fwd_kernel(x, y, stride_x_row, stride_y_row, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
381
  i_n = tl.program_id(0).to(tl.int64)
382
  offs = i_n * B + tl.arange(0, B)
383
  mask = offs < T
 
388
  tl.store(y + row * stride_y_row + col, b_y.to(y.dtype.element_ty), mask=mask)
389
 
390
 
391
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
392
  @triton.jit(do_not_specialize=['T'])
393
+ def swish_bwd_kernel(x, dy, dx, stride_x_row, stride_dy_row, stride_dx_row, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
 
394
  i_n = tl.program_id(0).to(tl.int64)
395
  offs = i_n * B + tl.arange(0, B)
396
  mask = offs < T
 
403
  tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
404
 
405
 
406
+ @dispatch
407
  def swish_fwd(x: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
408
+ x = _ensure_inner_contiguous(x=x)
409
  T, D = x.numel(), x.shape[-1]
410
+ y = _alloc_output(x=x, contiguous=output_contiguous)
411
  swish_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
412
  x=x,
413
  y=y,
414
+ stride_x_row=_get_stride(x=x),
415
+ stride_y_row=_get_stride(x=y),
416
  T=T,
417
  D=D,
418
  )
419
  return y
420
 
421
 
422
+ @dispatch
423
  def swish_bwd(x: torch.Tensor, dy: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
424
+ x = _ensure_inner_contiguous(x=x)
425
+ dy = _ensure_inner_contiguous(x=dy)
426
  T, D = x.numel(), x.shape[-1]
427
+ dx = _alloc_output(x=x, contiguous=output_contiguous)
428
  swish_bwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
429
  x=x,
430
  dy=dy,
431
  dx=dx,
432
+ stride_x_row=_get_stride(x=x),
433
+ stride_dy_row=_get_stride(x=dy),
434
+ stride_dx_row=_get_stride(x=dx),
435
  T=T,
436
  D=D,
437
  )
 
444
  @input_guard(no_guard_contiguous=True)
445
  def forward(ctx, x):
446
  ctx.save_for_backward(x)
447
+ return swish_fwd(x=x)
448
 
449
  @staticmethod
450
  @input_guard(no_guard_contiguous=True)
451
  def backward(ctx, dout):
452
  x, = ctx.saved_tensors
453
+ return swish_bwd(x=x, dy=dout)
454
 
455
 
456
  swish = SwishFunction.apply
 
460
  # sqrt(2/pi) -> 0.79788456
461
 
462
 
463
+ # use the tanh GELU approximation.
 
 
464
  @torch.compile
465
  def bias_gelu(y, bias):
466
  x = bias + y
467
  return (x * 0.5 * (1.0 + torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x)))).to(dtype=y.dtype)
468
 
469
 
 
 
 
470
  @torch.compile
471
  def bias_gelu_bwd(g, y, bias):
472
+ """Inputs have shape [B, D] and bias has shape [D]."""
473
  x = bias + y
474
  tanh_out = torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x))
475
  # sqrt(2/pi) * 3 * 0.044715 -> 0.1070322243
476
+ ff = 0.5 * x * ((1 - tanh_out * tanh_out) * (0.79788456 + 0.1070322243 * x * x)) + 0.5 * (1 + tanh_out)
 
 
477
  grad_y = ff * g
478
  return grad_y.to(dtype=y.dtype), grad_y.sum(dim=(0), dtype=bias.dtype)
479
 
 
481
  class GeLUFunction(torch.autograd.Function):
482
 
483
  @staticmethod
 
484
  def forward(ctx, input, bias):
485
  ctx.save_for_backward(input, bias)
486
+ return bias_gelu(y=input, bias=bias)
487
 
488
  @staticmethod
489
  def backward(ctx, grad_output):
490
  input, bias = ctx.saved_tensors
491
+ return bias_gelu_bwd(g=grad_output, y=input, bias=bias)
492
 
493
 
494
  bias_gelu_impl = GeLUFunction.apply
495
 
496
 
497
+ # use the tanh GELU approximation.
 
 
 
498
  @torch.compile
499
  def gelu_fwd(x):
500
  return (x * 0.5 * (1.0 + torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x)))).to(dtype=x.dtype)
501
 
502
 
 
 
 
 
503
  @torch.compile
504
  def gelu_bwd(g, x):
505
  tanh_out = torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x))
506
  # sqrt(2/pi) * 3 * 0.044715 -> 0.1070322243
507
+ ff = 0.5 * x * ((1 - tanh_out * tanh_out) * (0.79788456 + 0.1070322243 * x * x)) + 0.5 * (1 + tanh_out)
 
 
508
  return (ff * g).to(dtype=x.dtype)
509
 
510
 
511
  class FastGeLUFunction(torch.autograd.Function):
512
  @staticmethod
 
513
  def forward(ctx, input):
514
  ctx.save_for_backward(input)
515
+ return gelu_fwd(x=input)
516
 
517
  @staticmethod
518
  def backward(ctx, grad_output):
519
  (input,) = ctx.saved_tensors
520
+ tmp = gelu_bwd(g=grad_output, x=input)
521
  return tmp
522
 
523
 
 
529
  return torch.where(x >= 0, g, 0.0).to(dtype=x.dtype)
530
 
531
 
 
532
  @torch.compile
533
  def sqrelu_fwd(x):
534
  r = F.relu(x.float())
535
  return (r * r).to(dtype=x.dtype)
536
 
537
 
 
538
  @torch.compile
539
  def sqrelu_bwd(g, x):
540
  return (2.0 * g * F.relu(x.float())).to(dtype=x.dtype)
 
545
  @staticmethod
546
  def forward(ctx, input):
547
  ctx.save_for_backward(input)
548
+ return sqrelu_fwd(x=input)
549
 
550
  @staticmethod
551
  def backward(ctx, grad_output):
552
  input, = ctx.saved_tensors
553
+ return sqrelu_bwd(g=grad_output, x=input)
554
 
555
 
556
  sqrelu = SquaredReLUFunction.apply
557
 
558
 
559
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
560
  @triton.jit(do_not_specialize=['T'])
561
+ def swiglu_fwd_kernel(x, y, z, stride_x_row, stride_y_row, stride_z_row, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
 
562
  i_n = tl.program_id(0).to(tl.int64)
563
  offs = i_n * B + tl.arange(0, B)
564
  mask = offs < T
 
570
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
571
 
572
 
573
+ @triton.heuristics({'HAS_WEIGHT': lambda args: args['z'] is not None})
574
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
 
 
575
  @triton.jit(do_not_specialize=['T'])
576
  def swiglu_fwdbwd_kernel(
577
+ x,
578
+ y,
579
+ g,
580
+ dx,
581
+ dy,
582
+ z,
583
  stride_x_row,
584
  stride_y_row,
585
  stride_g_row,
 
612
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
613
 
614
 
615
+ @dispatch
616
  def swiglu_fwd(x: torch.Tensor, y: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
617
  assert x.shape == y.shape, f"swiglu_fwd: shape mismatch x={x.shape} y={y.shape}"
618
+ x = _ensure_inner_contiguous(x=x)
619
+ y = _ensure_inner_contiguous(x=y)
620
  T, D = x.numel(), x.shape[-1]
621
+ z = _alloc_output(x=x, contiguous=output_contiguous)
622
  swiglu_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
623
  x=x,
624
  y=y,
625
  z=z,
626
+ stride_x_row=_get_stride(x=x),
627
+ stride_y_row=_get_stride(x=y),
628
+ stride_z_row=_get_stride(x=z),
629
  T=T,
630
  D=D,
631
  )
632
  return z
633
 
634
 
635
+ @dispatch
636
  def swiglu_fwdbwd(
637
  x: torch.Tensor,
638
  y: torch.Tensor,
 
641
  output_contiguous: bool = False,
642
  ):
643
  assert x.shape == y.shape == g.shape, f"swiglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
644
+ x = _ensure_inner_contiguous(x=x)
645
+ y = _ensure_inner_contiguous(x=y)
646
+ g = _ensure_inner_contiguous(x=g)
647
  T, D = x.numel(), x.shape[-1]
648
+ dx = _alloc_output(x=x, contiguous=output_contiguous)
649
+ dy = _alloc_output(x=y, contiguous=output_contiguous)
650
  if use_weight:
651
+ z = _alloc_output(x=x, contiguous=output_contiguous)
652
  else:
653
  z = None
654
  swiglu_fwdbwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
 
658
  dx=dx,
659
  dy=dy,
660
  z=z,
661
+ stride_x_row=_get_stride(x=x),
662
+ stride_y_row=_get_stride(x=y),
663
+ stride_g_row=_get_stride(x=g),
664
+ stride_dx_row=_get_stride(x=dx),
665
+ stride_dy_row=_get_stride(x=dy),
666
+ stride_z_row=_get_stride(x=z) if z is not None else 0,
667
  T=T,
668
  D=D,
669
  )
 
684
  @input_guard(no_guard_contiguous=True)
685
  def forward(ctx, x, y):
686
  ctx.save_for_backward(x, y)
687
+ return swiglu_fwd(x=x, y=y)
688
 
689
  @staticmethod
690
  @input_guard(no_guard_contiguous=True)
691
  def backward(ctx, dout):
692
  x, y = ctx.saved_tensors
693
+ return swiglu_fwdbwd(x=x, y=y, g=dout)
694
 
695
 
696
  class SwiGLULinearFunction(torch.autograd.Function):
 
707
  @input_guard(no_guard_contiguous=True)
708
  @autocast_custom_fwd
709
  def forward(ctx, x, y, weight, bias):
710
+ z = swiglu_fwd(x=x, y=y, output_contiguous=True)
711
  out = F.linear(z, weight, bias)
712
  ctx.save_for_backward(x, y, weight)
 
713
  return out
714
 
715
  @staticmethod
 
718
  def backward(ctx, dout, *args):
719
  x, y, weight = ctx.saved_tensors
720
  dout = dout.reshape(-1, dout.shape[-1])
721
+ dx = dy = dlinear_weight = None
722
+ if ctx.needs_input_grad[0] or ctx.needs_input_grad[1]:
723
+ dz = F.linear(dout, weight.t()).view_as(x)
724
+ if ctx.needs_input_grad[2]:
725
+ dx, dy, z = swiglu_fwdbwd(x=x, y=y, g=dz, use_weight=True, output_contiguous=True)
726
+ else:
727
+ dx, dy = swiglu_fwdbwd(x=x, y=y, g=dz, output_contiguous=True)
728
+ elif ctx.needs_input_grad[2]:
729
+ z = swiglu_fwd(x=x, y=y, output_contiguous=True)
730
+ if ctx.needs_input_grad[2]:
731
+ dlinear_weight = torch.einsum("bo,bi->oi", dout, z.reshape(-1, z.shape[-1]))
732
+ dlinear_bias = dout.sum(0) if ctx.needs_input_grad[3] else None
733
  return dx, dy, dlinear_weight, dlinear_bias
734
 
735
 
736
  swiglu = SwiGLUFunction.apply
737
 
738
 
739
+ @dispatch
740
  def swiglu_linear(x, y, weight, bias):
741
  return SwiGLULinearFunction.apply(x, y, weight, bias)
742
 
743
 
744
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
745
  @triton.jit(do_not_specialize=['T'])
746
+ def sigmoidglu_fwd_kernel(x, y, z, stride_x_row, stride_y_row, stride_z_row, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
 
747
  i_n = tl.program_id(0).to(tl.int64)
748
  offs = i_n * B + tl.arange(0, B)
749
  mask = offs < T
 
755
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
756
 
757
 
758
+ @triton.heuristics({'HAS_WEIGHT': lambda args: args['z'] is not None})
759
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
 
 
760
  @triton.jit(do_not_specialize=['T'])
761
  def sigmoidglu_fwdbwd_kernel(
762
+ x,
763
+ y,
764
+ g,
765
+ dx,
766
+ dy,
767
+ z,
768
  stride_x_row,
769
  stride_y_row,
770
  stride_g_row,
 
799
  @torch.compiler.disable
800
  def sigmoidglu_fwd(x: torch.Tensor, y: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
801
  assert x.shape == y.shape, f"sigmoidglu_fwd: shape mismatch x={x.shape} y={y.shape}"
802
+ x = _ensure_inner_contiguous(x=x)
803
+ y = _ensure_inner_contiguous(x=y)
804
  T, D = x.numel(), x.shape[-1]
805
+ z = _alloc_output(x=x, contiguous=output_contiguous)
806
  sigmoidglu_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
807
  x=x,
808
  y=y,
809
  z=z,
810
+ stride_x_row=_get_stride(x=x),
811
+ stride_y_row=_get_stride(x=y),
812
+ stride_z_row=_get_stride(x=z),
813
  T=T,
814
  D=D,
815
  )
 
825
  output_contiguous: bool = False,
826
  ):
827
  assert x.shape == y.shape == g.shape, f"sigmoidglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
828
+ x = _ensure_inner_contiguous(x=x)
829
+ y = _ensure_inner_contiguous(x=y)
830
+ g = _ensure_inner_contiguous(x=g)
831
  T, D = x.numel(), x.shape[-1]
832
+ dx = _alloc_output(x=x, contiguous=output_contiguous)
833
+ dy = _alloc_output(x=y, contiguous=output_contiguous)
834
  if use_weight:
835
+ z = _alloc_output(x=x, contiguous=output_contiguous)
836
  else:
837
  z = None
838
  sigmoidglu_fwdbwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
 
842
  dx=dx,
843
  dy=dy,
844
  z=z,
845
+ stride_x_row=_get_stride(x=x),
846
+ stride_y_row=_get_stride(x=y),
847
+ stride_g_row=_get_stride(x=g),
848
+ stride_dx_row=_get_stride(x=dx),
849
+ stride_dy_row=_get_stride(x=dy),
850
+ stride_z_row=_get_stride(x=z) if z is not None else 0,
851
  T=T,
852
  D=D,
853
  )
 
868
  @input_guard(no_guard_contiguous=True)
869
  def forward(ctx, x, y):
870
  ctx.save_for_backward(x, y)
871
+ return sigmoidglu_fwd(x=x, y=y)
872
 
873
  @staticmethod
874
  @input_guard(no_guard_contiguous=True)
875
  def backward(ctx, dout):
876
  x, y = ctx.saved_tensors
877
+ return sigmoidglu_fwdbwd(x=x, y=y, g=dout)
878
 
879
 
880
  class SigmoidGLULinearFunction(torch.autograd.Function):
 
891
  @input_guard(no_guard_contiguous=True)
892
  @autocast_custom_fwd
893
  def forward(ctx, x, y, weight, bias):
894
+ z = sigmoidglu_fwd(x=x, y=y, output_contiguous=True)
895
  out = F.linear(z, weight, bias)
896
  ctx.save_for_backward(x, y, weight)
897
  ctx.linear_bias_is_none = bias is None
 
904
  x, y, weight = ctx.saved_tensors
905
  dout = dout.reshape(-1, dout.shape[-1])
906
  dz = F.linear(dout, weight.t()).view_as(x)
907
+ dx, dy, z = sigmoidglu_fwdbwd(x=x, y=y, g=dz, use_weight=True, output_contiguous=True)
908
  dlinear_weight = torch.einsum("bo,bi->oi", dout, z.reshape(-1, z.shape[-1]))
909
  dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
910
  return dx, dy, dlinear_weight, dlinear_bias
 
916
  sigmoidglu_linear = SigmoidGLULinearFunction.apply
917
 
918
 
919
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
920
  @triton.jit(do_not_specialize=['T'])
921
+ def powglu_fwd_kernel(x, y, z, stride_x_row, stride_y_row, stride_z_row, m, T, D: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
 
 
922
  i_n = tl.program_id(0).to(tl.int64)
923
  offs = i_n * B + tl.arange(0, B)
924
  mask = offs < T
 
938
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
939
 
940
 
941
+ @triton.heuristics({'HAS_WEIGHT': lambda args: args['z'] is not None})
942
+ @triton.autotune(configs=_activation_autotune_configs(), key=['D'], **autotune_cache_kwargs)
 
 
 
 
 
 
 
 
 
 
943
  @triton.jit(do_not_specialize=['T'])
944
  def powglu_fwdbwd_kernel(
945
+ x,
946
+ y,
947
+ g,
948
+ dx,
949
+ dy,
950
+ z,
951
  stride_x_row,
952
  stride_y_row,
953
  stride_g_row,
 
997
  tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
998
 
999
 
1000
+ @dispatch
1001
  def powglu_fwd(x: torch.Tensor, y: torch.Tensor, power: float = 3.0, output_contiguous: bool = False) -> torch.Tensor:
1002
  assert x.shape == y.shape, f"powglu_fwd: shape mismatch x={x.shape} y={y.shape}"
1003
+ x = _ensure_inner_contiguous(x=x)
1004
+ y = _ensure_inner_contiguous(x=y)
1005
  T, D = x.numel(), x.shape[-1]
1006
+ z = _alloc_output(x=x, contiguous=output_contiguous)
1007
  powglu_fwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
1008
  x=x,
1009
  y=y,
1010
  z=z,
1011
+ stride_x_row=_get_stride(x=x),
1012
+ stride_y_row=_get_stride(x=y),
1013
+ stride_z_row=_get_stride(x=z),
1014
  m=power,
1015
  T=T,
1016
  D=D,
 
1018
  return z
1019
 
1020
 
1021
+ @dispatch
1022
  def powglu_fwdbwd(
1023
  x: torch.Tensor,
1024
  y: torch.Tensor,
 
1028
  output_contiguous: bool = False,
1029
  ):
1030
  assert x.shape == y.shape == g.shape, f"powglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
1031
+ x = _ensure_inner_contiguous(x=x)
1032
+ y = _ensure_inner_contiguous(x=y)
1033
+ g = _ensure_inner_contiguous(x=g)
1034
  T, D = x.numel(), x.shape[-1]
1035
+ dx = _alloc_output(x=x, contiguous=output_contiguous)
1036
+ dy = _alloc_output(x=y, contiguous=output_contiguous)
1037
  if use_weight:
1038
+ z = _alloc_output(x=x, contiguous=output_contiguous)
1039
  else:
1040
  z = None
1041
  powglu_fwdbwd_kernel[lambda meta: (triton.cdiv(T, meta['B']),)](
 
1045
  dx=dx,
1046
  dy=dy,
1047
  z=z,
1048
+ stride_x_row=_get_stride(x=x),
1049
+ stride_y_row=_get_stride(x=y),
1050
+ stride_g_row=_get_stride(x=g),
1051
+ stride_dx_row=_get_stride(x=dx),
1052
+ stride_dy_row=_get_stride(x=dy),
1053
+ stride_z_row=_get_stride(x=z) if z is not None else 0,
1054
  m=power,
1055
  T=T,
1056
  D=D,
 
1068
  \text{PowGLU}(x, y) = g(x) * y,\quad
1069
  g(x) = \begin{cases} x^{power/(\sqrt{x}+1)}\,\sigma(x) & x > 0 \\ x\,\sigma(x) & x \le 0 \end{cases}
1070
 
1071
+ For ``x <= 0`` the gate reduces to swish, matching SwiGLU; for large ``x > 0`` it saturates instead of growing,
1072
+ replacing SwiGLU's quadratic amplification with bounded growth (Power Linear Unit, arXiv:2605.25704).
1073
  """
1074
 
1075
  @staticmethod
 
1077
  def forward(ctx, x, y, power):
1078
  ctx.save_for_backward(x, y)
1079
  ctx.power = power
1080
+ return powglu_fwd(x=x, y=y, power=power)
1081
 
1082
  @staticmethod
1083
  @input_guard(no_guard_contiguous=True)
1084
  def backward(ctx, dout):
1085
  x, y = ctx.saved_tensors
1086
+ dx, dy = powglu_fwdbwd(x=x, y=y, g=dout, power=ctx.power)
1087
  return dx, dy, None
1088
 
1089
 
 
1101
  @input_guard(no_guard_contiguous=True)
1102
  @autocast_custom_fwd
1103
  def forward(ctx, x, y, weight, bias, power):
1104
+ z = powglu_fwd(x=x, y=y, power=power, output_contiguous=True)
1105
  out = F.linear(z, weight, bias)
1106
  ctx.save_for_backward(x, y, weight)
1107
  ctx.linear_bias_is_none = bias is None
 
1115
  x, y, weight = ctx.saved_tensors
1116
  dout = dout.reshape(-1, dout.shape[-1])
1117
  dz = F.linear(dout, weight.t()).view_as(x)
1118
+ dx, dy, z = powglu_fwdbwd(x=x, y=y, g=dz, power=ctx.power, use_weight=True, output_contiguous=True)
1119
  dlinear_weight = torch.einsum("bo,bi->oi", dout, z.reshape(-1, z.shape[-1]))
1120
  dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
1121
  return dx, dy, dlinear_weight, dlinear_bias, None
 
1125
  return PowGLUFunction.apply(x, y, power)
1126
 
1127
 
1128
+ @dispatch
1129
  def powglu_linear(
1130
  x: torch.Tensor,
1131
  y: torch.Tensor,
build/torch-cuda/modules/backends/__init__.py DELETED
@@ -1,17 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Module-level backends for FLA components such as rotary and cross-entropy."""
9
-
10
- from ...modules.backends.triton_ascend import TritonAscendBackend
11
- from ...ops.backends import BackendRegistry, dispatch
12
-
13
- modules_registry = BackendRegistry("modules")
14
-
15
- modules_registry.register(TritonAscendBackend())
16
-
17
- __all__ = ['dispatch', 'modules_registry']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/__init__.py DELETED
@@ -1,422 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Triton-Ascend (Huawei NPU) backend for FLA modules."""
9
-
10
- from __future__ import annotations
11
-
12
- from ....ops.backends import BaseBackend
13
-
14
-
15
- class TritonAscendBackend(BaseBackend):
16
- """Ascend NPU backend using triton-ascend kernels."""
17
-
18
- backend_type = "triton_ascend"
19
- package_name = None
20
- env_var = None
21
- priority = 0
22
-
23
- @classmethod
24
- def is_available(cls) -> bool:
25
- from ....utils import IS_NPU
26
- return IS_NPU
27
-
28
- def rotary_embedding_fwdbwd(
29
- self,
30
- x,
31
- cos,
32
- sin,
33
- seqlen_offsets=0,
34
- cu_seqlens=None,
35
- interleaved=False,
36
- inplace=False,
37
- conjugate=False,
38
- chunk_indices=None,
39
- ):
40
- from ....modules.backends.triton_ascend.rotary import rotary_embedding_fwdbwd_npu
41
- return rotary_embedding_fwdbwd_npu(
42
- x,
43
- cos,
44
- sin,
45
- seqlen_offsets=seqlen_offsets,
46
- cu_seqlens=cu_seqlens,
47
- interleaved=interleaved,
48
- inplace=inplace,
49
- conjugate=conjugate,
50
- chunk_indices=chunk_indices,
51
- )
52
-
53
- def cross_entropy_loss(
54
- self,
55
- logits,
56
- target,
57
- label_smoothing=0.0,
58
- logit_scale=1.0,
59
- lse_square_scale=0.0,
60
- logit_softcapping=None,
61
- ignore_index=-100,
62
- inplace_backward=False,
63
- process_group=None,
64
- ):
65
- from ....modules.backends.triton_ascend.fused_cross_entropy import (
66
- cross_entropy_loss_npu,
67
- )
68
- return cross_entropy_loss_npu(
69
- logits,
70
- target,
71
- label_smoothing,
72
- logit_scale,
73
- lse_square_scale,
74
- logit_softcapping,
75
- ignore_index,
76
- inplace_backward,
77
- process_group,
78
- )
79
-
80
- def logsumexp_fwd(
81
- self,
82
- x,
83
- scale=None,
84
- softcapping=None,
85
- dtype=None,
86
- ):
87
- from ....modules.backends.triton_ascend.fused_linear_cross_entropy import (
88
- logsumexp_fwd_npu,
89
- )
90
- return logsumexp_fwd_npu(x, scale=scale, softcapping=softcapping, dtype=dtype)
91
-
92
- def fused_linear_cross_entropy_forward(
93
- self,
94
- x,
95
- target,
96
- weight,
97
- bias=None,
98
- ignore_index=-100,
99
- label_smoothing=0.0,
100
- logit_scale=1.0,
101
- logit_softcapping=None,
102
- num_chunks=8,
103
- reduction="mean",
104
- use_l2warp=False,
105
- l2_penalty_factor=1e-4,
106
- accumulate_grad_in_fp32=True,
107
- ):
108
- from ....modules.backends.triton_ascend.fused_linear_cross_entropy import (
109
- fused_linear_cross_entropy_forward_npu,
110
- )
111
- return fused_linear_cross_entropy_forward_npu(
112
- x,
113
- target,
114
- weight,
115
- bias,
116
- ignore_index,
117
- label_smoothing,
118
- logit_scale,
119
- logit_softcapping,
120
- num_chunks,
121
- reduction,
122
- use_l2warp,
123
- l2_penalty_factor,
124
- accumulate_grad_in_fp32,
125
- )
126
-
127
- def fused_linear_cross_entropy_backward(
128
- self,
129
- do,
130
- dx,
131
- dw,
132
- db,
133
- ):
134
- from ....modules.backends.triton_ascend.fused_linear_cross_entropy import (
135
- fused_linear_cross_entropy_backward_npu,
136
- )
137
- return fused_linear_cross_entropy_backward_npu(do, dx, dw, db)
138
-
139
- def sigmoid_fwd(self, x, output_contiguous=False):
140
- from ....modules.backends.triton_ascend.activations import sigmoid_fwd_npu
141
- return sigmoid_fwd_npu(x, output_contiguous=output_contiguous)
142
-
143
- def sigmoid_bwd(self, x, dy, output_contiguous=False):
144
- from ....modules.backends.triton_ascend.activations import sigmoid_bwd_npu
145
- return sigmoid_bwd_npu(x, dy, output_contiguous=output_contiguous)
146
-
147
- def logsigmoid_fwd(self, x, temperature=1., output_contiguous=False):
148
- from ....modules.backends.triton_ascend.activations import logsigmoid_fwd_npu
149
- return logsigmoid_fwd_npu(x, temperature=temperature, output_contiguous=output_contiguous)
150
-
151
- def logsigmoid_bwd(self, x, dy, temperature=1., output_contiguous=False):
152
- from ....modules.backends.triton_ascend.activations import logsigmoid_bwd_npu
153
- return logsigmoid_bwd_npu(x, dy, temperature=temperature, output_contiguous=output_contiguous)
154
-
155
- def swish_fwd(self, x, output_contiguous=False):
156
- from ....modules.backends.triton_ascend.activations import swish_fwd_npu
157
- return swish_fwd_npu(x, output_contiguous=output_contiguous)
158
-
159
- def swish_bwd(self, x, dy, output_contiguous=False):
160
- from ....modules.backends.triton_ascend.activations import swish_bwd_npu
161
- return swish_bwd_npu(x, dy, output_contiguous=output_contiguous)
162
-
163
- def swiglu_fwd(self, x, y, output_contiguous=False):
164
- from ....modules.backends.triton_ascend.activations import swiglu_fwd_npu
165
- return swiglu_fwd_npu(x, y, output_contiguous=output_contiguous)
166
-
167
- def swiglu_fwdbwd(self, x, y, g, use_weight=False, output_contiguous=False):
168
- from ....modules.backends.triton_ascend.activations import swiglu_fwdbwd_npu
169
- return swiglu_fwdbwd_npu(x, y, g, use_weight=use_weight, output_contiguous=output_contiguous)
170
-
171
- def swiglu_linear(self, x, y, weight, bias):
172
- from ....modules.backends.triton_ascend.activations import swiglu_linear_npu
173
- return swiglu_linear_npu(x, y, weight, bias)
174
-
175
- def gelu_fwd(self, x):
176
- from ....modules.backends.triton_ascend.activations import gelu_fwd_npu
177
- return gelu_fwd_npu(x)
178
-
179
- def gelu_bwd(self, g, x):
180
- from ....modules.backends.triton_ascend.activations import gelu_bwd_npu
181
- return gelu_bwd_npu(g, x)
182
-
183
- def sqrelu_fwd(self, x):
184
- from ....modules.backends.triton_ascend.activations import sqrelu_fwd_npu
185
- return sqrelu_fwd_npu(x)
186
-
187
- def sqrelu_bwd(self, g, x):
188
- from ....modules.backends.triton_ascend.activations import sqrelu_bwd_npu
189
- return sqrelu_bwd_npu(g, x)
190
-
191
- def powglu_fwd(self, x, y, power=3.0, output_contiguous=False):
192
- from ....modules.backends.triton_ascend.activations import powglu_fwd_npu
193
- return powglu_fwd_npu(x, y, power=power, output_contiguous=output_contiguous)
194
-
195
- def powglu_fwdbwd(self, x, y, g, power=3.0, use_weight=False, output_contiguous=False):
196
- from ....modules.backends.triton_ascend.activations import powglu_fwdbwd_npu
197
- return powglu_fwdbwd_npu(
198
- x, y, g, power=power, use_weight=use_weight, output_contiguous=output_contiguous,
199
- )
200
-
201
- def powglu_linear(self, x, y, weight, bias, power=3.0):
202
- from ....modules.backends.triton_ascend.activations import powglu_linear_npu
203
- return powglu_linear_npu(x, y, weight, bias, power)
204
-
205
- def fused_kl_div_forward(
206
- self,
207
- x,
208
- target_x,
209
- weight,
210
- target_weight,
211
- reduction='batchmean',
212
- accumulate_grad_in_fp32=True,
213
- ):
214
- from ....modules.backends.triton_ascend.fused_kl_div import fused_kl_div_forward_npu
215
- return fused_kl_div_forward_npu(
216
- x,
217
- target_x,
218
- weight,
219
- target_weight,
220
- reduction,
221
- accumulate_grad_in_fp32,
222
- )
223
-
224
- def fused_kl_div_backward(self, do, dx, dw):
225
- from ....modules.backends.triton_ascend.fused_kl_div import fused_kl_div_backward_npu
226
- return fused_kl_div_backward_npu(do, dx, dw)
227
-
228
- def layer_norm_fwd(
229
- self,
230
- x,
231
- weight,
232
- bias,
233
- eps=1e-5,
234
- residual=None,
235
- out_dtype=None,
236
- residual_dtype=None,
237
- is_rms_norm=False,
238
- num_groups=1,
239
- ):
240
- from ....modules.backends.triton_ascend.layernorm import layer_norm_fwd_npu
241
- return layer_norm_fwd_npu(
242
- x,
243
- weight,
244
- bias,
245
- eps,
246
- residual,
247
- out_dtype,
248
- residual_dtype,
249
- is_rms_norm,
250
- num_groups,
251
- )
252
-
253
- def layer_norm_bwd(
254
- self,
255
- dy,
256
- x,
257
- weight,
258
- bias,
259
- mean=None,
260
- rstd=None,
261
- dres=None,
262
- has_residual=False,
263
- is_rms_norm=False,
264
- x_dtype=None,
265
- recompute_output=False,
266
- num_groups=1,
267
- ):
268
- from ....modules.backends.triton_ascend.layernorm import layer_norm_bwd_npu
269
- return layer_norm_bwd_npu(
270
- dy,
271
- x,
272
- weight,
273
- bias,
274
- mean,
275
- rstd,
276
- dres,
277
- has_residual,
278
- is_rms_norm,
279
- x_dtype,
280
- recompute_output,
281
- num_groups,
282
- )
283
-
284
- def fused_grpo_loss(
285
- self,
286
- logits,
287
- ref_logp,
288
- input_ids,
289
- advantages,
290
- beta=0.1,
291
- completion_mask=None,
292
- save_kl=False,
293
- inplace=False,
294
- ):
295
- from ....modules.backends.triton_ascend.grpo import fused_grpo_loss_npu
296
- return fused_grpo_loss_npu(
297
- logits,
298
- ref_logp,
299
- input_ids,
300
- advantages,
301
- beta,
302
- completion_mask,
303
- save_kl,
304
- inplace,
305
- )
306
-
307
- def causal_conv1d_fwd(
308
- self,
309
- x,
310
- weight,
311
- bias,
312
- residual,
313
- initial_state=None,
314
- output_final_state=False,
315
- activation=None,
316
- cu_seqlens=None,
317
- cu_seqlens_cpu=None,
318
- chunk_indices=None,
319
- BT=64,
320
- layout_fallback=False,
321
- ):
322
- from ....modules.backends.triton_ascend.causal_conv1d import causal_conv1d_fwd_npu
323
- return causal_conv1d_fwd_npu(
324
- x,
325
- weight,
326
- bias,
327
- residual,
328
- initial_state,
329
- output_final_state,
330
- activation,
331
- cu_seqlens,
332
- cu_seqlens_cpu,
333
- chunk_indices,
334
- BT,
335
- layout_fallback,
336
- )
337
-
338
- def causal_conv1d_bwd(
339
- self,
340
- x,
341
- dy,
342
- dht,
343
- weight=None,
344
- bias=None,
345
- residual=None,
346
- initial_state=None,
347
- activation=None,
348
- cu_seqlens=None,
349
- cu_seqlens_cpu=None,
350
- chunk_indices=None,
351
- BT=64,
352
- layout_fallback=False,
353
- ):
354
- from ....modules.backends.triton_ascend.causal_conv1d import causal_conv1d_bwd_npu
355
- return causal_conv1d_bwd_npu(
356
- x,
357
- dy,
358
- dht,
359
- weight,
360
- bias,
361
- residual,
362
- initial_state,
363
- activation,
364
- cu_seqlens,
365
- cu_seqlens_cpu,
366
- chunk_indices,
367
- BT,
368
- layout_fallback,
369
- )
370
-
371
- def compute_dh0_triton(
372
- self,
373
- dy,
374
- y,
375
- weight,
376
- initial_state,
377
- activation,
378
- cu_seqlens,
379
- ):
380
- from ....modules.backends.triton_ascend.causal_conv1d import compute_dh0_npu
381
- return compute_dh0_npu(
382
- dy,
383
- y,
384
- weight,
385
- initial_state,
386
- activation,
387
- cu_seqlens,
388
- )
389
-
390
- def causal_conv1d_update_states(
391
- self,
392
- x,
393
- state_len,
394
- initial_state=None,
395
- cu_seqlens=None,
396
- ):
397
- from ....modules.backends.triton_ascend.causal_conv1d import causal_conv1d_update_states_npu
398
- return causal_conv1d_update_states_npu(
399
- x,
400
- state_len,
401
- initial_state,
402
- cu_seqlens,
403
- )
404
-
405
- def causal_conv1d_update(
406
- self,
407
- x,
408
- cache,
409
- residual=None,
410
- weight=None,
411
- bias=None,
412
- activation=None,
413
- ):
414
- from ....modules.backends.triton_ascend.causal_conv1d import causal_conv1d_update_npu
415
- return causal_conv1d_update_npu(
416
- x,
417
- cache,
418
- residual,
419
- weight,
420
- bias,
421
- activation,
422
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/activations.py DELETED
@@ -1,931 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Activation kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import torch.nn.functional as F
12
- import triton
13
- import triton.language as tl
14
-
15
- from ....ops.utils.op import exp, log
16
- from ....utils import autocast_custom_bwd, autocast_custom_fwd, input_guard
17
- from ....utils.ascend_ub_manager import ASCEND_MAX_GRID_DIM, compute_activation_block_size
18
-
19
- # Ascend launch limits: grid dim and per-core vector width.
20
- _MAX_CORE_DIM = 65535
21
-
22
-
23
- def _activation_launch_config(
24
- T: int,
25
- is_backward: bool = False,
26
- *,
27
- memory_multiplier: float | None = None,
28
- ) -> tuple[tuple[int], int]:
29
- """Pick block size under Ascend launch and UB limits."""
30
- B = compute_activation_block_size(
31
- T,
32
- is_backward,
33
- max_grid=ASCEND_MAX_GRID_DIM,
34
- max_core_dim=_MAX_CORE_DIM,
35
- memory_multiplier=memory_multiplier,
36
- )
37
- return (triton.cdiv(T, B),), B
38
-
39
-
40
- @triton.jit
41
- def _flat_offset(
42
- offs,
43
- D: tl.constexpr,
44
- stride,
45
- IS_LINEAR: tl.constexpr,
46
- ):
47
- if IS_LINEAR:
48
- return offs
49
- row = offs // D
50
- col = offs % D
51
- return row * stride + col
52
-
53
-
54
- def _get_stride(x: torch.Tensor) -> int:
55
- if x.ndim < 2:
56
- return 0
57
- return x.stride(-2)
58
-
59
-
60
- def _is_linear_stride(stride: int, D: int) -> bool:
61
- return stride == D
62
-
63
-
64
- _LINEAR_HEURISTICS_XY = {
65
- 'X_LINEAR': lambda args: _is_linear_stride(args['stride_x_row'], args['D']),
66
- 'Y_LINEAR': lambda args: _is_linear_stride(args['stride_y_row'], args['D']),
67
- }
68
-
69
- _LINEAR_HEURISTICS_XYZ = {
70
- **_LINEAR_HEURISTICS_XY,
71
- 'Z_LINEAR': lambda args: _is_linear_stride(args['stride_z_row'], args['D']),
72
- }
73
-
74
- _LINEAR_HEURISTICS_BWD = {
75
- 'X_LINEAR': lambda args: _is_linear_stride(args['stride_x_row'], args['D']),
76
- 'DY_LINEAR': lambda args: _is_linear_stride(args['stride_dy_row'], args['D']),
77
- 'DX_LINEAR': lambda args: _is_linear_stride(args['stride_dx_row'], args['D']),
78
- }
79
-
80
- _LINEAR_HEURISTICS_FWDBWD = {
81
- **_LINEAR_HEURISTICS_XYZ,
82
- 'G_LINEAR': lambda args: _is_linear_stride(args['stride_g_row'], args['D']),
83
- 'DX_LINEAR': lambda args: _is_linear_stride(args['stride_dx_row'], args['D']),
84
- 'DY_LINEAR': lambda args: _is_linear_stride(args['stride_dy_row'], args['D']),
85
- }
86
-
87
-
88
- def _is_inner_contiguous(x: torch.Tensor) -> bool:
89
- ndim = x.ndim
90
- if ndim < 2:
91
- return True
92
- if x.stride(-1) != 1:
93
- return False
94
- if ndim == 2:
95
- return True
96
- if ndim == 3:
97
- return x.stride(0) == x.stride(-2) * x.shape[-2]
98
- if ndim == 4:
99
- if x.stride(1) != x.stride(-2) * x.shape[-2]:
100
- return False
101
- return x.stride(0) == x.stride(1) * x.shape[1]
102
- expected = x.stride(-2) * x.shape[-2]
103
- for d in range(ndim - 3, -1, -1):
104
- if x.stride(d) != expected:
105
- return False
106
- expected *= x.shape[d]
107
- return True
108
-
109
-
110
- def _ensure_inner_contiguous(x: torch.Tensor) -> torch.Tensor:
111
- if _is_inner_contiguous(x):
112
- return x
113
- return x.contiguous()
114
-
115
-
116
- def _alloc_output(x: torch.Tensor, contiguous: bool = False) -> torch.Tensor:
117
- if contiguous:
118
- return x.new_empty(x.shape)
119
- return torch.empty_like(x)
120
-
121
-
122
- @triton.heuristics(_LINEAR_HEURISTICS_XY)
123
- @triton.jit(do_not_specialize=['T'])
124
- def sigmoid_fwd_kernel(
125
- x, y,
126
- T,
127
- D: tl.constexpr,
128
- stride_x_row,
129
- stride_y_row,
130
- B: tl.constexpr,
131
- X_LINEAR: tl.constexpr,
132
- Y_LINEAR: tl.constexpr,
133
- ):
134
- pid = tl.program_id(0)
135
- offs = pid * B + tl.arange(0, B)
136
- mask = offs < T
137
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
138
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
139
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
140
- y_val = tl.sigmoid(x_val)
141
- tl.store(y + y_off, y_val.to(y.dtype.element_ty), mask=mask)
142
-
143
-
144
- @triton.heuristics(_LINEAR_HEURISTICS_BWD)
145
- @triton.jit(do_not_specialize=['T'])
146
- def sigmoid_bwd_kernel(
147
- x, dy, dx,
148
- T,
149
- D: tl.constexpr,
150
- stride_x_row,
151
- stride_dy_row,
152
- stride_dx_row,
153
- B: tl.constexpr,
154
- X_LINEAR: tl.constexpr,
155
- DY_LINEAR: tl.constexpr,
156
- DX_LINEAR: tl.constexpr,
157
- ):
158
- pid = tl.program_id(0)
159
- offs = pid * B + tl.arange(0, B)
160
- mask = offs < T
161
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
162
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
163
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
164
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
165
- g_val = tl.load(dy + dy_off, mask=mask, other=0.).to(tl.float32)
166
- s = tl.sigmoid(x_val)
167
- dx_val = g_val * s * (1.0 - s)
168
- tl.store(dx + dx_off, dx_val.to(dx.dtype.element_ty), mask=mask)
169
-
170
-
171
- @triton.heuristics(_LINEAR_HEURISTICS_XY)
172
- @triton.jit(do_not_specialize=['T'])
173
- def logsigmoid_fwd_kernel(
174
- x,
175
- y,
176
- temperature,
177
- T,
178
- D: tl.constexpr,
179
- stride_x_row,
180
- stride_y_row,
181
- B: tl.constexpr,
182
- X_LINEAR: tl.constexpr,
183
- Y_LINEAR: tl.constexpr,
184
- ):
185
- i = tl.program_id(0)
186
- offs = i * B + tl.arange(0, B)
187
- mask = offs < T
188
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
189
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
190
-
191
- b_x = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
192
- b_m = tl.minimum(0., b_x)
193
- b_z = 1. + exp(-tl.abs(b_x))
194
- b_y = (b_m - log(b_z)) / temperature
195
- tl.store(y + y_off, b_y.to(y.dtype.element_ty), mask=mask)
196
-
197
-
198
- @triton.heuristics(_LINEAR_HEURISTICS_BWD)
199
- @triton.jit(do_not_specialize=['T'])
200
- def logsigmoid_bwd_kernel(
201
- x,
202
- dx,
203
- dy,
204
- temperature,
205
- T,
206
- D: tl.constexpr,
207
- stride_x_row,
208
- stride_dx_row,
209
- stride_dy_row,
210
- B: tl.constexpr,
211
- X_LINEAR: tl.constexpr,
212
- DX_LINEAR: tl.constexpr,
213
- DY_LINEAR: tl.constexpr,
214
- ):
215
- i = tl.program_id(0)
216
- offs = i * B + tl.arange(0, B)
217
- mask = offs < T
218
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
219
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
220
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
221
-
222
- b_x = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
223
- b_dy = tl.load(dy + dy_off, mask=mask, other=0.).to(tl.float32)
224
- b_s = tl.sigmoid(b_x)
225
- b_dx = b_dy * ((1. - b_s) / temperature)
226
- tl.store(dx + dx_off, b_dx.to(dx.dtype.element_ty), mask=mask)
227
-
228
-
229
- @triton.heuristics(_LINEAR_HEURISTICS_XY)
230
- @triton.jit(do_not_specialize=['T'])
231
- def swish_fwd_kernel(
232
- x, y,
233
- T,
234
- D: tl.constexpr,
235
- stride_x_row,
236
- stride_y_row,
237
- B: tl.constexpr,
238
- X_LINEAR: tl.constexpr,
239
- Y_LINEAR: tl.constexpr,
240
- ):
241
- pid = tl.program_id(0)
242
- offs = pid * B + tl.arange(0, B)
243
- mask = offs < T
244
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
245
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
246
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
247
- s = tl.sigmoid(x_val)
248
- y_val = x_val * s
249
- tl.store(y + y_off, y_val.to(y.dtype.element_ty), mask=mask)
250
-
251
-
252
- @triton.heuristics(_LINEAR_HEURISTICS_BWD)
253
- @triton.jit(do_not_specialize=['T'])
254
- def swish_bwd_kernel(
255
- x, dy, dx,
256
- T,
257
- D: tl.constexpr,
258
- stride_x_row,
259
- stride_dy_row,
260
- stride_dx_row,
261
- B: tl.constexpr,
262
- X_LINEAR: tl.constexpr,
263
- DY_LINEAR: tl.constexpr,
264
- DX_LINEAR: tl.constexpr,
265
- ):
266
- pid = tl.program_id(0)
267
- offs = pid * B + tl.arange(0, B)
268
- mask = offs < T
269
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
270
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
271
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
272
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
273
- g_val = tl.load(dy + dy_off, mask=mask, other=0.).to(tl.float32)
274
- s = tl.sigmoid(x_val)
275
- dx_val = g_val * s * (1.0 + x_val * (1.0 - s))
276
- tl.store(dx + dx_off, dx_val.to(dx.dtype.element_ty), mask=mask)
277
-
278
-
279
- @triton.heuristics(_LINEAR_HEURISTICS_XYZ)
280
- @triton.jit(do_not_specialize=['T'])
281
- def swiglu_fwd_kernel(
282
- x, y, z,
283
- T,
284
- D: tl.constexpr,
285
- stride_x_row,
286
- stride_y_row,
287
- stride_z_row,
288
- B: tl.constexpr,
289
- X_LINEAR: tl.constexpr,
290
- Y_LINEAR: tl.constexpr,
291
- Z_LINEAR: tl.constexpr,
292
- ):
293
- pid = tl.program_id(0)
294
- offs = pid * B + tl.arange(0, B)
295
- mask = offs < T
296
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
297
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
298
- z_off = _flat_offset(offs, D, stride_z_row, Z_LINEAR)
299
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
300
- y_val = tl.load(y + y_off, mask=mask, other=0.).to(tl.float32)
301
- s = tl.sigmoid(x_val)
302
- z_val = x_val * s * y_val
303
- tl.store(z + z_off, z_val.to(z.dtype.element_ty), mask=mask)
304
-
305
-
306
- @triton.heuristics({
307
- 'HAS_WEIGHT': lambda args: args['z'] is not None,
308
- **_LINEAR_HEURISTICS_FWDBWD,
309
- })
310
- @triton.jit(do_not_specialize=['T'])
311
- def swiglu_fwdbwd_kernel(
312
- x, y, g, dx, dy, z,
313
- T,
314
- D: tl.constexpr,
315
- stride_x_row,
316
- stride_y_row,
317
- stride_g_row,
318
- stride_dx_row,
319
- stride_dy_row,
320
- stride_z_row,
321
- B: tl.constexpr,
322
- HAS_WEIGHT: tl.constexpr,
323
- X_LINEAR: tl.constexpr,
324
- Y_LINEAR: tl.constexpr,
325
- G_LINEAR: tl.constexpr,
326
- DX_LINEAR: tl.constexpr,
327
- DY_LINEAR: tl.constexpr,
328
- Z_LINEAR: tl.constexpr,
329
- ):
330
- pid = tl.program_id(0)
331
- offs = pid * B + tl.arange(0, B)
332
- mask = offs < T
333
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
334
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
335
- g_off = _flat_offset(offs, D, stride_g_row, G_LINEAR)
336
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
337
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
338
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
339
- y_val = tl.load(y + y_off, mask=mask, other=0.).to(tl.float32)
340
- g_val = tl.load(g + g_off, mask=mask, other=0.).to(tl.float32)
341
-
342
- s = tl.sigmoid(x_val)
343
- x_s = x_val * s
344
- dx_val = g_val * s * (1.0 + x_val * (1.0 - s)) * y_val
345
- dy_val = g_val * x_s
346
-
347
- tl.store(dx + dx_off, dx_val.to(dx.dtype.element_ty), mask=mask)
348
- tl.store(dy + dy_off, dy_val.to(dy.dtype.element_ty), mask=mask)
349
- if HAS_WEIGHT:
350
- z_off = _flat_offset(offs, D, stride_z_row, Z_LINEAR)
351
- z_val = x_s * y_val
352
- tl.store(z + z_off, z_val.to(z.dtype.element_ty), mask=mask)
353
-
354
-
355
- @triton.heuristics(_LINEAR_HEURISTICS_XY)
356
- @triton.jit(do_not_specialize=['T'])
357
- def gelu_fwd_kernel(
358
- x, y,
359
- T,
360
- D: tl.constexpr,
361
- stride_x_row,
362
- stride_y_row,
363
- B: tl.constexpr,
364
- X_LINEAR: tl.constexpr,
365
- Y_LINEAR: tl.constexpr,
366
- ):
367
- pid = tl.program_id(0)
368
- offs = pid * B + tl.arange(0, B)
369
- mask = offs < T
370
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
371
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
372
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
373
- t = 0.79788456 * x_val * (1.0 + 0.044715 * x_val * x_val)
374
- tanh_out = tl.tanh(t)
375
- y_val = x_val * 0.5 * (1.0 + tanh_out)
376
- tl.store(y + y_off, y_val.to(y.dtype.element_ty), mask=mask)
377
-
378
-
379
- @triton.heuristics(_LINEAR_HEURISTICS_BWD)
380
- @triton.jit(do_not_specialize=['T'])
381
- def gelu_bwd_kernel(
382
- x, dy, dx,
383
- T,
384
- D: tl.constexpr,
385
- stride_x_row,
386
- stride_dy_row,
387
- stride_dx_row,
388
- B: tl.constexpr,
389
- X_LINEAR: tl.constexpr,
390
- DY_LINEAR: tl.constexpr,
391
- DX_LINEAR: tl.constexpr,
392
- ):
393
- pid = tl.program_id(0)
394
- offs = pid * B + tl.arange(0, B)
395
- mask = offs < T
396
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
397
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
398
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
399
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
400
- g_val = tl.load(dy + dy_off, mask=mask, other=0.).to(tl.float32)
401
- t = 0.79788456 * x_val * (1.0 + 0.044715 * x_val * x_val)
402
- tanh_out = tl.tanh(t)
403
- ff = 0.5 * x_val * (
404
- (1.0 - tanh_out * tanh_out) * (0.79788456 + 0.1070322243 * x_val * x_val)
405
- ) + 0.5 * (1.0 + tanh_out)
406
- dx_val = ff * g_val
407
- tl.store(dx + dx_off, dx_val.to(dx.dtype.element_ty), mask=mask)
408
-
409
-
410
- @triton.heuristics(_LINEAR_HEURISTICS_XY)
411
- @triton.jit(do_not_specialize=['T'])
412
- def sqrelu_fwd_kernel(
413
- x, y,
414
- T,
415
- D: tl.constexpr,
416
- stride_x_row,
417
- stride_y_row,
418
- B: tl.constexpr,
419
- X_LINEAR: tl.constexpr,
420
- Y_LINEAR: tl.constexpr,
421
- ):
422
- pid = tl.program_id(0)
423
- offs = pid * B + tl.arange(0, B)
424
- mask = offs < T
425
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
426
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
427
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
428
- r = tl.maximum(x_val, 0.0)
429
- y_val = r * r
430
- tl.store(y + y_off, y_val.to(y.dtype.element_ty), mask=mask)
431
-
432
-
433
- @triton.heuristics(_LINEAR_HEURISTICS_BWD)
434
- @triton.jit(do_not_specialize=['T'])
435
- def sqrelu_bwd_kernel(
436
- x, dy, dx,
437
- T,
438
- D: tl.constexpr,
439
- stride_x_row,
440
- stride_dy_row,
441
- stride_dx_row,
442
- B: tl.constexpr,
443
- X_LINEAR: tl.constexpr,
444
- DY_LINEAR: tl.constexpr,
445
- DX_LINEAR: tl.constexpr,
446
- ):
447
- pid = tl.program_id(0)
448
- offs = pid * B + tl.arange(0, B)
449
- mask = offs < T
450
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
451
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
452
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
453
- x_val = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
454
- g_val = tl.load(dy + dy_off, mask=mask, other=0.).to(tl.float32)
455
- dx_val = 2.0 * g_val * tl.maximum(x_val, 0.0)
456
- tl.store(dx + dx_off, dx_val.to(dx.dtype.element_ty), mask=mask)
457
-
458
-
459
- @torch.compiler.disable
460
- def gelu_fwd_npu(x: torch.Tensor) -> torch.Tensor:
461
- x = _ensure_inner_contiguous(x)
462
- T, D = x.numel(), x.shape[-1]
463
- y = _alloc_output(x)
464
- grid, B = _activation_launch_config(T)
465
- gelu_fwd_kernel[grid](
466
- x, y, T=T, D=D,
467
- stride_x_row=_get_stride(x),
468
- stride_y_row=_get_stride(y),
469
- BLOCK_SIZE=B,
470
- )
471
- return y
472
-
473
-
474
- @torch.compiler.disable
475
- def gelu_bwd_npu(g: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
476
- x = _ensure_inner_contiguous(x)
477
- g = _ensure_inner_contiguous(g)
478
- T, D = x.numel(), x.shape[-1]
479
- dx = _alloc_output(x)
480
- grid, B = _activation_launch_config(T, is_backward=True)
481
- gelu_bwd_kernel[grid](
482
- x, g, dx, T=T, D=D,
483
- stride_x_row=_get_stride(x),
484
- stride_dy_row=_get_stride(g),
485
- stride_dx_row=_get_stride(dx),
486
- BLOCK_SIZE=B,
487
- )
488
- return dx
489
-
490
-
491
- @torch.compiler.disable
492
- def sqrelu_fwd_npu(x: torch.Tensor) -> torch.Tensor:
493
- x = _ensure_inner_contiguous(x)
494
- T, D = x.numel(), x.shape[-1]
495
- y = _alloc_output(x)
496
- grid, B = _activation_launch_config(T)
497
- sqrelu_fwd_kernel[grid](
498
- x, y, T=T, D=D,
499
- stride_x_row=_get_stride(x),
500
- stride_y_row=_get_stride(y),
501
- BLOCK_SIZE=B,
502
- )
503
- return y
504
-
505
-
506
- @torch.compiler.disable
507
- def sqrelu_bwd_npu(g: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
508
- x = _ensure_inner_contiguous(x)
509
- g = _ensure_inner_contiguous(g)
510
- T, D = x.numel(), x.shape[-1]
511
- dx = _alloc_output(x)
512
- grid, B = _activation_launch_config(T, is_backward=True)
513
- sqrelu_bwd_kernel[grid](
514
- x, g, dx, T=T, D=D,
515
- stride_x_row=_get_stride(x),
516
- stride_dy_row=_get_stride(g),
517
- stride_dx_row=_get_stride(dx),
518
- BLOCK_SIZE=B,
519
- )
520
- return dx
521
-
522
-
523
- @torch.compiler.disable
524
- def sigmoid_fwd_npu(x: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
525
- x = _ensure_inner_contiguous(x)
526
- T, D = x.numel(), x.shape[-1]
527
- y = _alloc_output(x, output_contiguous)
528
- grid, B = _activation_launch_config(T)
529
- sigmoid_fwd_kernel[grid](
530
- x, y, T=T, D=D,
531
- stride_x_row=_get_stride(x),
532
- stride_y_row=_get_stride(y),
533
- B=B,
534
- )
535
- return y
536
-
537
-
538
- @torch.compiler.disable
539
- def sigmoid_bwd_npu(x: torch.Tensor, dy: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
540
- x = _ensure_inner_contiguous(x)
541
- dy = _ensure_inner_contiguous(dy)
542
- T, D = x.numel(), x.shape[-1]
543
- dx = _alloc_output(x, output_contiguous)
544
- grid, B = _activation_launch_config(T, is_backward=True)
545
- sigmoid_bwd_kernel[grid](
546
- x, dy, dx, T=T, D=D,
547
- stride_x_row=_get_stride(x),
548
- stride_dy_row=_get_stride(dy),
549
- stride_dx_row=_get_stride(dx),
550
- B=B,
551
- )
552
- return dx
553
-
554
-
555
- @torch.compiler.disable
556
- def logsigmoid_fwd_npu(x: torch.Tensor, temperature: float = 1., output_contiguous: bool = False) -> torch.Tensor:
557
- x = _ensure_inner_contiguous(x)
558
- T, D = x.numel(), x.shape[-1]
559
- y = _alloc_output(x, output_contiguous)
560
- grid, B = _activation_launch_config(T)
561
- logsigmoid_fwd_kernel[grid](
562
- x=x,
563
- y=y,
564
- temperature=temperature,
565
- T=T,
566
- D=D,
567
- stride_x_row=_get_stride(x),
568
- stride_y_row=_get_stride(y),
569
- B=B,
570
- )
571
- return y
572
-
573
-
574
- @torch.compiler.disable
575
- def logsigmoid_bwd_npu(
576
- x: torch.Tensor,
577
- dy: torch.Tensor,
578
- temperature: float = 1.,
579
- output_contiguous: bool = False,
580
- ) -> torch.Tensor:
581
- x = _ensure_inner_contiguous(x)
582
- dy = _ensure_inner_contiguous(dy)
583
- T, D = x.numel(), x.shape[-1]
584
- dx = _alloc_output(x, output_contiguous)
585
- grid, B = _activation_launch_config(T, is_backward=True)
586
- logsigmoid_bwd_kernel[grid](
587
- x=x,
588
- dx=dx,
589
- dy=dy,
590
- temperature=temperature,
591
- T=T,
592
- D=D,
593
- stride_x_row=_get_stride(x),
594
- stride_dx_row=_get_stride(dx),
595
- stride_dy_row=_get_stride(dy),
596
- B=B,
597
- )
598
- return dx
599
-
600
-
601
- @torch.compiler.disable
602
- def swish_fwd_npu(x: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
603
- x = _ensure_inner_contiguous(x)
604
- T, D = x.numel(), x.shape[-1]
605
- y = _alloc_output(x, output_contiguous)
606
- grid, B = _activation_launch_config(T)
607
- swish_fwd_kernel[grid](
608
- x, y, T=T, D=D,
609
- stride_x_row=_get_stride(x),
610
- stride_y_row=_get_stride(y),
611
- B=B,
612
- )
613
- return y
614
-
615
-
616
- @torch.compiler.disable
617
- def swish_bwd_npu(x: torch.Tensor, dy: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
618
- x = _ensure_inner_contiguous(x)
619
- dy = _ensure_inner_contiguous(dy)
620
- T, D = x.numel(), x.shape[-1]
621
- dx = _alloc_output(x, output_contiguous)
622
- grid, B = _activation_launch_config(T, is_backward=True)
623
- swish_bwd_kernel[grid](
624
- x, dy, dx, T=T, D=D,
625
- stride_x_row=_get_stride(x),
626
- stride_dy_row=_get_stride(dy),
627
- stride_dx_row=_get_stride(dx),
628
- B=B,
629
- )
630
- return dx
631
-
632
-
633
- @torch.compiler.disable
634
- def swiglu_fwd_npu(x: torch.Tensor, y: torch.Tensor, output_contiguous: bool = False) -> torch.Tensor:
635
- assert x.shape == y.shape, f"swiglu_fwd: shape mismatch x={x.shape} y={y.shape}"
636
- x = _ensure_inner_contiguous(x)
637
- y = _ensure_inner_contiguous(y)
638
- T, D = x.numel(), x.shape[-1]
639
- z = _alloc_output(x, output_contiguous)
640
- grid, B = _activation_launch_config(T)
641
- swiglu_fwd_kernel[grid](
642
- x, y, z, T=T, D=D,
643
- stride_x_row=_get_stride(x),
644
- stride_y_row=_get_stride(y),
645
- stride_z_row=_get_stride(z),
646
- B=B,
647
- )
648
- return z
649
-
650
-
651
- @torch.compiler.disable
652
- def swiglu_fwdbwd_npu(
653
- x: torch.Tensor,
654
- y: torch.Tensor,
655
- g: torch.Tensor,
656
- use_weight: bool = False,
657
- output_contiguous: bool = False,
658
- ):
659
- assert x.shape == y.shape == g.shape, f"swiglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
660
- x = _ensure_inner_contiguous(x)
661
- y = _ensure_inner_contiguous(y)
662
- g = _ensure_inner_contiguous(g)
663
- T, D = x.numel(), x.shape[-1]
664
- dx = _alloc_output(x, output_contiguous)
665
- dy = _alloc_output(y, output_contiguous)
666
- if use_weight:
667
- z = _alloc_output(x, output_contiguous)
668
- else:
669
- z = None
670
- grid, B = _activation_launch_config(T, is_backward=True)
671
- swiglu_fwdbwd_kernel[grid](
672
- x, y, g, dx, dy, z, T=T, D=D,
673
- stride_x_row=_get_stride(x),
674
- stride_y_row=_get_stride(y),
675
- stride_g_row=_get_stride(g),
676
- stride_dx_row=_get_stride(dx),
677
- stride_dy_row=_get_stride(dy),
678
- stride_z_row=_get_stride(z) if z is not None else 0,
679
- B=B,
680
- )
681
- if use_weight:
682
- return dx, dy, z
683
- return dx, dy
684
-
685
-
686
- class SwiGLULinearFunctionNPU(torch.autograd.Function):
687
-
688
- @staticmethod
689
- @input_guard(no_guard_contiguous=True)
690
- @autocast_custom_fwd
691
- def forward(ctx, x, y, weight, bias):
692
- z = swiglu_fwd_npu(x, y, output_contiguous=True)
693
- out = F.linear(z, weight, bias)
694
- ctx.save_for_backward(x, y, weight)
695
- ctx.linear_bias_is_none = bias is None
696
- return out
697
-
698
- @staticmethod
699
- @input_guard(no_guard_contiguous=True)
700
- @autocast_custom_bwd
701
- def backward(ctx, dout, *args):
702
- x, y, weight = ctx.saved_tensors
703
- dout = dout.reshape(-1, dout.shape[-1])
704
- dz = F.linear(dout, weight.t()).view_as(x)
705
- dx, dy, z = swiglu_fwdbwd_npu(x, y, dz, use_weight=True, output_contiguous=True)
706
- z_flat = z.reshape(-1, z.shape[-1])
707
- dlinear_weight = dout.t() @ z_flat
708
- dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
709
- return dx, dy, dlinear_weight, dlinear_bias
710
-
711
-
712
- def swiglu_linear_npu(x, y, weight, bias):
713
- return SwiGLULinearFunctionNPU.apply(x, y, weight, bias)
714
-
715
-
716
- @triton.heuristics(_LINEAR_HEURISTICS_XYZ)
717
- @triton.jit(do_not_specialize=['T'])
718
- def powglu_fwd_kernel(
719
- x, y, z,
720
- stride_x_row,
721
- stride_y_row,
722
- stride_z_row,
723
- m,
724
- T,
725
- D: tl.constexpr,
726
- B: tl.constexpr,
727
- X_LINEAR: tl.constexpr,
728
- Y_LINEAR: tl.constexpr,
729
- Z_LINEAR: tl.constexpr,
730
- ):
731
- i_n = tl.program_id(0)
732
- offs = i_n * B + tl.arange(0, B)
733
- mask = offs < T
734
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
735
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
736
- z_off = _flat_offset(offs, D, stride_z_row, Z_LINEAR)
737
- b_x = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
738
- b_y = tl.load(y + y_off, mask=mask, other=0.).to(tl.float32)
739
- b_s = tl.sigmoid(b_x)
740
- b_pos = b_x > 0
741
- # feed only positive lanes to log/sqrt; masked lanes give x**p = 1 and are dropped by the where
742
- b_xp = tl.where(b_pos, b_x, 1.0)
743
- b_sqrt = tl.sqrt(b_xp)
744
- b_p = m / (b_sqrt + 1.0)
745
- b_pow = exp(b_p * log(b_xp))
746
- b_g = tl.where(b_pos, b_pow * b_s, b_x * b_s)
747
- b_z = b_g * b_y
748
- tl.store(z + z_off, b_z.to(z.dtype.element_ty), mask=mask)
749
-
750
-
751
- @triton.heuristics({
752
- 'HAS_WEIGHT': lambda args: args['z'] is not None,
753
- **_LINEAR_HEURISTICS_FWDBWD,
754
- })
755
- @triton.jit(do_not_specialize=['T'])
756
- def powglu_fwdbwd_kernel(
757
- x, y, g, dx, dy, z,
758
- stride_x_row,
759
- stride_y_row,
760
- stride_g_row,
761
- stride_dx_row,
762
- stride_dy_row,
763
- stride_z_row,
764
- m,
765
- T,
766
- D: tl.constexpr,
767
- B: tl.constexpr,
768
- HAS_WEIGHT: tl.constexpr,
769
- X_LINEAR: tl.constexpr,
770
- Y_LINEAR: tl.constexpr,
771
- G_LINEAR: tl.constexpr,
772
- DX_LINEAR: tl.constexpr,
773
- DY_LINEAR: tl.constexpr,
774
- Z_LINEAR: tl.constexpr,
775
- ):
776
- i_n = tl.program_id(0)
777
- offs = i_n * B + tl.arange(0, B)
778
- mask = offs < T
779
- x_off = _flat_offset(offs, D, stride_x_row, X_LINEAR)
780
- y_off = _flat_offset(offs, D, stride_y_row, Y_LINEAR)
781
- g_off = _flat_offset(offs, D, stride_g_row, G_LINEAR)
782
- dx_off = _flat_offset(offs, D, stride_dx_row, DX_LINEAR)
783
- dy_off = _flat_offset(offs, D, stride_dy_row, DY_LINEAR)
784
- b_x = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
785
- b_y = tl.load(y + y_off, mask=mask, other=0.).to(tl.float32)
786
- b_g = tl.load(g + g_off, mask=mask, other=0.).to(tl.float32)
787
-
788
- b_s = tl.sigmoid(b_x)
789
- b_pos = b_x > 0
790
- b_xp = tl.where(b_pos, b_x, 1.0)
791
- b_sqrt = tl.sqrt(b_xp)
792
- b_ln = log(b_xp)
793
- b_p = m / (b_sqrt + 1.0)
794
- b_pow = exp(b_p * b_ln)
795
-
796
- b_gate_pos = b_pow * b_s
797
- # d/dx of the exponent term: p' = -m / (2*sqrt(x)*(sqrt(x)+1)**2)
798
- b_pprime = -m / (2.0 * b_sqrt * (b_sqrt + 1.0) * (b_sqrt + 1.0))
799
- b_dgate_pos = b_gate_pos * (b_pprime * b_ln + b_p / b_xp + 1.0 - b_s)
800
- b_gate_neg = b_x * b_s
801
- b_dgate_neg = b_s * (1.0 + b_x * (1.0 - b_s))
802
-
803
- b_gate = tl.where(b_pos, b_gate_pos, b_gate_neg)
804
- b_dgate = tl.where(b_pos, b_dgate_pos, b_dgate_neg)
805
-
806
- b_dx = b_g * b_y * b_dgate
807
- b_dy = b_g * b_gate
808
-
809
- tl.store(dx + dx_off, b_dx.to(dx.dtype.element_ty), mask=mask)
810
- tl.store(dy + dy_off, b_dy.to(dy.dtype.element_ty), mask=mask)
811
- if HAS_WEIGHT:
812
- b_z = b_gate * b_y
813
- z_off = _flat_offset(offs, D, stride_z_row, Z_LINEAR)
814
- tl.store(z + z_off, b_z.to(z.dtype.element_ty), mask=mask)
815
-
816
-
817
- # Peak fp32 temporaries: sigmoid, sqrt, log, exp, pow, gate, output.
818
- _POWGLU_FWD_MEM_MULT = 8.0
819
- _POWGLU_BWD_MEM_MULT = 10.0
820
-
821
-
822
- @torch.compiler.disable
823
- def powglu_fwd_npu(x: torch.Tensor, y: torch.Tensor, power: float = 3.0, output_contiguous: bool = False) -> torch.Tensor:
824
- assert x.shape == y.shape, f"powglu_fwd: shape mismatch x={x.shape} y={y.shape}"
825
- x = _ensure_inner_contiguous(x)
826
- y = _ensure_inner_contiguous(y)
827
- T, D = x.numel(), x.shape[-1]
828
- z = _alloc_output(x, output_contiguous)
829
- grid, B = _activation_launch_config(T, memory_multiplier=_POWGLU_FWD_MEM_MULT)
830
- powglu_fwd_kernel[grid](
831
- x=x,
832
- y=y,
833
- z=z,
834
- stride_x_row=_get_stride(x),
835
- stride_y_row=_get_stride(y),
836
- stride_z_row=_get_stride(z),
837
- m=power,
838
- T=T,
839
- D=D,
840
- B=B,
841
- )
842
- return z
843
-
844
-
845
- @torch.compiler.disable
846
- def powglu_fwdbwd_npu(
847
- x: torch.Tensor,
848
- y: torch.Tensor,
849
- g: torch.Tensor,
850
- power: float = 3.0,
851
- use_weight: bool = False,
852
- output_contiguous: bool = False,
853
- ):
854
- assert x.shape == y.shape == g.shape, f"powglu_fwdbwd: shape mismatch x={x.shape} y={y.shape} g={g.shape}"
855
- x = _ensure_inner_contiguous(x)
856
- y = _ensure_inner_contiguous(y)
857
- g = _ensure_inner_contiguous(g)
858
- T, D = x.numel(), x.shape[-1]
859
- dx = _alloc_output(x, output_contiguous)
860
- dy = _alloc_output(y, output_contiguous)
861
- if use_weight:
862
- z = _alloc_output(x, output_contiguous)
863
- else:
864
- z = None
865
- grid, B = _activation_launch_config(T, is_backward=True, memory_multiplier=_POWGLU_BWD_MEM_MULT)
866
- powglu_fwdbwd_kernel[grid](
867
- x=x,
868
- y=y,
869
- g=g,
870
- dx=dx,
871
- dy=dy,
872
- z=z,
873
- stride_x_row=_get_stride(x),
874
- stride_y_row=_get_stride(y),
875
- stride_g_row=_get_stride(g),
876
- stride_dx_row=_get_stride(dx),
877
- stride_dy_row=_get_stride(dy),
878
- stride_z_row=_get_stride(z) if z is not None else 0,
879
- m=power,
880
- T=T,
881
- D=D,
882
- B=B,
883
- )
884
- if use_weight:
885
- return dx, dy, z
886
- return dx, dy
887
-
888
-
889
- class PowGLULinearFunctionNPU(torch.autograd.Function):
890
- r"""
891
- Power-Gated Linear Unit (PowGLU) function followed by a linear transformation.
892
-
893
- .. math::
894
- \text{PowGLULinear}(x, y, W, b) = (g(x) * y) W + b
895
-
896
- This simple wrap discards the intermediate results of PowGLU(x, y) to save memory.
897
- """
898
-
899
- @staticmethod
900
- @input_guard(no_guard_contiguous=True)
901
- @autocast_custom_fwd
902
- def forward(ctx, x, y, weight, bias, power):
903
- z = powglu_fwd_npu(x, y, power, output_contiguous=True)
904
- out = F.linear(z, weight, bias)
905
- ctx.save_for_backward(x, y, weight)
906
- ctx.linear_bias_is_none = bias is None
907
- ctx.power = power
908
- return out
909
-
910
- @staticmethod
911
- @input_guard(no_guard_contiguous=True)
912
- @autocast_custom_bwd
913
- def backward(ctx, dout, *args):
914
- x, y, weight = ctx.saved_tensors
915
- dout = dout.reshape(-1, dout.shape[-1])
916
- dz = F.linear(dout, weight.t()).view_as(x)
917
- dx, dy, z = powglu_fwdbwd_npu(x, y, dz, ctx.power, use_weight=True, output_contiguous=True)
918
- z_flat = z.reshape(-1, z.shape[-1])
919
- dlinear_weight = dout.t() @ z_flat
920
- dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
921
- return dx, dy, dlinear_weight, dlinear_bias, None
922
-
923
-
924
- def powglu_linear_npu(
925
- x: torch.Tensor,
926
- y: torch.Tensor,
927
- weight: torch.Tensor,
928
- bias: torch.Tensor,
929
- power: float = 3.0,
930
- ) -> torch.Tensor:
931
- return PowGLULinearFunctionNPU.apply(x, y, weight, bias, power)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/causal_conv1d.py DELETED
@@ -1,1175 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Causal 1D convolution kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import triton
12
- import triton.language as tl
13
- from einops import rearrange
14
-
15
- from ....ops.utils import prepare_chunk_indices
16
- from ....utils import input_guard
17
-
18
- STATIC_WARPS = 2
19
- # Ascend Triton rejects grids whose product exceeds 65535 (see fla/modules/token_shift.py).
20
- _NPU_MAX_TRITON_GRID = 65535
21
- _ELEM_BLOCK = 2048
22
-
23
-
24
- def _elementwise_launch_iters(numel: int):
25
- n_blocks = triton.cdiv(numel, _ELEM_BLOCK)
26
- for block_off in range(0, n_blocks, _NPU_MAX_TRITON_GRID):
27
- yield min(_NPU_MAX_TRITON_GRID, n_blocks - block_off), block_off * _ELEM_BLOCK
28
-
29
-
30
- def _npu_chunk_size(T: int, BT: int) -> int:
31
- BT = min(max(BT, 1), 64)
32
- if BT not in (1, 2, 4, 8, 16, 32, 64):
33
- BT = triton.next_power_of_2(BT)
34
- # Ascend compiler requires power-of-2 BT; pad with mask when BT > T.
35
- if T not in (1, 2, 4, 8, 16, 32, 64):
36
- BT = min(triton.next_power_of_2(T), 64)
37
- else:
38
- BT = min(BT, T, 64)
39
- return BT
40
-
41
-
42
- def _clamp_bd_for_grid(B: int, NT: int, D: int, BD: int) -> int:
43
- while triton.cdiv(D, BD) * NT * B > _NPU_MAX_TRITON_GRID and BD < 64:
44
- BD *= 2
45
- return BD
46
-
47
-
48
- def _npu_max_axis_chunks(grid_dim0: int, batch: int = 1) -> int:
49
- denom = grid_dim0 * batch
50
- if denom > _NPU_MAX_TRITON_GRID:
51
- raise RuntimeError(
52
- f'Ascend Triton grid dim0*batch={denom} exceeds {_NPU_MAX_TRITON_GRID}',
53
- )
54
- return max(1, _NPU_MAX_TRITON_GRID // max(denom, 1))
55
-
56
-
57
- def _npu_tile_config(
58
- T: int,
59
- BT: int,
60
- D: int,
61
- dtype: torch.dtype,
62
- initial_state: torch.Tensor | None,
63
- ) -> tuple[int, int, int]:
64
- BT = _npu_chunk_size(T, BT)
65
- BD = 16
66
- if D >= 8192:
67
- BD = 8
68
- BT = min(BT, 8)
69
- elif D >= 1024:
70
- # BD=4 overflows Ascend UB on large-D forward; cap BT to limit NT.
71
- BD = 8
72
- BT = min(BT, 32)
73
- elif D >= 512:
74
- BD = 8
75
- if dtype == torch.float16 and initial_state is not None:
76
- BD = min(BD, 8)
77
- if dtype == torch.bfloat16 and T <= 16:
78
- BD = 8
79
- return BD, BT, STATIC_WARPS
80
-
81
-
82
- def _npu_bwd_tile_config(
83
- T: int,
84
- BT: int,
85
- D: int,
86
- dtype: torch.dtype,
87
- initial_state: torch.Tensor | None,
88
- ) -> tuple[int, int, int]:
89
- BT = _npu_chunk_size(T, BT)
90
- BD = 16
91
- if initial_state is not None:
92
- BD = min(BD, 8)
93
- BT = min(BT, 32)
94
- if D >= 2048:
95
- BD = 8
96
- BT = min(BT, 8)
97
- elif D >= 1024:
98
- BD = 8
99
- BT = min(BT, 16)
100
- elif D >= 512:
101
- BD = 8
102
- BT = min(BT, 32)
103
- if dtype == torch.bfloat16 and T <= 16:
104
- BD = 8
105
- BT = 32
106
- return BD, BT, STATIC_WARPS
107
-
108
-
109
- @triton.heuristics({
110
- 'HAS_WEIGHT': lambda args: args['weight'] is not None,
111
- 'HAS_BIAS': lambda args: args['bias'] is not None,
112
- 'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
113
- 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
114
- })
115
- @triton.jit
116
- def causal_conv1d_fwd_kernel(
117
- x,
118
- y,
119
- weight,
120
- bias,
121
- cu_seqlens,
122
- initial_state,
123
- chunk_indices,
124
- B,
125
- T,
126
- stride_x_n,
127
- stride_x_t,
128
- stride_x_d,
129
- stride_y_n,
130
- stride_y_t,
131
- stride_y_d,
132
- D: tl.constexpr,
133
- W: tl.constexpr,
134
- BT: tl.constexpr,
135
- BW: tl.constexpr,
136
- BD: tl.constexpr,
137
- HAS_WEIGHT: tl.constexpr,
138
- HAS_BIAS: tl.constexpr,
139
- USE_INITIAL_STATE: tl.constexpr,
140
- IS_VARLEN: tl.constexpr,
141
- CHUNK_OFFSET: tl.constexpr,
142
- ):
143
- i_d, i_t, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2)
144
-
145
- if IS_VARLEN:
146
- i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
147
- bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
148
- T = eos - bos
149
- p_x = x + bos * stride_x_t
150
- p_y = y + bos * stride_y_t
151
- else:
152
- i_n = i_b
153
- i_t = i_t + CHUNK_OFFSET
154
- bos = (i_b * T).to(tl.int64)
155
- p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
156
- p_y = y + tl.cast(i_b, tl.int64) * stride_y_n
157
-
158
- o_d = i_d * BD + tl.arange(0, BD)
159
- o_w = tl.arange(0, BW) + W - BW
160
- m_d = o_d < D
161
- m_w = o_w >= 0
162
-
163
- if HAS_WEIGHT:
164
- b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0).to(tl.float32)
165
-
166
- o_t = i_t * BT + tl.arange(0, BT)
167
- m_t = (o_t >= 0) & (o_t < T)
168
- b_y = tl.zeros((BT, BD), dtype=tl.float32)
169
-
170
- for i_w in tl.static_range(-W + 1, 1):
171
- o_x = o_t + i_w
172
- m_x = ((o_x >= 0) & (o_x < T))[:, None] & m_d[None, :]
173
- b_yi = tl.load(
174
- p_x + o_x[:, None] * stride_x_t + o_d[None, :] * stride_x_d,
175
- mask=m_x,
176
- other=0,
177
- ).to(tl.float32)
178
-
179
- if USE_INITIAL_STATE:
180
- m_c = ((o_x + W >= 0) & (o_x < 0))[:, None] & m_d[None, :]
181
- b_yi += tl.load(
182
- initial_state + i_n * D * W + o_d[None, :] * W + (o_x + W)[:, None],
183
- mask=m_c,
184
- other=0,
185
- ).to(tl.float32)
186
-
187
- if HAS_WEIGHT:
188
- b_yi = b_yi * tl.sum(b_w * (o_w == (i_w + W - 1)), 1)[None, :]
189
- b_y += b_yi
190
-
191
- if HAS_BIAS:
192
- b_y += tl.load(bias + o_d, mask=m_d).to(tl.float32)[None, :]
193
-
194
- tl.store(
195
- p_y + o_t[:, None] * stride_y_t + o_d[None, :] * stride_y_d,
196
- tl.cast(b_y, dtype=y.dtype.element_ty, fp_downcast_rounding='rtne'),
197
- mask=m_t[:, None] & m_d[None, :],
198
- )
199
-
200
-
201
- @triton.jit
202
- def _silu_kernel(
203
- x_ptr,
204
- y_ptr,
205
- n_elements,
206
- ELEM_OFFSET: tl.constexpr,
207
- BLOCK: tl.constexpr,
208
- ):
209
- pid = tl.program_id(0)
210
- offs = pid * BLOCK + tl.arange(0, BLOCK) + ELEM_OFFSET
211
- mask = offs < n_elements
212
- x = tl.load(x_ptr + offs, mask=mask, other=0.).to(tl.float32)
213
- y = x * tl.sigmoid(x)
214
- tl.store(y_ptr + offs, y.to(y_ptr.dtype.element_ty), mask=mask)
215
-
216
-
217
- @triton.jit
218
- def _add_kernel(
219
- a_ptr,
220
- b_ptr,
221
- out_ptr,
222
- n_elements,
223
- ELEM_OFFSET: tl.constexpr,
224
- BLOCK: tl.constexpr,
225
- ):
226
- pid = tl.program_id(0)
227
- offs = pid * BLOCK + tl.arange(0, BLOCK) + ELEM_OFFSET
228
- mask = offs < n_elements
229
- a = tl.load(a_ptr + offs, mask=mask, other=0.).to(tl.float32)
230
- b = tl.load(b_ptr + offs, mask=mask, other=0.).to(tl.float32)
231
- tl.store(out_ptr + offs, (a + b).to(out_ptr.dtype.element_ty), mask=mask)
232
-
233
-
234
- def _launch_silu(y: torch.Tensor) -> torch.Tensor:
235
- y = y.contiguous()
236
- out = torch.zeros_like(y)
237
- n = y.numel()
238
- for grid, elem_off in _elementwise_launch_iters(n):
239
- _silu_kernel[(grid,)](
240
- y, out, n,
241
- ELEM_OFFSET=elem_off,
242
- BLOCK=_ELEM_BLOCK,
243
- num_warps=STATIC_WARPS,
244
- )
245
- return out
246
-
247
-
248
- def _launch_add(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
249
- a = a.contiguous()
250
- b = b.contiguous()
251
- out = torch.zeros_like(a)
252
- n = a.numel()
253
- for grid, elem_off in _elementwise_launch_iters(n):
254
- _add_kernel[(grid,)](
255
- a, b, out, n,
256
- ELEM_OFFSET=elem_off,
257
- BLOCK=_ELEM_BLOCK,
258
- num_warps=STATIC_WARPS,
259
- )
260
- return out
261
-
262
-
263
- @triton.jit
264
- def _silu_bwd_kernel(
265
- y_ptr,
266
- dy_ptr,
267
- out_ptr,
268
- stride_y_n,
269
- stride_y_t,
270
- stride_y_d,
271
- stride_dy_n,
272
- stride_dy_t,
273
- stride_dy_d,
274
- stride_out_n,
275
- stride_out_t,
276
- stride_out_d,
277
- B,
278
- T,
279
- D,
280
- ELEM_OFFSET: tl.constexpr,
281
- BLOCK: tl.constexpr,
282
- ):
283
- pid = tl.program_id(0)
284
- offs = pid * BLOCK + tl.arange(0, BLOCK) + ELEM_OFFSET
285
- n_elements = B * T * D
286
- mask = offs < n_elements
287
- rem = offs % D
288
- d = rem
289
- rem = (offs - d) // D
290
- t = rem % T
291
- b = rem // T
292
- y_off = b * stride_y_n + t * stride_y_t + d * stride_y_d
293
- dy_off = b * stride_dy_n + t * stride_dy_t + d * stride_dy_d
294
- out_off = b * stride_out_n + t * stride_out_t + d * stride_out_d
295
- y = tl.load(y_ptr + y_off, mask=mask, other=0.).to(tl.float32)
296
- dy = tl.load(dy_ptr + dy_off, mask=mask, other=0.).to(tl.float32)
297
- s = tl.sigmoid(y)
298
- out = dy * s * (1.0 + y * (1.0 - s))
299
- tl.store(out_ptr + out_off, out.to(out_ptr.dtype.element_ty), mask=mask)
300
-
301
-
302
- def _launch_silu_bwd(y_pre: torch.Tensor, dy: torch.Tensor) -> torch.Tensor:
303
- out = torch.zeros_like(dy, memory_format=torch.contiguous_format)
304
- B, T, D = dy.shape
305
- n = B * T * D
306
- sy_n, sy_t, sy_d = y_pre.stride()
307
- sdy_n, sdy_t, sdy_d = dy.stride()
308
- so_n, so_t, so_d = out.stride()
309
- for grid, elem_off in _elementwise_launch_iters(n):
310
- _silu_bwd_kernel[(grid,)](
311
- y_pre, dy, out,
312
- sy_n, sy_t, sy_d,
313
- sdy_n, sdy_t, sdy_d,
314
- so_n, so_t, so_d,
315
- B, T, D,
316
- ELEM_OFFSET=elem_off,
317
- BLOCK=_ELEM_BLOCK,
318
- num_warps=STATIC_WARPS,
319
- )
320
- return out
321
-
322
-
323
- def _postprocess_fwd(
324
- y: torch.Tensor,
325
- residual: torch.Tensor | None,
326
- activation: str | None,
327
- ) -> torch.Tensor:
328
- if activation in ('swish', 'silu'):
329
- y = _launch_silu(y)
330
- if residual is not None:
331
- if residual.stride() != y.stride():
332
- residual = residual.contiguous()
333
- y = _launch_add(y, residual)
334
- return y
335
-
336
-
337
- def _use_seq_bwd(
338
- T: int,
339
- dtype: torch.dtype,
340
- initial_state: torch.Tensor | None,
341
- dht: torch.Tensor | None,
342
- cu_seqlens: torch.Tensor | None,
343
- ) -> bool:
344
- return (
345
- cu_seqlens is None
346
- and initial_state is None
347
- and dht is None
348
- and dtype == torch.bfloat16
349
- and T <= 16
350
- )
351
-
352
-
353
- @triton.heuristics({
354
- 'HAS_WEIGHT': lambda args: args['dw'] is not None,
355
- 'HAS_BIAS': lambda args: args['db'] is not None,
356
- })
357
- @triton.jit
358
- def causal_conv1d_bwd_seq_kernel(
359
- x,
360
- weight,
361
- dy,
362
- dx,
363
- dw,
364
- db,
365
- stride_x_n,
366
- stride_x_t,
367
- stride_x_d,
368
- stride_dx_n,
369
- stride_dx_t,
370
- stride_dx_d,
371
- stride_dy_n,
372
- stride_dy_t,
373
- stride_dy_d,
374
- B,
375
- TC: tl.constexpr,
376
- D: tl.constexpr,
377
- W: tl.constexpr,
378
- HAS_WEIGHT: tl.constexpr,
379
- HAS_BIAS: tl.constexpr,
380
- BLOCK: tl.constexpr,
381
- ):
382
- pid = tl.program_id(0)
383
- offs = pid * BLOCK + tl.arange(0, BLOCK)
384
- n_elements = B * TC * D
385
- mask = offs < n_elements
386
- d = offs % D
387
- tmp = offs // D
388
- t = tmp % TC
389
- b = tmp // TC
390
-
391
- b_dx = tl.zeros((BLOCK,), dtype=tl.float32)
392
- for i_w in tl.static_range(0, W):
393
- t_dy = t + i_w
394
- dy_off = b * stride_dy_n + t_dy * stride_dy_t + d * stride_dy_d
395
- b_dy = tl.load(dy + dy_off, mask=mask & (t_dy < TC), other=0.).to(tl.float32)
396
- if HAS_WEIGHT:
397
- w_idx = W - i_w - 1
398
- b_w = tl.load(weight + d * W + w_idx, mask=mask, other=0.).to(tl.float32)
399
- b_dx += b_dy * b_w
400
- else:
401
- b_dx += b_dy
402
-
403
- dx_off = b * stride_dx_n + t * stride_dx_t + d * stride_dx_d
404
- tl.store(dx + dx_off, b_dx.to(dx.dtype.element_ty), mask=mask)
405
-
406
- if HAS_WEIGHT:
407
- x_off = b * stride_x_n + t * stride_x_t + d * stride_x_d
408
- b_x = tl.load(x + x_off, mask=mask, other=0.).to(tl.float32)
409
- i_tg = b * TC + t
410
- for i_w in tl.static_range(0, W):
411
- t_dy = t + i_w
412
- dy_off = b * stride_dy_n + t_dy * stride_dy_t + d * stride_dy_d
413
- b_dy = tl.load(dy + dy_off, mask=mask & (t_dy < TC), other=0.).to(tl.float32)
414
- w_idx = W - i_w - 1
415
- tl.store(
416
- dw + (i_tg * D + d) * W + w_idx,
417
- (b_dy * b_x).to(dw.dtype.element_ty),
418
- mask=mask,
419
- )
420
-
421
- if HAS_BIAS:
422
- i_tg = b * TC + t
423
- dy_off = b * stride_dy_n + t * stride_dy_t + d * stride_dy_d
424
- b_dy0 = tl.load(dy + dy_off, mask=mask, other=0.)
425
- tl.store(db + i_tg * D + d, b_dy0.to(db.dtype.element_ty), mask=mask)
426
-
427
-
428
- @triton.heuristics({
429
- 'HAS_WEIGHT': lambda args: args['dw'] is not None,
430
- 'HAS_BIAS': lambda args: args['db'] is not None,
431
- 'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
432
- 'USE_FINAL_STATE': lambda args: args['dht'] is not None,
433
- 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
434
- })
435
- @triton.jit
436
- def causal_conv1d_bwd_kernel(
437
- x,
438
- weight,
439
- initial_state,
440
- dht,
441
- dy,
442
- dx,
443
- dw,
444
- db,
445
- cu_seqlens,
446
- chunk_indices,
447
- B,
448
- T,
449
- stride_x_n,
450
- stride_x_t,
451
- stride_x_d,
452
- stride_dx_n,
453
- stride_dx_t,
454
- stride_dx_d,
455
- stride_dy_n,
456
- stride_dy_t,
457
- stride_dy_d,
458
- D: tl.constexpr,
459
- W: tl.constexpr,
460
- BT: tl.constexpr,
461
- BW: tl.constexpr,
462
- BD: tl.constexpr,
463
- HAS_WEIGHT: tl.constexpr,
464
- HAS_BIAS: tl.constexpr,
465
- USE_INITIAL_STATE: tl.constexpr,
466
- USE_FINAL_STATE: tl.constexpr,
467
- IS_VARLEN: tl.constexpr,
468
- CHUNK_OFFSET: tl.constexpr,
469
- NT: tl.constexpr,
470
- ):
471
- i_d, i_t, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2)
472
- if IS_VARLEN:
473
- i_tg = i_t
474
- i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
475
- bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
476
- T = eos - bos
477
- p_x = x + bos * stride_x_t
478
- p_dy = dy + bos * stride_dy_t
479
- p_dx = dx + bos * stride_dx_t
480
- else:
481
- i_t = i_t + CHUNK_OFFSET
482
- i_tg = i_b * NT + i_t
483
- i_n = i_b
484
- p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
485
- p_dy = dy + tl.cast(i_b, tl.int64) * stride_dy_n
486
- p_dx = dx + tl.cast(i_b, tl.int64) * stride_dx_n
487
-
488
- o_d = i_d * BD + tl.arange(0, BD)
489
- o_w = tl.arange(0, BW) + W - BW
490
- m_d = o_d < D
491
- m_w = o_w >= 0
492
-
493
- o_t = i_t * BT + tl.arange(0, BT)
494
- m_t = (o_t >= 0) & (o_t < T)
495
-
496
- b_x = tl.zeros((BT, BD), dtype=tl.float32)
497
- if HAS_WEIGHT:
498
- b_x = tl.load(
499
- p_x + o_t[:, None] * stride_x_t + o_d[None, :] * stride_x_d,
500
- mask=m_t[:, None] & m_d[None, :],
501
- other=0,
502
- ).to(tl.float32)
503
- b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0).to(tl.float32)
504
-
505
- b_dx = tl.zeros((BT, BD), dtype=tl.float32)
506
- if HAS_BIAS:
507
- b_db = tl.zeros((BD,), dtype=tl.float32)
508
-
509
- for i_w in tl.static_range(0, W):
510
- o_dy = o_t + i_w
511
- m_dy = ((o_dy >= 0) & (o_dy < T))[:, None] & m_d[None, :]
512
- b_dy = tl.load(
513
- p_dy + o_dy[:, None] * stride_dy_t + o_d[None, :] * stride_dy_d,
514
- mask=m_dy,
515
- other=0,
516
- ).to(tl.float32)
517
-
518
- if HAS_WEIGHT:
519
- b_wdy = b_dy * tl.sum(b_w * (o_w == (W - i_w - 1)), 1)[None, :]
520
- b_dw = tl.sum(b_dy * b_x, 0)
521
- if USE_INITIAL_STATE:
522
- mask_head_rows = (o_t < i_w) & (o_t < T)
523
- b_dy_head = tl.load(
524
- p_dy + o_t[:, None] * stride_dy_t + o_d[None, :] * stride_dy_d,
525
- mask=(mask_head_rows[:, None] & m_d[None, :]),
526
- other=0.0,
527
- ).to(tl.float32)
528
- o_c = W - i_w + o_t
529
- mask_c = (mask_head_rows & (o_c >= 1) & (o_c < W))
530
- b_xc = tl.load(
531
- initial_state + i_n * D * W + o_d[None, :] * W + o_c[:, None],
532
- mask=(mask_c[:, None] & m_d[None, :]),
533
- other=0.0,
534
- ).to(tl.float32)
535
- b_dw += tl.sum(b_dy_head * b_xc, 0)
536
- tl.store(dw + i_tg * D * W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
537
- else:
538
- b_wdy = b_dy
539
-
540
- if HAS_BIAS and i_w == 0:
541
- b_db += tl.sum(b_dy, 0)
542
- b_dx += b_wdy
543
-
544
- if HAS_BIAS:
545
- b_db = tl.cast(b_db, dtype=db.dtype.element_ty, fp_downcast_rounding='rtne')
546
- tl.store(db + i_tg * D + o_d, b_db, mask=m_d)
547
-
548
- if USE_FINAL_STATE:
549
- if i_t * BT + BT >= T - W:
550
- start_tok = T - (W - 1)
551
- offset = i_t * BT + tl.arange(0, BT)
552
- tok_idx = offset - start_tok
553
- mask = (offset >= start_tok) & (offset < T)
554
- w_idx = 1 + tok_idx
555
- dht_off = i_n * D * W + o_d[None, :] * W + w_idx[:, None]
556
- b_dht = tl.load(dht + dht_off, mask=mask[:, None] & m_d[None, :], other=0.).to(tl.float32)
557
- b_dx += b_dht
558
-
559
- tl.store(
560
- p_dx + o_t[:, None] * stride_dx_t + o_d[None, :] * stride_dx_d,
561
- tl.cast(b_dx, dtype=dx.dtype.element_ty, fp_downcast_rounding='rtne'),
562
- mask=m_t[:, None] & m_d[None, :],
563
- )
564
-
565
-
566
- @triton.heuristics({
567
- 'USE_ACTIVATION': lambda args: args['y'] is not None,
568
- 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
569
- })
570
- @triton.jit
571
- def compute_dh0_kernel(
572
- dy,
573
- y,
574
- weight,
575
- dh0,
576
- cu_seqlens,
577
- stride_dy_n,
578
- stride_dy_t,
579
- stride_dy_d,
580
- stride_y_n,
581
- stride_y_t,
582
- stride_y_d,
583
- T,
584
- D: tl.constexpr,
585
- W: tl.constexpr,
586
- BD: tl.constexpr,
587
- USE_ACTIVATION: tl.constexpr,
588
- IS_VARLEN: tl.constexpr,
589
- CHUNK_OFFSET: tl.constexpr,
590
- ):
591
- i_d, i_n = tl.program_id(0), tl.program_id(1) + CHUNK_OFFSET
592
-
593
- if IS_VARLEN:
594
- bos = tl.load(cu_seqlens + i_n).to(tl.int64)
595
- eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
596
- seq_len = eos - bos
597
- dy_base = dy + bos * stride_dy_t
598
- else:
599
- seq_len = T
600
- dy_base = dy + tl.cast(i_n, tl.int64) * stride_dy_n
601
-
602
- o_d = i_d * BD + tl.arange(0, BD)
603
- m_d = o_d < D
604
-
605
- for i_w in tl.static_range(1, W):
606
- b_dh0 = tl.zeros([BD], dtype=tl.float32)
607
-
608
- for t in tl.static_range(0, W - 1):
609
- if t < i_w:
610
- w_idx = i_w - 1 - t
611
- p_dy = dy_base + t * stride_dy_t + o_d * stride_dy_d
612
- m_t = (t < seq_len) & m_d
613
- b_dy = tl.load(p_dy, mask=m_t, other=0).to(tl.float32)
614
-
615
- if USE_ACTIVATION:
616
- if IS_VARLEN:
617
- p_y = y + bos * stride_y_t + t * stride_y_t + o_d * stride_y_d
618
- else:
619
- p_y = y + tl.cast(i_n, tl.int64) * stride_y_n + t * stride_y_t + o_d * stride_y_d
620
- b_y = tl.load(p_y, mask=m_t, other=0).to(tl.float32)
621
- b_ys = tl.sigmoid(b_y)
622
- b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
623
-
624
- b_w_col = tl.load(weight + o_d * W + w_idx, mask=m_d, other=0).to(tl.float32)
625
- b_dh0 += tl.where(m_t, b_dy * b_w_col, 0)
626
-
627
- p_dh0 = dh0 + i_n * D * W + o_d * W + i_w
628
- tl.store(p_dh0, b_dh0.to(dh0.dtype.element_ty), mask=m_d)
629
-
630
-
631
- @triton.heuristics({
632
- 'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
633
- 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
634
- })
635
- @triton.jit
636
- def causal_conv1d_states_fwd_kernel(
637
- x,
638
- initial_state,
639
- final_state,
640
- cu_seqlens,
641
- T,
642
- D,
643
- W,
644
- stride_x_n,
645
- stride_x_t,
646
- stride_x_d,
647
- BD: tl.constexpr,
648
- BW: tl.constexpr,
649
- USE_INITIAL_STATE: tl.constexpr,
650
- IS_VARLEN: tl.constexpr,
651
- CHUNK_OFFSET: tl.constexpr,
652
- ):
653
- i_d, i_n = tl.program_id(0), tl.program_id(1) + CHUNK_OFFSET
654
-
655
- o_d = i_d * BD + tl.arange(0, BD)
656
- m_d = o_d < D
657
-
658
- if IS_VARLEN:
659
- bos = tl.load(cu_seqlens + i_n).to(tl.int64)
660
- eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
661
- seq_len = (eos - bos).to(tl.int32)
662
- p_x = x + bos * stride_x_t
663
- else:
664
- seq_len = T
665
- p_x = x + tl.cast(i_n, tl.int64) * stride_x_n
666
-
667
- o_w = W - BW + tl.arange(0, BW)
668
- m_w = o_w >= 0
669
- o_t = seq_len - BW + tl.arange(0, BW)
670
- m_t = (o_t >= 0) & (o_t < seq_len)
671
-
672
- b_x = tl.load(
673
- p_x + o_t[:, None] * stride_x_t + o_d[None, :] * stride_x_d,
674
- mask=m_t[:, None] & m_d[None, :],
675
- other=0,
676
- ).to(tl.float32)
677
-
678
- if USE_INITIAL_STATE:
679
- if seq_len < BW:
680
- o_c = W - (BW - seq_len) + tl.arange(0, BW)
681
- m_c = (o_c >= 0) & (o_c < W)
682
- b_cache = tl.load(
683
- initial_state + i_n * D * W + o_d[None, :] * W + o_c[:, None],
684
- mask=m_d[None, :] & m_c[:, None],
685
- other=0,
686
- ).to(tl.float32)
687
- b_x += b_cache
688
-
689
- p_final = final_state + tl.cast(i_n, tl.int64) * D * W + o_d[:, None] * W + o_w[None, :]
690
- tl.store(p_final, tl.trans(b_x).to(final_state.dtype.element_ty), mask=m_d[:, None] & m_w[None, :])
691
-
692
-
693
- @triton.heuristics({
694
- 'HAS_WEIGHT': lambda args: args['weight'] is not None,
695
- 'HAS_BIAS': lambda args: args['bias'] is not None,
696
- })
697
- @triton.jit
698
- def causal_conv1d_update_kernel(
699
- x,
700
- cache,
701
- y,
702
- weight,
703
- bias,
704
- stride_x_n,
705
- stride_x_d,
706
- stride_y_n,
707
- stride_y_d,
708
- D: tl.constexpr,
709
- W: tl.constexpr,
710
- BD: tl.constexpr,
711
- HAS_WEIGHT: tl.constexpr,
712
- HAS_BIAS: tl.constexpr,
713
- CHUNK_OFFSET: tl.constexpr,
714
- ):
715
- i_d, i_n = tl.program_id(0), tl.program_id(1) + CHUNK_OFFSET
716
-
717
- o_d = i_d * BD + tl.arange(0, BD)
718
- m_d = o_d < D
719
-
720
- b_x = tl.load(x + i_n * stride_x_n + o_d * stride_x_d, mask=m_d, other=0).to(tl.float32)
721
-
722
- b_y = tl.zeros((BD,), dtype=tl.float32)
723
- for iw in tl.static_range(0, W):
724
- if iw < W - 1:
725
- b_c = tl.load(cache + i_n * D * W + o_d * W + (iw + 1), mask=m_d, other=0).to(tl.float32)
726
- else:
727
- b_c = b_x
728
- tl.store(
729
- cache + i_n * D * W + o_d * W + iw,
730
- tl.cast(b_c, dtype=cache.dtype.element_ty, fp_downcast_rounding='rtne'),
731
- mask=m_d,
732
- )
733
- if HAS_WEIGHT:
734
- b_y += b_c * tl.load(weight + o_d * W + iw, mask=m_d, other=0).to(tl.float32)
735
- else:
736
- b_y += b_c
737
-
738
- if HAS_BIAS:
739
- b_y += tl.load(bias + o_d, mask=m_d)
740
-
741
- tl.store(
742
- y + i_n * stride_y_n + o_d * stride_y_d,
743
- tl.cast(b_y, dtype=y.dtype.element_ty, fp_downcast_rounding='rtne'),
744
- mask=m_d,
745
- )
746
-
747
-
748
- def _postprocess_update(
749
- y: torch.Tensor,
750
- residual: torch.Tensor | None,
751
- activation: str | None,
752
- ) -> torch.Tensor:
753
- if activation in ('swish', 'silu'):
754
- y = _launch_silu(y)
755
- if residual is not None:
756
- if residual.stride() != y.stride():
757
- residual = residual.contiguous()
758
- y = _launch_add(y, residual)
759
- return y
760
-
761
-
762
- def _launch_fwd_core(
763
- x: torch.Tensor,
764
- weight: torch.Tensor,
765
- bias: torch.Tensor,
766
- initial_state: torch.Tensor | None,
767
- cu_seqlens: torch.LongTensor | None,
768
- chunk_indices: torch.LongTensor | None,
769
- B: int,
770
- T: int,
771
- D: int,
772
- W: int,
773
- BT: int,
774
- BD: int | None = None,
775
- num_warps: int | None = None,
776
- ) -> torch.Tensor:
777
- if BD is None or num_warps is None:
778
- BD, BT, num_warps = _npu_tile_config(T, BT, D, x.dtype, initial_state)
779
- NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
780
- BD = _clamp_bd_for_grid(B, NT, D, BD)
781
- BW = triton.next_power_of_2(W)
782
-
783
- stride_x_n, stride_x_t, stride_x_d = x.stride()
784
- y = torch.zeros_like(x, memory_format=torch.contiguous_format)
785
- stride_y_n, stride_y_t, stride_y_d = y.stride()
786
-
787
- max_nt = _npu_max_axis_chunks(triton.cdiv(D, BD), B)
788
- kernel_kwargs = dict(
789
- x=x,
790
- y=y,
791
- weight=weight,
792
- bias=bias,
793
- cu_seqlens=cu_seqlens,
794
- initial_state=initial_state,
795
- B=B,
796
- T=T,
797
- D=D,
798
- W=W,
799
- BT=BT,
800
- BW=BW,
801
- BD=BD,
802
- stride_x_n=stride_x_n,
803
- stride_x_t=stride_x_t,
804
- stride_x_d=stride_x_d,
805
- stride_y_n=stride_y_n,
806
- stride_y_t=stride_y_t,
807
- stride_y_d=stride_y_d,
808
- num_warps=num_warps,
809
- )
810
- for nt_off in range(0, NT, max_nt):
811
- nt_len = min(max_nt, NT - nt_off)
812
- grid = (triton.cdiv(D, BD), nt_len, B)
813
- if cu_seqlens is not None:
814
- kernel_kwargs['chunk_indices'] = chunk_indices[nt_off:nt_off + nt_len]
815
- kernel_kwargs['CHUNK_OFFSET'] = 0
816
- else:
817
- kernel_kwargs['chunk_indices'] = chunk_indices
818
- kernel_kwargs['CHUNK_OFFSET'] = nt_off
819
- causal_conv1d_fwd_kernel[grid](**kernel_kwargs)
820
- return y
821
-
822
-
823
- @input_guard(no_guard_contiguous=['x'])
824
- def causal_conv1d_fwd_npu(
825
- x: torch.Tensor,
826
- weight: torch.Tensor,
827
- bias: torch.Tensor,
828
- residual: torch.Tensor,
829
- initial_state: torch.Tensor | None = None,
830
- output_final_state: bool = False,
831
- activation: str | None = None,
832
- cu_seqlens: torch.LongTensor | None = None,
833
- cu_seqlens_cpu: torch.LongTensor | None = None,
834
- chunk_indices: torch.LongTensor | None = None,
835
- BT: int = 64,
836
- layout_fallback: bool = False,
837
- ):
838
- del layout_fallback
839
- shape = x.shape
840
- if x.shape[-1] != weight.shape[0]:
841
- x = rearrange(x, 'b t ... -> b t (...)')
842
- B, T, D = x.shape[0], x.shape[1], weight.shape[0]
843
- W = weight.shape[1]
844
-
845
- BD, BT, num_warps = _npu_tile_config(T, BT, D, x.dtype, initial_state)
846
- if cu_seqlens is not None:
847
- chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
848
-
849
- y = _launch_fwd_core(
850
- x, weight, bias, initial_state, cu_seqlens, chunk_indices, B, T, D, W, BT, BD, num_warps,
851
- )
852
- y = _postprocess_fwd(y, residual, activation)
853
-
854
- final_state = None
855
- if output_final_state:
856
- final_state = causal_conv1d_update_states_npu(
857
- x=x,
858
- state_len=W,
859
- initial_state=initial_state,
860
- cu_seqlens=cu_seqlens,
861
- )
862
- return y.view(shape), final_state
863
-
864
-
865
- def causal_conv1d_bwd_npu(
866
- x: torch.Tensor,
867
- dy: torch.Tensor,
868
- dht: torch.Tensor,
869
- weight: torch.Tensor | None = None,
870
- bias: torch.Tensor | None = None,
871
- residual: torch.Tensor | None = None,
872
- initial_state: torch.Tensor | None = None,
873
- activation: str | None = None,
874
- cu_seqlens: torch.Tensor | None = None,
875
- cu_seqlens_cpu: torch.LongTensor | None = None,
876
- chunk_indices: torch.LongTensor | None = None,
877
- BT: int = 64,
878
- layout_fallback: bool = False,
879
- ):
880
- del layout_fallback
881
- shape = x.shape
882
- if x.shape[-1] != weight.shape[0]:
883
- x = rearrange(x, 'b t ... -> b t (...)')
884
- B, T, D = x.shape
885
- W = weight.shape[1] if weight is not None else None
886
-
887
- BD, BT, num_warps = _npu_bwd_tile_config(T, BT, D, x.dtype, initial_state)
888
- if cu_seqlens is not None:
889
- chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
890
- NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
891
- BD = _clamp_bd_for_grid(B, NT, D, BD)
892
- BW = triton.next_power_of_2(W)
893
-
894
- dr = dy if residual is not None else None
895
- dy_conv = dy
896
-
897
- y_pre = None
898
- if activation in ('swish', 'silu'):
899
- BD_f, BT_f, nw_f = _npu_tile_config(T, BT, D, x.dtype, initial_state)
900
- chunk_indices_f = chunk_indices
901
- if cu_seqlens is not None:
902
- chunk_indices_f = prepare_chunk_indices(cu_seqlens, BT_f, cu_seqlens_cpu=cu_seqlens_cpu)
903
- y_pre = _launch_fwd_core(
904
- x, weight, bias, initial_state, cu_seqlens, chunk_indices_f,
905
- B, T, D, W, BT_f, BD_f, nw_f,
906
- )
907
- dy_conv = _launch_silu_bwd(y_pre, dy)
908
-
909
- stride_x_n, stride_x_t, stride_x_d = x.stride()
910
- use_seq = _use_seq_bwd(T, x.dtype, initial_state, dht, cu_seqlens)
911
- stride_dy_n, stride_dy_t, stride_dy_d = dy_conv.stride()
912
-
913
- dx = torch.zeros_like(x)
914
- stride_dx_n, stride_dx_t, stride_dx_d = dx.stride()
915
-
916
- if use_seq:
917
- block = 1024
918
- dw = weight.new_empty(B * T, *weight.shape, dtype=torch.float) if weight is not None else None
919
- db = bias.new_empty(B * T, *bias.shape, dtype=torch.float) if bias is not None else None
920
- grid = (triton.cdiv(B * T * D, block),)
921
- causal_conv1d_bwd_seq_kernel[grid](
922
- x=x,
923
- weight=weight,
924
- dy=dy_conv,
925
- dx=dx,
926
- dw=dw,
927
- db=db,
928
- stride_x_n=stride_x_n,
929
- stride_x_t=stride_x_t,
930
- stride_x_d=stride_x_d,
931
- stride_dx_n=stride_dx_n,
932
- stride_dx_t=stride_dx_t,
933
- stride_dx_d=stride_dx_d,
934
- stride_dy_n=stride_dy_n,
935
- stride_dy_t=stride_dy_t,
936
- stride_dy_d=stride_dy_d,
937
- B=B,
938
- TC=T,
939
- D=D,
940
- W=W,
941
- BLOCK=block,
942
- num_warps=STATIC_WARPS,
943
- )
944
- else:
945
- if not dy_conv.is_contiguous():
946
- dy_conv = dy_conv.contiguous()
947
- stride_dy_n, stride_dy_t, stride_dy_d = dy_conv.stride()
948
- dw = weight.new_empty(B * NT, *weight.shape, dtype=torch.float) if weight is not None else None
949
- db = bias.new_empty(B * NT, *bias.shape, dtype=torch.float) if bias is not None else None
950
- max_nt = _npu_max_axis_chunks(triton.cdiv(D, BD), B)
951
- kernel_kwargs = dict(
952
- x=x,
953
- weight=weight,
954
- initial_state=initial_state,
955
- dht=dht,
956
- dy=dy_conv,
957
- dx=dx,
958
- cu_seqlens=cu_seqlens,
959
- B=B,
960
- T=T,
961
- D=D,
962
- W=W,
963
- BT=BT,
964
- BW=BW,
965
- BD=BD,
966
- stride_x_n=stride_x_n,
967
- stride_x_t=stride_x_t,
968
- stride_x_d=stride_x_d,
969
- stride_dx_n=stride_dx_n,
970
- stride_dx_t=stride_dx_t,
971
- stride_dx_d=stride_dx_d,
972
- stride_dy_n=stride_dy_n,
973
- stride_dy_t=stride_dy_t,
974
- stride_dy_d=stride_dy_d,
975
- num_warps=num_warps,
976
- NT=NT,
977
- )
978
- for nt_off in range(0, NT, max_nt):
979
- nt_len = min(max_nt, NT - nt_off)
980
- grid = (triton.cdiv(D, BD), nt_len, B)
981
- if cu_seqlens is not None:
982
- kernel_kwargs['chunk_indices'] = chunk_indices[nt_off:nt_off + nt_len]
983
- kernel_kwargs['CHUNK_OFFSET'] = 0
984
- kernel_kwargs['dw'] = dw[nt_off:nt_off + nt_len] if weight is not None else None
985
- kernel_kwargs['db'] = db[nt_off:nt_off + nt_len] if bias is not None else None
986
- else:
987
- kernel_kwargs['chunk_indices'] = chunk_indices
988
- kernel_kwargs['CHUNK_OFFSET'] = nt_off
989
- kernel_kwargs['dw'] = dw
990
- kernel_kwargs['db'] = db
991
- causal_conv1d_bwd_kernel[grid](**kernel_kwargs)
992
- if weight is not None:
993
- dw = dw.sum(0).to(weight)
994
- if bias is not None:
995
- db = db.sum(0).to(bias)
996
-
997
- dh0 = None
998
- if initial_state is not None:
999
- dh0 = compute_dh0_npu(
1000
- dy=dy,
1001
- y=y_pre,
1002
- weight=weight,
1003
- initial_state=initial_state,
1004
- activation=activation,
1005
- cu_seqlens=cu_seqlens,
1006
- )
1007
-
1008
- return dx.view(shape), dw, db, dr, dh0
1009
-
1010
-
1011
- def compute_dh0_npu(
1012
- dy: torch.Tensor,
1013
- y: torch.Tensor | None,
1014
- weight: torch.Tensor,
1015
- initial_state: torch.Tensor,
1016
- activation: str | None,
1017
- cu_seqlens: torch.Tensor | None,
1018
- ) -> torch.Tensor:
1019
- D, W = weight.shape
1020
- N = initial_state.shape[0]
1021
- T = dy.shape[1]
1022
-
1023
- BD = 8 if dy.dtype == torch.float16 and activation in ('swish', 'silu') else 16
1024
- dh0 = torch.zeros_like(initial_state)
1025
-
1026
- stride_dy_n = dy.stride(0)
1027
- stride_dy_t = dy.stride(1)
1028
- stride_dy_d = dy.stride(2) if dy.dim() == 3 else dy.stride(-1)
1029
- stride_y_n = stride_y_t = stride_y_d = 0
1030
- if y is not None:
1031
- stride_y_n = y.stride(0)
1032
- stride_y_t = y.stride(1)
1033
- stride_y_d = y.stride(2) if y.dim() == 3 else y.stride(-1)
1034
-
1035
- max_n = _npu_max_axis_chunks(triton.cdiv(D, BD))
1036
- kernel_kwargs = dict(
1037
- dy=dy,
1038
- y=y if activation in ('swish', 'silu') else None,
1039
- weight=weight,
1040
- dh0=dh0,
1041
- cu_seqlens=cu_seqlens,
1042
- stride_dy_n=stride_dy_n,
1043
- stride_dy_t=stride_dy_t,
1044
- stride_dy_d=stride_dy_d,
1045
- stride_y_n=stride_y_n,
1046
- stride_y_t=stride_y_t,
1047
- stride_y_d=stride_y_d,
1048
- T=T,
1049
- D=D,
1050
- W=W,
1051
- BD=BD,
1052
- num_warps=STATIC_WARPS,
1053
- )
1054
- for n_off in range(0, N, max_n):
1055
- n_len = min(max_n, N - n_off)
1056
- kernel_kwargs['CHUNK_OFFSET'] = n_off
1057
- compute_dh0_kernel[(triton.cdiv(D, BD), n_len)](**kernel_kwargs)
1058
- return dh0
1059
-
1060
-
1061
- @input_guard(no_guard_contiguous=['x'])
1062
- def causal_conv1d_update_states_npu(
1063
- x: torch.Tensor,
1064
- state_len: int,
1065
- initial_state: torch.Tensor | None = None,
1066
- cu_seqlens: torch.Tensor | None = None,
1067
- layout_fallback: bool = False,
1068
- ) -> torch.Tensor:
1069
- del layout_fallback
1070
- if cu_seqlens is not None:
1071
- N = len(cu_seqlens) - 1
1072
- if x.dim() == 2:
1073
- stride_x_n = 0
1074
- stride_x_t, stride_x_d = x.stride()
1075
- T = x.shape[0]
1076
- else:
1077
- stride_x_n = x.stride(0)
1078
- stride_x_t, stride_x_d = x.stride(1), x.stride(2)
1079
- T = x.shape[1]
1080
- D = x.shape[-1]
1081
- else:
1082
- B, T, D = x.shape
1083
- N = B
1084
- stride_x_n, stride_x_t, stride_x_d = x.stride()
1085
-
1086
- W = state_len
1087
- final_state = torch.empty(N, D, W, dtype=x.dtype, device=x.device)
1088
- BD = min(triton.next_power_of_2(D), 16)
1089
- BW = triton.next_power_of_2(W)
1090
- grid_dim0 = triton.cdiv(D, BD)
1091
- max_n = _npu_max_axis_chunks(grid_dim0)
1092
- kernel_kwargs = dict(
1093
- x=x,
1094
- initial_state=initial_state,
1095
- final_state=final_state,
1096
- cu_seqlens=cu_seqlens,
1097
- T=T,
1098
- D=D,
1099
- W=W,
1100
- stride_x_n=stride_x_n,
1101
- stride_x_t=stride_x_t,
1102
- stride_x_d=stride_x_d,
1103
- BW=BW,
1104
- BD=BD,
1105
- num_warps=STATIC_WARPS,
1106
- )
1107
- for n_off in range(0, N, max_n):
1108
- n_len = min(max_n, N - n_off)
1109
- kernel_kwargs['CHUNK_OFFSET'] = n_off
1110
- causal_conv1d_states_fwd_kernel[(grid_dim0, n_len)](**kernel_kwargs)
1111
- return final_state
1112
-
1113
-
1114
- @input_guard(no_guard_contiguous=['x'])
1115
- def causal_conv1d_update_npu(
1116
- x: torch.Tensor,
1117
- cache: torch.Tensor,
1118
- residual: torch.Tensor | None = None,
1119
- weight: torch.Tensor | None = None,
1120
- bias: torch.Tensor | None = None,
1121
- activation: str | None = None,
1122
- ) -> tuple[torch.Tensor, torch.Tensor]:
1123
- shape = x.shape
1124
- if weight is not None and x.shape[-1] != weight.shape[0]:
1125
- x = rearrange(x, 'b t ... -> b t (...)')
1126
-
1127
- D = x.shape[-1]
1128
- N = x.numel() // D
1129
- W = weight.shape[1] if weight is not None else None
1130
- BD = min(triton.next_power_of_2(D), 16)
1131
-
1132
- if x.dim() == 2:
1133
- stride_x_n = x.stride(0)
1134
- stride_x_d = x.stride(1)
1135
- elif x.dim() == 3 and x.shape[0] == 1:
1136
- stride_x_n = x.stride(1)
1137
- stride_x_d = x.stride(2)
1138
- elif x.dim() == 3:
1139
- stride_x_n = x.stride(0)
1140
- stride_x_d = x.stride(2)
1141
- else:
1142
- raise ValueError(f"Unsupported input shape: {x.shape}")
1143
-
1144
- y = torch.zeros_like(x, memory_format=torch.contiguous_format)
1145
-
1146
- if y.dim() == 2:
1147
- stride_y_n, stride_y_d = y.stride(0), y.stride(1)
1148
- elif y.dim() == 3 and y.shape[0] == 1:
1149
- stride_y_n, stride_y_d = y.stride(1), y.stride(2)
1150
- elif y.dim() == 3:
1151
- stride_y_n, stride_y_d = y.stride(0), y.stride(2)
1152
-
1153
- grid_dim0 = triton.cdiv(D, BD)
1154
- max_n = _npu_max_axis_chunks(grid_dim0)
1155
- kernel_kwargs = dict(
1156
- x=x,
1157
- cache=cache,
1158
- y=y,
1159
- weight=weight,
1160
- bias=bias,
1161
- stride_x_n=stride_x_n,
1162
- stride_x_d=stride_x_d,
1163
- stride_y_n=stride_y_n,
1164
- stride_y_d=stride_y_d,
1165
- D=D,
1166
- W=W,
1167
- BD=BD,
1168
- num_warps=STATIC_WARPS,
1169
- )
1170
- for n_off in range(0, N, max_n):
1171
- n_len = min(max_n, N - n_off)
1172
- kernel_kwargs['CHUNK_OFFSET'] = n_off
1173
- causal_conv1d_update_kernel[(grid_dim0, n_len)](**kernel_kwargs)
1174
- y = _postprocess_update(y, residual, activation)
1175
- return y.view(shape), cache
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/fused_cross_entropy.py DELETED
@@ -1,469 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Fused cross-entropy kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import triton
12
- import triton.language as tl
13
- from triton.language.math import tanh
14
-
15
- from ....ops.utils.op import exp, log
16
- from ....utils import input_guard
17
- from ....utils.ascend_ub_manager import (
18
- ASCEND_MAX_GRID_DIM,
19
- compute_vocab_block_size,
20
- iter_axis_launch_chunks,
21
- )
22
-
23
- # Cross-entropy fwd/bwd peak fp32 buffers along vocab dimension.
24
- _CE_FWD_MEM_MULT = 8.0
25
- _CE_BWD_MEM_MULT = 12.0
26
-
27
-
28
- @triton.heuristics({
29
- "HAS_SMOOTHING": lambda args: args["label_smoothing"] > 0.0,
30
- })
31
- @triton.jit
32
- def cross_entropy_fwd_kernel(
33
- loss_ptr,
34
- lse_ptr,
35
- z_loss_ptr,
36
- logits_ptr,
37
- labels_ptr,
38
- label_smoothing,
39
- logit_scale,
40
- lse_square_scale,
41
- logit_softcapping: tl.constexpr,
42
- ignore_index,
43
- total_classes,
44
- class_start_idx,
45
- n_cols,
46
- n_rows,
47
- logits_row_stride,
48
- ROW_OFFSET,
49
- BLOCK_SIZE: tl.constexpr,
50
- HAS_SMOOTHING: tl.constexpr,
51
- HAS_SOFTCAPPING: tl.constexpr,
52
- SPLIT: tl.constexpr,
53
- ):
54
- row_idx = tl.program_id(0)
55
- abs_row_idx = row_idx + ROW_OFFSET
56
- col_block_idx = tl.program_id(1)
57
- logits_ptr = logits_ptr + row_idx * logits_row_stride.to(tl.int64)
58
- col_offsets = col_block_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
59
- label_idx = tl.load(labels_ptr + row_idx)
60
- logits = tl.load(logits_ptr + col_offsets, mask=col_offsets < n_cols, other=-float("inf"))
61
- logits = logits.to(tl.float32) * logit_scale
62
- if HAS_SOFTCAPPING:
63
- logits = logit_softcapping * tanh(logits / logit_softcapping)
64
- max_logits = tl.max(logits, 0)
65
- if HAS_SMOOTHING:
66
- sum_logits = tl.sum(tl.where(col_offsets < n_cols, logits, 0.0), 0)
67
- lse = log(tl.sum(exp(logits - max_logits), 0)) + max_logits
68
- tl.store(lse_ptr + col_block_idx * n_rows + abs_row_idx, lse)
69
- if label_idx == ignore_index:
70
- loss = 0.0
71
- z_loss = 0.0
72
- else:
73
- label_idx -= class_start_idx
74
- if label_idx >= col_block_idx * BLOCK_SIZE and label_idx < min(
75
- n_cols, (col_block_idx + 1) * BLOCK_SIZE,
76
- ):
77
- logits_label = tl.load(logits_ptr + label_idx).to(tl.float32) * logit_scale
78
- if HAS_SOFTCAPPING:
79
- logits_label = logit_softcapping * tanh(logits_label / logit_softcapping)
80
- if HAS_SMOOTHING:
81
- loss = (
82
- (lse if not SPLIT else 0.0)
83
- - label_smoothing * sum_logits / total_classes
84
- - (1 - label_smoothing) * logits_label
85
- )
86
- else:
87
- loss = (lse if not SPLIT else 0.0) - logits_label
88
- else:
89
- if HAS_SMOOTHING:
90
- loss = label_smoothing * ((lse if not SPLIT else 0.0) - sum_logits / total_classes)
91
- else:
92
- loss = 0.0
93
- if not SPLIT:
94
- z_loss = lse_square_scale * lse * lse
95
- loss += z_loss
96
- else:
97
- z_loss = 0.0
98
- tl.store(loss_ptr + col_block_idx * n_rows + abs_row_idx, loss)
99
- if not SPLIT:
100
- tl.store(z_loss_ptr + col_block_idx * n_rows + abs_row_idx, z_loss)
101
-
102
-
103
- @triton.heuristics({
104
- "HAS_SMOOTHING": lambda args: args["label_smoothing"] > 0.0,
105
- })
106
- @triton.jit
107
- def cross_entropy_bwd_kernel(
108
- dlogits_ptr,
109
- dloss_ptr,
110
- logits_ptr,
111
- lse_ptr,
112
- labels_ptr,
113
- label_smoothing,
114
- logit_scale,
115
- lse_square_scale,
116
- logit_softcapping: tl.constexpr,
117
- ignore_index,
118
- total_classes,
119
- class_start_idx,
120
- n_cols,
121
- logits_row_stride,
122
- dlogits_row_stride,
123
- dloss_row_stride,
124
- ROW_OFFSET,
125
- BLOCK_SIZE: tl.constexpr,
126
- HAS_SMOOTHING: tl.constexpr,
127
- HAS_SOFTCAPPING: tl.constexpr,
128
- ):
129
- row_idx = tl.program_id(0)
130
- abs_row_idx = row_idx + ROW_OFFSET
131
- col_block_idx = tl.program_id(1)
132
- logits_ptr = logits_ptr + row_idx * logits_row_stride.to(tl.int64)
133
- dlogits_ptr = dlogits_ptr + row_idx * dlogits_row_stride.to(tl.int64)
134
- col_offsets = col_block_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
135
- label_idx = tl.load(labels_ptr + row_idx)
136
- if label_idx != ignore_index:
137
- dloss = tl.load(dloss_ptr + abs_row_idx * dloss_row_stride)
138
- else:
139
- dloss = 0.0
140
- logits = tl.load(logits_ptr + col_offsets, mask=col_offsets < n_cols, other=-float("inf")).to(
141
- tl.float32,
142
- ) * logit_scale
143
- if HAS_SOFTCAPPING:
144
- t = tanh(logits / logit_softcapping)
145
- logits = logit_softcapping * t
146
- lse = tl.load(lse_ptr + abs_row_idx)
147
- probs = exp(logits - lse)
148
- probs += 2.0 * lse_square_scale * lse * probs
149
- label_idx -= class_start_idx
150
- if HAS_SMOOTHING:
151
- smooth_negative = label_smoothing / total_classes
152
- probs = tl.where(col_offsets == label_idx, probs - (1 - label_smoothing), probs) - smooth_negative
153
- else:
154
- probs = tl.where(col_offsets == label_idx, probs - 1.0, probs)
155
- if HAS_SOFTCAPPING:
156
- probs = probs * (1.0 - t * t)
157
- tl.store(dlogits_ptr + col_offsets, (dloss * logit_scale) * probs, mask=col_offsets < n_cols)
158
-
159
-
160
- def _npu_block_size(n_cols: int, n_rows: int, is_backward: bool = False) -> tuple[int, int]:
161
- memory_multiplier = _CE_BWD_MEM_MULT if is_backward else _CE_FWD_MEM_MULT
162
- block_size = compute_vocab_block_size(n_cols, n_rows, memory_multiplier)
163
- num_warps = 2 if block_size <= 2048 else 4
164
- return block_size, num_warps
165
-
166
-
167
- def _launch_cross_entropy_fwd(
168
- losses,
169
- lse,
170
- z_losses,
171
- logits,
172
- target,
173
- *,
174
- label_smoothing,
175
- logit_scale,
176
- lse_square_scale,
177
- softcap_val,
178
- ignore_index,
179
- total_classes,
180
- class_start_idx,
181
- n_cols,
182
- n_rows,
183
- logits_stride,
184
- BLOCK_SIZE,
185
- has_softcapping,
186
- num_warps,
187
- split,
188
- ):
189
- n_splits = triton.cdiv(n_cols, BLOCK_SIZE)
190
- for row_off, row_len in iter_axis_launch_chunks(n_rows, n_splits, max_grid=ASCEND_MAX_GRID_DIM):
191
- cross_entropy_fwd_kernel[(row_len, n_splits)](
192
- losses,
193
- lse,
194
- z_losses,
195
- logits[row_off:row_off + row_len],
196
- target[row_off:row_off + row_len],
197
- label_smoothing,
198
- logit_scale,
199
- lse_square_scale,
200
- softcap_val,
201
- ignore_index,
202
- total_classes,
203
- class_start_idx,
204
- n_cols,
205
- n_rows,
206
- logits_stride,
207
- row_off,
208
- BLOCK_SIZE=BLOCK_SIZE,
209
- HAS_SOFTCAPPING=has_softcapping,
210
- num_warps=num_warps,
211
- SPLIT=split,
212
- )
213
-
214
-
215
- def _launch_cross_entropy_bwd(
216
- dlogits,
217
- grad_losses,
218
- logits,
219
- lse,
220
- target,
221
- *,
222
- label_smoothing,
223
- logit_scale,
224
- lse_square_scale,
225
- softcap_val,
226
- ignore_index,
227
- total_classes,
228
- class_start_idx,
229
- n_cols,
230
- n_rows,
231
- logits_stride,
232
- dlogits_stride,
233
- grad_losses_stride,
234
- BLOCK_SIZE,
235
- has_softcapping,
236
- num_warps,
237
- ):
238
- n_splits = triton.cdiv(n_cols, BLOCK_SIZE)
239
- for row_off, row_len in iter_axis_launch_chunks(n_rows, n_splits, max_grid=ASCEND_MAX_GRID_DIM):
240
- cross_entropy_bwd_kernel[(row_len, n_splits)](
241
- dlogits[row_off:row_off + row_len],
242
- grad_losses,
243
- logits[row_off:row_off + row_len],
244
- lse,
245
- target[row_off:row_off + row_len],
246
- label_smoothing,
247
- logit_scale,
248
- lse_square_scale,
249
- softcap_val,
250
- ignore_index,
251
- total_classes,
252
- class_start_idx,
253
- n_cols,
254
- logits_stride,
255
- dlogits_stride,
256
- grad_losses_stride,
257
- row_off,
258
- BLOCK_SIZE=BLOCK_SIZE,
259
- HAS_SOFTCAPPING=has_softcapping,
260
- num_warps=num_warps,
261
- )
262
-
263
-
264
- def fused_cross_entropy_forward_npu(
265
- logits: torch.Tensor,
266
- target: torch.Tensor,
267
- label_smoothing: float = 0.0,
268
- logit_scale: float = 1.0,
269
- lse_square_scale: float = 0.0,
270
- logit_softcapping: float = None,
271
- ignore_index: int = -100,
272
- process_group=None,
273
- ):
274
- n_rows, n_cols = logits.shape
275
- assert target.shape == (n_rows,)
276
- world_size = 1 if process_group is None else torch.distributed.get_world_size(process_group)
277
- total_classes = world_size * n_cols
278
- rank = 0 if process_group is None else torch.distributed.get_rank(process_group)
279
- class_start_idx = rank * n_cols
280
-
281
- if logits.stride(-1) != 1:
282
- logits = logits.contiguous()
283
-
284
- MAX_BLOCK_SIZE = 64 * 1024
285
- BLOCK_SIZE, num_warps = _npu_block_size(n_cols, n_rows)
286
- has_softcapping = logit_softcapping is not None
287
- softcap_val = float(logit_softcapping) if has_softcapping else 0.0
288
- n_splits = (n_cols + BLOCK_SIZE - 1) // BLOCK_SIZE
289
- split = world_size > 1 or n_cols > MAX_BLOCK_SIZE or n_splits > 1
290
- loss_shape = (n_splits, n_rows) if n_splits > 1 else (n_rows,)
291
- losses = torch.empty(*loss_shape, dtype=torch.float, device=logits.device)
292
- lse = torch.empty(*loss_shape, dtype=torch.float, device=logits.device)
293
- z_losses = torch.empty(*loss_shape, dtype=torch.float, device=logits.device)
294
-
295
- _launch_cross_entropy_fwd(
296
- losses,
297
- lse,
298
- z_losses,
299
- logits,
300
- target,
301
- label_smoothing=label_smoothing,
302
- logit_scale=logit_scale,
303
- lse_square_scale=lse_square_scale,
304
- softcap_val=softcap_val,
305
- ignore_index=ignore_index,
306
- total_classes=total_classes,
307
- class_start_idx=class_start_idx,
308
- n_cols=n_cols,
309
- n_rows=n_rows,
310
- logits_stride=logits.stride(0),
311
- BLOCK_SIZE=BLOCK_SIZE,
312
- has_softcapping=has_softcapping,
313
- num_warps=num_warps,
314
- split=split,
315
- )
316
-
317
- if split:
318
- if n_splits > 1:
319
- lse = torch.logsumexp(lse, dim=0)
320
- losses = losses.sum(dim=0)
321
- if world_size > 1:
322
- lse_allgather = torch.empty(world_size, n_rows, dtype=lse.dtype, device=lse.device)
323
- torch.distributed.all_gather_into_tensor(lse_allgather, lse, group=process_group)
324
- handle_losses = torch.distributed.all_reduce(
325
- losses, op=torch.distributed.ReduceOp.SUM, group=process_group, async_op=True,
326
- )
327
- lse = torch.logsumexp(lse_allgather, dim=0)
328
- handle_losses.wait()
329
- losses += lse
330
- if lse_square_scale != 0.0:
331
- z_losses = lse_square_scale * lse.square()
332
- z_losses.masked_fill_(target == ignore_index, 0.0)
333
- losses += z_losses
334
- else:
335
- z_losses = torch.zeros_like(losses)
336
- losses.masked_fill_(target == ignore_index, 0.0)
337
-
338
- return losses, z_losses, lse, total_classes, class_start_idx
339
-
340
-
341
- def fused_cross_entropy_backward_npu(
342
- dlogits: torch.Tensor,
343
- grad_losses: torch.Tensor,
344
- logits: torch.Tensor,
345
- lse: torch.Tensor,
346
- target: torch.Tensor,
347
- label_smoothing: float,
348
- logit_scale: float,
349
- lse_square_scale: float,
350
- logit_softcapping: float | None,
351
- ignore_index: int,
352
- total_classes: int,
353
- class_start_idx: int,
354
- ) -> torch.Tensor:
355
- n_rows, n_cols = logits.shape
356
- BLOCK_SIZE, num_warps = _npu_block_size(n_cols, n_rows, is_backward=True)
357
- has_softcapping = logit_softcapping is not None
358
- softcap_val = float(logit_softcapping) if has_softcapping else 0.0
359
-
360
- _launch_cross_entropy_bwd(
361
- dlogits,
362
- grad_losses,
363
- logits,
364
- lse,
365
- target,
366
- label_smoothing=label_smoothing,
367
- logit_scale=logit_scale,
368
- lse_square_scale=lse_square_scale,
369
- softcap_val=softcap_val,
370
- ignore_index=ignore_index,
371
- total_classes=total_classes,
372
- class_start_idx=class_start_idx,
373
- n_cols=n_cols,
374
- n_rows=n_rows,
375
- logits_stride=logits.stride(0),
376
- dlogits_stride=dlogits.stride(0),
377
- grad_losses_stride=grad_losses.stride(0),
378
- BLOCK_SIZE=BLOCK_SIZE,
379
- has_softcapping=has_softcapping,
380
- num_warps=num_warps,
381
- )
382
- return dlogits
383
-
384
-
385
- class CrossEntropyLossFunctionNPU(torch.autograd.Function):
386
-
387
- @staticmethod
388
- @input_guard
389
- def forward(
390
- ctx,
391
- logits,
392
- target,
393
- label_smoothing=0.0,
394
- logit_scale=1.0,
395
- lse_square_scale=0.0,
396
- logit_softcapping=None,
397
- ignore_index=-100,
398
- inplace_backward=False,
399
- process_group=None,
400
- ):
401
- losses, z_losses, lse, total_classes, class_start_idx = fused_cross_entropy_forward_npu(
402
- logits,
403
- target,
404
- label_smoothing,
405
- logit_scale,
406
- lse_square_scale,
407
- logit_softcapping,
408
- ignore_index,
409
- process_group,
410
- )
411
- ctx.save_for_backward(logits, lse, target)
412
- ctx.mark_non_differentiable(z_losses)
413
- ctx.label_smoothing = label_smoothing
414
- ctx.logit_scale = logit_scale
415
- ctx.lse_square_scale = lse_square_scale
416
- ctx.logit_softcapping = logit_softcapping
417
- ctx.ignore_index = ignore_index
418
- ctx.total_classes = total_classes
419
- ctx.class_start_idx = class_start_idx
420
- ctx.inplace_backward = inplace_backward
421
-
422
- return losses, z_losses
423
-
424
- @staticmethod
425
- @input_guard
426
- def backward(ctx, grad_losses, grad_z_losses):
427
- del grad_z_losses
428
-
429
- logits, lse, target = ctx.saved_tensors
430
- dlogits = logits if ctx.inplace_backward else torch.empty_like(logits)
431
- fused_cross_entropy_backward_npu(
432
- dlogits,
433
- grad_losses,
434
- logits,
435
- lse,
436
- target,
437
- ctx.label_smoothing,
438
- ctx.logit_scale,
439
- ctx.lse_square_scale,
440
- ctx.logit_softcapping,
441
- ctx.ignore_index,
442
- ctx.total_classes,
443
- ctx.class_start_idx,
444
- )
445
- return dlogits, None, None, None, None, None, None, None, None, None
446
-
447
-
448
- def cross_entropy_loss_npu(
449
- logits: torch.Tensor,
450
- target: torch.Tensor,
451
- label_smoothing: float = 0.0,
452
- logit_scale: float = 1.0,
453
- lse_square_scale: float = 0.0,
454
- logit_softcapping: float = None,
455
- ignore_index: int = -100,
456
- inplace_backward: bool = False,
457
- process_group=None,
458
- ) -> tuple[torch.Tensor, torch.Tensor]:
459
- return CrossEntropyLossFunctionNPU.apply(
460
- logits,
461
- target,
462
- label_smoothing,
463
- logit_scale,
464
- lse_square_scale,
465
- logit_softcapping,
466
- ignore_index,
467
- inplace_backward,
468
- process_group,
469
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/fused_kl_div.py DELETED
@@ -1,188 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Fused KL divergence kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import torch.nn.functional as F
12
- import triton
13
- import triton.language as tl
14
-
15
- from ....ops.utils.op import exp, log
16
- from ....utils.ascend_ub_manager import ASCEND_MAX_GRID_DIM, compute_elementwise_block_size, compute_vocab_block_size
17
-
18
- _KLD_FWD_MEM_MULT = 12.0
19
- _ELEMENTWISE_MEM_MULT = 2.5
20
- STATIC_WARPS = 2
21
-
22
-
23
- @triton.jit
24
- def kl_div_kernel(
25
- logits,
26
- target_logits,
27
- loss,
28
- s_logits,
29
- s_loss,
30
- reduction: tl.constexpr,
31
- N: tl.constexpr,
32
- V: tl.constexpr,
33
- BV: tl.constexpr,
34
- ):
35
- i_n = tl.program_id(0).to(tl.int64)
36
-
37
- logits += i_n * s_logits
38
- target_logits += i_n * s_logits
39
-
40
- sm = float('-inf')
41
- tm = float('-inf')
42
- sd, td = 0.0, 0.0
43
-
44
- NV = tl.cdiv(V, BV)
45
- for iv in range(0, NV):
46
- o_x = iv * BV + tl.arange(0, BV)
47
- b_sl = tl.load(logits + o_x, mask=o_x < V, other=float('-inf'))
48
- b_sm = tl.max(b_sl)
49
- m_new = tl.maximum(sm, b_sm)
50
- sd = sd * exp(sm - m_new) + tl.sum(exp(b_sl - m_new))
51
- sm = m_new
52
-
53
- b_tl = tl.load(target_logits + o_x, mask=o_x < V, other=float('-inf'))
54
- b_tm = tl.max(b_tl)
55
- m_new = tl.maximum(tm, b_tm)
56
- td = td * exp(tm - m_new) + tl.sum(exp(b_tl - m_new))
57
- tm = m_new
58
-
59
- b_loss = 0.
60
- for iv in range(0, NV):
61
- o_x = iv * BV + tl.arange(0, BV)
62
- b_sl = tl.load(logits + o_x, mask=o_x < V, other=float('-inf'))
63
- b_tl = tl.load(target_logits + o_x, mask=o_x < V, other=float('-inf'))
64
- b_sp_log = b_sl - sm - log(sd)
65
- b_tp_log = b_tl - tm - log(td)
66
- b_sp = exp(b_sp_log)
67
- b_tp = exp(b_tp_log)
68
- b_kl = tl.where(o_x < V, b_tp * (b_tp_log - b_sp_log), 0)
69
- b_dl = -b_tp + b_sp
70
- b_loss += tl.sum(b_kl)
71
- if reduction == 'batchmean':
72
- b_dl = b_dl / N
73
- tl.store(logits + o_x, b_dl, mask=o_x < V)
74
-
75
- if reduction == 'batchmean':
76
- b_loss = b_loss / N
77
-
78
- tl.store(loss + i_n * s_loss, b_loss)
79
-
80
-
81
- @triton.jit
82
- def elementwise_mul_kernel(
83
- x,
84
- g,
85
- N: tl.constexpr,
86
- B: tl.constexpr,
87
- ):
88
- i_x = tl.program_id(0).to(tl.int64)
89
- o_x = i_x * B + tl.arange(0, B)
90
-
91
- b_g = tl.load(g)
92
- b_x = tl.load(x + o_x, mask=o_x < N)
93
- tl.store(x + o_x, b_x * b_g, mask=o_x < N)
94
-
95
-
96
- def _npu_vocab_block_size(vocab_size: int, num_rows: int) -> int:
97
- return compute_vocab_block_size(vocab_size, num_rows, _KLD_FWD_MEM_MULT)
98
-
99
-
100
- def fused_kl_div_forward_npu(
101
- x: torch.Tensor,
102
- target_x: torch.Tensor,
103
- weight: torch.Tensor,
104
- target_weight: torch.Tensor,
105
- reduction: str = 'batchmean',
106
- accumulate_grad_in_fp32: bool = True,
107
- ):
108
- device = x.device
109
-
110
- N, H, V = *x.shape, weight.shape[0]
111
- BV = _npu_vocab_block_size(V, N)
112
- NC = min(8, triton.cdiv(V, H))
113
- C = min(triton.next_power_of_2(triton.cdiv(N, NC)), ASCEND_MAX_GRID_DIM)
114
- NC = triton.cdiv(N, C)
115
-
116
- grad_dtype = torch.float32 if accumulate_grad_in_fp32 else weight.dtype
117
-
118
- dx = torch.zeros_like(x, device=device)
119
- dw = torch.zeros_like(weight, device=device, dtype=grad_dtype) if weight is not None else None
120
- loss = torch.zeros(N, dtype=torch.float32, device=device)
121
-
122
- for ic in range(NC):
123
- start, end = ic * C, min((ic + 1) * C, N)
124
- c_sx = x[start:end]
125
- c_tx = target_x[start:end]
126
- c_sl = F.linear(c_sx, weight)
127
- c_tl = F.linear(c_tx, target_weight)
128
- if weight is not None and c_sx.dtype != grad_dtype:
129
- c_sx = c_sx.to(dtype=grad_dtype)
130
-
131
- c_loss = loss[start:end]
132
-
133
- kl_div_kernel[(c_sx.shape[0],)](
134
- logits=c_sl,
135
- target_logits=c_tl,
136
- loss=c_loss,
137
- s_logits=c_sl.stride(-2),
138
- s_loss=c_loss.stride(-1),
139
- reduction=reduction,
140
- N=N,
141
- V=V,
142
- BV=BV,
143
- num_warps=STATIC_WARPS,
144
- )
145
-
146
- c_grad = c_sl if c_sl.is_contiguous() else c_sl.contiguous()
147
- dx[start:end] = torch.mm(c_grad, weight)
148
-
149
- if weight is not None:
150
- grad_w = c_grad.t().to(dtype=grad_dtype)
151
- grad_x = c_sx if c_sx.dtype == grad_dtype else c_sx.to(dtype=grad_dtype)
152
- dw.add_(grad_w @ grad_x)
153
-
154
- loss = loss.sum()
155
- if dw is not None:
156
- dw = dw.to(weight)
157
- return loss, dx, dw
158
-
159
-
160
- def fused_kl_div_backward_npu(
161
- do: torch.Tensor,
162
- dx: torch.Tensor,
163
- dw: torch.Tensor,
164
- ):
165
- if torch.ne(do, torch.tensor(1.0, device=do.device)):
166
- N, H = dx.shape
167
- B = compute_elementwise_block_size(N * H, _ELEMENTWISE_MEM_MULT)
168
-
169
- elementwise_mul_kernel[(triton.cdiv(N * H, B),)](
170
- x=dx,
171
- g=do,
172
- N=N*H,
173
- B=B,
174
- num_warps=STATIC_WARPS,
175
- )
176
-
177
- if dw is not None:
178
- V, H = dw.shape
179
- B_dw = compute_elementwise_block_size(V * H, _ELEMENTWISE_MEM_MULT)
180
- elementwise_mul_kernel[(triton.cdiv(V * H, B_dw),)](
181
- x=dw,
182
- g=do,
183
- N=V*H,
184
- B=B_dw,
185
- num_warps=STATIC_WARPS,
186
- )
187
-
188
- return dx, dw
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/fused_linear_cross_entropy.py DELETED
@@ -1,347 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Fused linear cross-entropy kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import torch.nn.functional as F
12
- import triton
13
- import triton.language as tl
14
- from triton.language.math import tanh
15
-
16
- from ....ops.utils.op import exp, log
17
- from ....utils.ascend_ub_manager import (
18
- ASCEND_MAX_GRID_DIM,
19
- compute_elementwise_block_size,
20
- compute_vocab_block_size,
21
- iter_axis_launch_chunks,
22
- )
23
-
24
- # Fused linear CE: logsumexp forward vs gradient kernels along vocab.
25
- _LCE_FWD_MEM_MULT = 8.0
26
- _LCE_BWD_MEM_MULT = 12.0
27
- _ELEMENTWISE_MEM_MULT = 2.5
28
- STATIC_WARPS = 2
29
-
30
-
31
- @triton.heuristics({
32
- 'HAS_SCALE': lambda args: args['scale'] is not None,
33
- })
34
- @triton.jit
35
- def logsumexp_fwd_kernel(
36
- x,
37
- z,
38
- scale,
39
- softcapping: tl.constexpr,
40
- D: tl.constexpr,
41
- B: tl.constexpr,
42
- ROWWISE: tl.constexpr,
43
- HAS_SCALE: tl.constexpr,
44
- HAS_SOFTCAPPING: tl.constexpr,
45
- ):
46
- i_n = tl.program_id(0).to(tl.int64)
47
- if ROWWISE:
48
- row = x + i_n * D
49
- m = float('-inf')
50
- d = 0.0
51
- for start in range(0, D, B):
52
- o = start + tl.arange(0, B)
53
- b_x = tl.load(row + o, mask=o < D, other=float('-inf')).to(tl.float32)
54
- if HAS_SCALE:
55
- b_x = b_x * scale
56
- if HAS_SOFTCAPPING:
57
- b_x = softcapping * tanh(b_x / softcapping)
58
- blk_max = tl.max(b_x, 0)
59
- new_m = tl.maximum(m, blk_max)
60
- d = d * exp(m - new_m) + tl.sum(exp(b_x - new_m), 0)
61
- m = new_m
62
- tl.store(z + i_n, m + log(d))
63
- else:
64
- i_d = tl.program_id(1).to(tl.int64)
65
- o_d = i_d * B + tl.arange(0, B)
66
- m_d = o_d < D
67
-
68
- b_x = tl.load(x + i_n * D + o_d, mask=m_d, other=-float('inf'))
69
- if HAS_SCALE:
70
- b_x = b_x * scale
71
- if HAS_SOFTCAPPING:
72
- b_x = softcapping * tanh(b_x / softcapping)
73
- b_m = tl.max(b_x, 0)
74
- b_z = log(tl.sum(exp(b_x - b_m), 0)) + b_m
75
- tl.store(z + i_n * tl.cdiv(D, B) + i_d, b_z)
76
-
77
-
78
- @triton.jit
79
- def cross_entropy_kernel(
80
- logits,
81
- lse,
82
- target,
83
- loss,
84
- total,
85
- ignore_index,
86
- label_smoothing: tl.constexpr,
87
- logit_scale: tl.constexpr,
88
- logit_softcapping: tl.constexpr,
89
- HAS_SOFTCAPPING: tl.constexpr,
90
- reduction: tl.constexpr,
91
- V: tl.constexpr,
92
- BV: tl.constexpr,
93
- ):
94
- i_n = tl.program_id(0).to(tl.int64)
95
- NV = tl.cdiv(V, BV)
96
-
97
- b_y = tl.load(target + i_n)
98
- logits += i_n * V
99
-
100
- if b_y == ignore_index:
101
- for i in range(0, V, BV):
102
- o_v = i + tl.arange(0, BV)
103
- tl.store(logits + o_v, 0.0, mask=o_v < V)
104
- return
105
-
106
- b_l = tl.load(logits + b_y).to(tl.float32) * logit_scale
107
- if HAS_SOFTCAPPING:
108
- b_t_y = tanh(b_l / logit_softcapping)
109
- b_l = logit_softcapping * b_t_y
110
- b_softcap_deriv_y = 1.0 - b_t_y * b_t_y
111
- b_lse = tl.load(lse + i_n)
112
-
113
- b_loss = b_lse - b_l
114
- b_z = 0.0
115
- eps = label_smoothing / V
116
-
117
- for iv in range(0, NV):
118
- o_v = iv * BV + tl.arange(0, BV)
119
- b_logits = tl.load(logits + o_v, mask=o_v < V, other=float('-inf')).to(tl.float32) * logit_scale
120
- if HAS_SOFTCAPPING:
121
- b_t = tanh(b_logits / logit_softcapping)
122
- b_capped = logit_softcapping * b_t
123
- else:
124
- b_capped = b_logits
125
- if label_smoothing > 0:
126
- b_z += tl.sum(tl.where(o_v < V, -eps * b_capped, 0.0))
127
- b_p = (exp(b_capped - b_lse) - eps) * logit_scale
128
- if HAS_SOFTCAPPING:
129
- b_p = b_p * (1.0 - b_t * b_t)
130
- if reduction == "mean":
131
- b_p = b_p / total
132
- tl.store(logits + o_v, b_p, mask=o_v < V)
133
-
134
- if label_smoothing > 0:
135
- b_loss = b_loss * (1 - label_smoothing) + (b_z + label_smoothing * b_lse)
136
-
137
- b_l = tl.load(logits + b_y)
138
-
139
- if HAS_SOFTCAPPING:
140
- b_sc_factor = b_softcap_deriv_y
141
- else:
142
- b_sc_factor = 1.0
143
-
144
- if reduction == 'mean':
145
- b_loss = b_loss / total
146
- b_l += (label_smoothing - 1) / total * logit_scale * b_sc_factor
147
- else:
148
- b_l += (label_smoothing - 1) * logit_scale * b_sc_factor
149
-
150
- tl.store(loss + i_n, b_loss)
151
- tl.store(logits + b_y, b_l)
152
-
153
-
154
- @triton.jit
155
- def elementwise_mul_kernel(
156
- x,
157
- g,
158
- N: tl.constexpr,
159
- B: tl.constexpr,
160
- ):
161
- i_x = tl.program_id(0).to(tl.int64)
162
- o_x = i_x * B + tl.arange(0, B)
163
-
164
- b_g = tl.load(g)
165
- b_x = tl.load(x + o_x, mask=o_x < N)
166
- tl.store(x + o_x, b_x * b_g, mask=o_x < N)
167
-
168
-
169
- def _npu_vocab_block_size(vocab_size: int, num_rows: int, is_backward: bool = True) -> int:
170
- memory_multiplier = _LCE_BWD_MEM_MULT if is_backward else _LCE_FWD_MEM_MULT
171
- return compute_vocab_block_size(vocab_size, num_rows, memory_multiplier)
172
-
173
-
174
- def logsumexp_fwd_npu(
175
- x,
176
- scale: float | None = None,
177
- softcapping: float | None = None,
178
- dtype: torch.dtype | None = None,
179
- ):
180
- shape = x.shape
181
- x = x.view(-1, shape[-1])
182
- N, D = x.shape
183
- B = _npu_vocab_block_size(D, N, is_backward=False)
184
- has_softcapping = softcapping is not None
185
- softcap_val = float(softcapping) if has_softcapping else 0.0
186
-
187
- z = x.new_empty(N, dtype=torch.float)
188
- for row_off, row_len in iter_axis_launch_chunks(N, 1, max_grid=ASCEND_MAX_GRID_DIM):
189
- logsumexp_fwd_kernel[(row_len,)](
190
- x=x[row_off:row_off + row_len],
191
- z=z[row_off:row_off + row_len],
192
- scale=scale,
193
- softcapping=softcap_val,
194
- D=D,
195
- B=B,
196
- ROWWISE=True,
197
- HAS_SOFTCAPPING=has_softcapping,
198
- )
199
- z = z.view(*shape[:-1])
200
- if dtype is not None and dtype != torch.float:
201
- z = z.to(dtype)
202
- return z
203
-
204
-
205
- def fused_linear_cross_entropy_forward_npu(
206
- x: torch.Tensor,
207
- target: torch.LongTensor,
208
- weight: torch.Tensor,
209
- bias: torch.Tensor = None,
210
- ignore_index: int = -100,
211
- label_smoothing: float = 0.0,
212
- logit_scale: float = 1.0,
213
- logit_softcapping: float = None,
214
- num_chunks: int = 8,
215
- reduction: str = "mean",
216
- use_l2warp: bool = False,
217
- l2_penalty_factor: float = 1e-4,
218
- accumulate_grad_in_fp32: bool = True,
219
- ):
220
- device = x.device
221
- N, H, V = *x.shape, weight.shape[0]
222
- BV = _npu_vocab_block_size(V, N)
223
- has_softcapping = logit_softcapping is not None
224
- softcap_val = float(logit_softcapping) if has_softcapping else 0.0
225
- NC = min(num_chunks, triton.cdiv(V, H))
226
- C = min(triton.next_power_of_2(triton.cdiv(N, NC)), ASCEND_MAX_GRID_DIM)
227
- NC = triton.cdiv(N, C)
228
-
229
- dx = torch.zeros_like(x, device=device)
230
- grad_dtype = torch.float32 if accumulate_grad_in_fp32 else weight.dtype
231
- bias_grad_dtype = None
232
- if bias is not None:
233
- bias_grad_dtype = torch.float32 if accumulate_grad_in_fp32 else bias.dtype
234
-
235
- dw = torch.zeros_like(weight, device=device, dtype=grad_dtype) if weight is not None else None
236
- db = torch.zeros_like(bias, device=device, dtype=bias_grad_dtype) if bias is not None else None
237
- loss = torch.zeros(N, device=device, dtype=torch.float)
238
-
239
- total = target.ne(ignore_index).sum().item()
240
-
241
- for ic in range(NC):
242
- start, end = ic * C, min((ic + 1) * C, N)
243
- c_x = x[start:end]
244
- c_logits = F.linear(c_x, weight, bias)
245
- if weight is not None and c_x.dtype != grad_dtype:
246
- c_x = c_x.to(dtype=grad_dtype)
247
- c_target = target[start:end]
248
- c_lse = logsumexp_fwd_npu(c_logits, scale=logit_scale, softcapping=logit_softcapping, dtype=torch.float)
249
-
250
- c_loss = loss[start:end]
251
- if use_l2warp:
252
- c_maxx, c_ids = torch.max(c_logits, -1, keepdim=True)
253
-
254
- cross_entropy_kernel[(c_logits.shape[0],)](
255
- logits=c_logits,
256
- lse=c_lse,
257
- target=c_target,
258
- loss=c_loss,
259
- total=total,
260
- ignore_index=ignore_index,
261
- label_smoothing=label_smoothing,
262
- logit_scale=logit_scale,
263
- logit_softcapping=softcap_val,
264
- HAS_SOFTCAPPING=has_softcapping,
265
- reduction=reduction,
266
- V=V,
267
- BV=BV,
268
- num_warps=STATIC_WARPS,
269
- )
270
- if use_l2warp:
271
- g_logits_l2 = torch.zeros_like(c_logits)
272
- l2_factor = l2_penalty_factor / N
273
- penalty_grad = c_maxx * l2_factor
274
- g_logits_l2.scatter_(-1, c_ids, penalty_grad)
275
-
276
- if weight is not None:
277
- torch.addmm(
278
- input=dw,
279
- mat1=g_logits_l2.t().to(dtype=grad_dtype),
280
- mat2=c_x,
281
- out=dw,
282
- )
283
- if bias is not None:
284
- torch.add(input=db, other=g_logits_l2.sum(0, dtype=bias_grad_dtype), out=db)
285
- dx_l2_contribution = torch.mm(g_logits_l2, weight)
286
- else:
287
- dx_l2_contribution = 0.0
288
-
289
- c_grad = c_logits if c_logits.is_contiguous() else c_logits.contiguous()
290
- dx[start:end] = torch.mm(c_grad, weight) + dx_l2_contribution
291
-
292
- if weight is not None:
293
- grad_w = c_grad.t().to(dtype=grad_dtype)
294
- grad_x = c_x if c_x.dtype == grad_dtype else c_x.to(dtype=grad_dtype)
295
- dw.add_(grad_w @ grad_x)
296
-
297
- if bias is not None:
298
- torch.add(input=db, other=c_logits.sum(0, dtype=bias_grad_dtype), out=db)
299
-
300
- loss = loss.sum()
301
- if dw is not None:
302
- dw = dw.to(weight)
303
- if db is not None:
304
- db = db.to(bias)
305
- return loss, dx, dw, db
306
-
307
-
308
- def fused_linear_cross_entropy_backward_npu(
309
- do: torch.Tensor,
310
- dx: torch.Tensor,
311
- dw: torch.Tensor,
312
- db: torch.Tensor,
313
- ):
314
- if torch.ne(do, torch.tensor(1.0, device=do.device)):
315
- N, H = dx.shape
316
- B = compute_elementwise_block_size(N * H, _ELEMENTWISE_MEM_MULT)
317
-
318
- elementwise_mul_kernel[(triton.cdiv(N * H, B),)](
319
- x=dx,
320
- g=do,
321
- N=N*H,
322
- B=B,
323
- num_warps=STATIC_WARPS,
324
- )
325
-
326
- if dw is not None:
327
- V, H = dw.shape
328
- B_dw = compute_elementwise_block_size(V * H, _ELEMENTWISE_MEM_MULT)
329
- elementwise_mul_kernel[(triton.cdiv(V * H, B_dw),)](
330
- x=dw,
331
- g=do,
332
- N=V*H,
333
- B=B_dw,
334
- num_warps=STATIC_WARPS,
335
- )
336
-
337
- if db is not None:
338
- V = db.shape[0]
339
- B_db = compute_elementwise_block_size(V, _ELEMENTWISE_MEM_MULT)
340
- elementwise_mul_kernel[(triton.cdiv(V, B_db),)](
341
- x=db,
342
- g=do,
343
- N=V,
344
- B=B_db,
345
- num_warps=STATIC_WARPS,
346
- )
347
- return dx, dw, db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/grpo.py DELETED
@@ -1,266 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """GRPO loss kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import triton
12
- import triton.language as tl
13
-
14
- from ....ops.utils.op import exp, log
15
- from ....utils import input_guard
16
- from ....utils.ascend_ub_manager import ASCEND_MAX_GRID_DIM, compute_vocab_block_size, iter_axis_launch_chunks
17
-
18
- # GRPO: use conservative multiplier covering both fwd softmax and bwd grad paths.
19
- _GRPO_MEM_MULT = 8.0
20
- STATIC_WARPS = 2
21
-
22
-
23
- def _npu_vocab_block_size(vocab_size: int, num_rows: int) -> int:
24
- return compute_vocab_block_size(vocab_size, num_rows, _GRPO_MEM_MULT)
25
-
26
-
27
- @triton.jit
28
- def grpo_fwd_kernel(
29
- logits_ptr,
30
- ref_logp_ptr,
31
- input_ids_ptr,
32
- advantages_ptr,
33
- completion_mask_ptr,
34
- loss_ptr,
35
- lse_ptr,
36
- beta,
37
- save_kl: tl.constexpr,
38
- B,
39
- M,
40
- N,
41
- L,
42
- start_idx,
43
- BLOCK_SIZE: tl.constexpr,
44
- ROW_OFFSET: tl.constexpr,
45
- ):
46
- row_idx = tl.program_id(0) + ROW_OFFSET
47
-
48
- off_b = row_idx // L
49
- N = tl.cast(N, tl.int64)
50
-
51
- loss_ptr += row_idx
52
-
53
- completion_mask_ptr += row_idx
54
- not_skip = tl.load(completion_mask_ptr).to(tl.int1)
55
- if not_skip == 1:
56
- ref_logp_ptr += row_idx
57
- lse_ptr += row_idx
58
- advantages_ptr += off_b
59
- logits_ptr += N * (row_idx + off_b)
60
- input_ids_ptr += row_idx + (off_b + 1) * start_idx
61
- base_cols = tl.arange(0, BLOCK_SIZE)
62
-
63
- m_i = -float("inf")
64
- l_i = 0.0
65
- for start_n in tl.range(0, N, BLOCK_SIZE):
66
- cols = start_n + base_cols
67
- mask = cols < N
68
- logits = tl.load(logits_ptr + cols, mask=mask, other=-float('inf')).to(tl.float32)
69
- m_ij = tl.max(logits)
70
- new_m_i = tl.maximum(m_i, m_ij)
71
- l_i = l_i * exp(m_i - new_m_i) + tl.sum(exp(logits - new_m_i))
72
- m_i = new_m_i
73
- lse = log(l_i) + m_i
74
-
75
- idx = tl.load(input_ids_ptr)
76
- x = tl.load(logits_ptr + idx).to(tl.float32)
77
- advantage = tl.load(advantages_ptr).to(tl.float32)
78
- ref_logp = tl.load(ref_logp_ptr)
79
- logp = x - lse
80
- diff = ref_logp - logp
81
- kl = exp(diff) - diff - 1
82
- loss = kl * beta - advantage
83
-
84
- tl.store(loss_ptr, loss.to(loss_ptr.dtype.element_ty))
85
- tl.store(lse_ptr, lse.to(lse_ptr.dtype.element_ty))
86
- if save_kl:
87
- tl.store(loss_ptr + M, kl.to(loss_ptr.dtype.element_ty))
88
- else:
89
- tl.store(loss_ptr, 0.0)
90
- if save_kl:
91
- tl.store(loss_ptr + M, 0.0)
92
-
93
-
94
- @triton.jit
95
- def grpo_bwd_kernel(
96
- dloss_ptr,
97
- dlogits_ptr,
98
- logits_ptr,
99
- ref_logp_ptr,
100
- input_ids_ptr,
101
- advantages_ptr,
102
- completion_mask_ptr,
103
- lse_ptr,
104
- beta,
105
- B,
106
- N,
107
- L,
108
- start_idx,
109
- BLOCK_SIZE: tl.constexpr,
110
- ROW_OFFSET: tl.constexpr,
111
- ):
112
- row_idx = tl.program_id(0) + ROW_OFFSET
113
- off_b = row_idx // L
114
-
115
- N = tl.cast(N, tl.int64)
116
-
117
- dlogits_ptr += N * (row_idx + off_b)
118
- base_cols = tl.arange(0, BLOCK_SIZE)
119
- completion_mask_ptr += row_idx
120
- not_skip = tl.load(completion_mask_ptr).to(tl.int1)
121
-
122
- if not_skip == 1:
123
- lse_ptr += row_idx
124
- dloss_ptr += row_idx
125
- advantages_ptr += off_b
126
- ref_logp_ptr += row_idx
127
- logits_ptr += N * (row_idx + off_b)
128
- input_ids_ptr += row_idx + (off_b + 1) * start_idx
129
- dloss = tl.load(dloss_ptr).to(tl.float32)
130
- lse = tl.load(lse_ptr).to(tl.float32)
131
- idx = tl.load(input_ids_ptr)
132
- x = tl.load(logits_ptr + idx).to(tl.float32)
133
- advantage = tl.load(advantages_ptr).to(tl.float32)
134
- ref_logp = tl.load(ref_logp_ptr)
135
- logp = x - lse
136
-
137
- dlogp = (beta * (-1.0 * exp(ref_logp - logp) + 1) - advantage) * dloss
138
-
139
- for start_n in tl.range(0, N, BLOCK_SIZE):
140
- cols = start_n + base_cols
141
- mask = cols < N
142
- logits = tl.load(logits_ptr + cols, mask=mask, other=-float('inf')).to(tl.float32)
143
- probs = exp(logits - lse)
144
- dlogits = tl.where(cols == idx, 1 - probs, -probs) * dlogp
145
-
146
- tl.store(dlogits_ptr + cols, dlogits.to(dlogits_ptr.dtype.element_ty), mask=mask)
147
- else:
148
- dlogits = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
149
- for start_n in tl.range(0, N, BLOCK_SIZE):
150
- cols = start_n + base_cols
151
- mask = cols < N
152
-
153
- tl.store(dlogits_ptr + cols, dlogits.to(dlogits_ptr.dtype.element_ty), mask=mask)
154
-
155
-
156
- class GrpoLossNPU(torch.autograd.Function):
157
-
158
- @input_guard
159
- @staticmethod
160
- def forward(ctx, logits, ref_logp, input_ids, advantages, beta, completion_mask, save_kl, inplace=True):
161
- ctx.input_shape = logits.shape
162
- B, L_ADD_1, N = ctx.input_shape
163
- L = L_ADD_1 - 1
164
- M = B * L
165
- input_ids_start_index = input_ids.size(1) - L
166
- block_size = _npu_vocab_block_size(N, M)
167
-
168
- if not save_kl:
169
- loss = torch.empty(B, L, device=logits.device, dtype=torch.float32)
170
- else:
171
- loss = torch.empty(B * 2, L, device=logits.device, dtype=torch.float32)
172
-
173
- lse = torch.empty(B, L, device=logits.device, dtype=torch.float32)
174
-
175
- if completion_mask is None:
176
- completion_mask = torch.ones(B, L, device=logits.device, dtype=torch.int32)
177
- else:
178
- loss[:B].masked_fill_(completion_mask.logical_not(), 0.0)
179
-
180
- for row_off, row_len in iter_axis_launch_chunks(M, 1, max_grid=ASCEND_MAX_GRID_DIM):
181
- grpo_fwd_kernel[(row_len,)](
182
- logits_ptr=logits,
183
- ref_logp_ptr=ref_logp,
184
- input_ids_ptr=input_ids,
185
- advantages_ptr=advantages,
186
- completion_mask_ptr=completion_mask,
187
- loss_ptr=loss,
188
- lse_ptr=lse,
189
- beta=beta,
190
- save_kl=save_kl,
191
- B=B,
192
- M=M,
193
- N=N,
194
- L=L,
195
- start_idx=input_ids_start_index,
196
- BLOCK_SIZE=block_size,
197
- ROW_OFFSET=row_off,
198
- num_warps=STATIC_WARPS,
199
- )
200
- ctx.beta = beta
201
- ctx.save_for_backward(lse, logits, input_ids, advantages, completion_mask)
202
- ctx.ref_logp = ref_logp
203
- ctx.inplace = inplace
204
- ctx.block_size = block_size
205
- return loss
206
-
207
- @input_guard
208
- @staticmethod
209
- def backward(ctx, dloss):
210
- lse, logits, input_ids, advantages, completion_mask = ctx.saved_tensors
211
- inplace = ctx.inplace
212
- B, L_ADD_1, N = ctx.input_shape
213
- L = L_ADD_1 - 1
214
- M = B * L
215
- block_size = ctx.block_size
216
-
217
- input_ids_start_index = input_ids.size(1) - L
218
-
219
- dlogits = logits if inplace else torch.empty_like(logits)
220
-
221
- for row_off, row_len in iter_axis_launch_chunks(M, 1, max_grid=ASCEND_MAX_GRID_DIM):
222
- grpo_bwd_kernel[(row_len,)](
223
- dloss_ptr=dloss,
224
- dlogits_ptr=dlogits,
225
- logits_ptr=logits,
226
- ref_logp_ptr=ctx.ref_logp,
227
- input_ids_ptr=input_ids,
228
- advantages_ptr=advantages,
229
- completion_mask_ptr=completion_mask,
230
- lse_ptr=lse,
231
- beta=ctx.beta,
232
- B=B,
233
- N=N,
234
- L=L,
235
- BLOCK_SIZE=block_size,
236
- start_idx=input_ids_start_index,
237
- ROW_OFFSET=row_off,
238
- num_warps=STATIC_WARPS,
239
- )
240
- dlogits[:, -1, :].fill_(0.0)
241
- return dlogits.view(*ctx.input_shape), None, None, None, None, None, None, None
242
-
243
-
244
- def fused_grpo_loss_npu(
245
- logits,
246
- ref_logp,
247
- input_ids,
248
- advantages,
249
- beta=0.1,
250
- completion_mask=None,
251
- save_kl=False,
252
- inplace=False,
253
- ) -> torch.Tensor:
254
- out = GrpoLossNPU.apply(
255
- logits,
256
- ref_logp,
257
- input_ids,
258
- advantages,
259
- beta,
260
- completion_mask,
261
- save_kl,
262
- inplace,
263
- )
264
- if not save_kl:
265
- return out
266
- return out.chunk(2, axis=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/layernorm.py DELETED
@@ -1,355 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """LayerNorm / RMSNorm / GroupNorm kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import triton
12
- import triton.language as tl
13
-
14
- from ....utils import get_multiprocessor_count
15
- from ....utils.ascend_ub_manager import ASCEND_MAX_GRID_DIM, compute_ub_block_size, iter_axis_launch_chunks
16
-
17
- # Peak live fp32 vectors in row-wise kernel1 (see Liger Ascend layer_norm).
18
- _FWD_MEM_MULT = 6.0
19
- _BWD_MEM_MULT = 8.0
20
- _UB_SAFETY_MARGIN = 0.85
21
- # Legacy byte cap when UB capacity cannot be detected (65536 // fp32).
22
- _FALLBACK_MAX_BD = 65536 // 4
23
-
24
-
25
- def _get_layer_norm_bd(D: int, is_forward: bool) -> int:
26
- """Return power-of-2 block size for feature dim D under UB constraints."""
27
- memory_multiplier = _FWD_MEM_MULT if is_forward else _BWD_MEM_MULT
28
- return compute_ub_block_size(
29
- D,
30
- memory_multiplier,
31
- safety_margin=_UB_SAFETY_MARGIN,
32
- fallback=_FALLBACK_MAX_BD,
33
- desired=triton.next_power_of_2(D),
34
- )
35
-
36
-
37
- def _layer_norm_bwd_launch_config(T: int, G: int, device_index: int) -> tuple[int, int, int]:
38
- """Return (NS, BS, GS) capped under Ascend grid limit."""
39
- NS = min(triton.cdiv(get_multiprocessor_count(device_index), G), T // G) * G
40
- NS = min(NS, ASCEND_MAX_GRID_DIM)
41
- BS = triton.cdiv(T, NS) if NS > 0 else T
42
- GS = NS // G if G > 0 else NS
43
- return NS, BS, GS
44
-
45
-
46
- @triton.jit
47
- def layer_norm_fwd_kernel1(
48
- x,
49
- y,
50
- w,
51
- b,
52
- res,
53
- res_out,
54
- mean,
55
- rstd,
56
- eps,
57
- G: tl.constexpr,
58
- D: tl.constexpr,
59
- BD: tl.constexpr,
60
- IS_RMS_NORM: tl.constexpr,
61
- HAS_RESIDUAL: tl.constexpr,
62
- STORE_RESIDUAL_OUT: tl.constexpr,
63
- HAS_WEIGHT: tl.constexpr,
64
- HAS_BIAS: tl.constexpr,
65
- ):
66
- i_t = tl.program_id(0)
67
- i_g = i_t % G
68
-
69
- x += i_t * D
70
- y += i_t * D
71
- if HAS_RESIDUAL:
72
- res += i_t * D
73
- if STORE_RESIDUAL_OUT:
74
- res_out += i_t * D
75
-
76
- o_d = tl.arange(0, BD)
77
- m_d = o_d < D
78
- b_x = tl.load(x + o_d, mask=m_d, other=0.0).to(tl.float32)
79
- if HAS_RESIDUAL:
80
- b_x += tl.load(res + o_d, mask=m_d, other=0.0).to(tl.float32)
81
- if STORE_RESIDUAL_OUT:
82
- tl.store(res_out + o_d, b_x, mask=m_d)
83
- if not IS_RMS_NORM:
84
- b_mean = tl.sum(b_x, axis=0) / D
85
- tl.store(mean + i_t, b_mean)
86
- b_xbar = tl.where(m_d, b_x - b_mean, 0.0)
87
- b_var = tl.sum(b_xbar * b_xbar, axis=0) / D
88
- else:
89
- b_xbar = tl.where(m_d, b_x, 0.0)
90
- b_var = tl.sum(b_xbar * b_xbar, axis=0) / D
91
- b_rstd = 1 / tl.sqrt(b_var + eps)
92
- tl.store(rstd + i_t, b_rstd)
93
-
94
- if HAS_WEIGHT:
95
- b_w = tl.load(w + i_g * D + o_d, mask=m_d).to(tl.float32)
96
- if HAS_BIAS:
97
- b_b = tl.load(b + i_g * D + o_d, mask=m_d).to(tl.float32)
98
- b_x_hat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
99
- b_y = b_x_hat * b_w if HAS_WEIGHT else b_x_hat
100
- if HAS_BIAS:
101
- b_y = b_y + b_b
102
-
103
- tl.store(y + o_d, b_y, mask=m_d)
104
-
105
-
106
- @triton.heuristics({
107
- 'RECOMPUTE_OUTPUT': lambda args: args['y'] is not None,
108
- })
109
- @triton.jit
110
- def layer_norm_bwd_kernel1(
111
- x,
112
- w,
113
- b,
114
- y,
115
- dy,
116
- dx,
117
- dw,
118
- db,
119
- dres,
120
- dres_in,
121
- mean,
122
- rstd,
123
- T,
124
- G: tl.constexpr,
125
- D: tl.constexpr,
126
- BS: tl.constexpr,
127
- BD: tl.constexpr,
128
- GS: tl.constexpr,
129
- IS_RMS_NORM: tl.constexpr,
130
- HAS_DRESIDUAL: tl.constexpr,
131
- STORE_DRESIDUAL: tl.constexpr,
132
- HAS_WEIGHT: tl.constexpr,
133
- HAS_BIAS: tl.constexpr,
134
- RECOMPUTE_OUTPUT: tl.constexpr,
135
- ):
136
- i_s = tl.program_id(0)
137
- i_g, i_sg = i_s // GS, i_s % GS
138
-
139
- o_d = tl.arange(0, BD)
140
- mask = o_d < D
141
-
142
- if HAS_WEIGHT:
143
- b_w = tl.load(w + i_g * D + o_d, mask=mask).to(tl.float32)
144
- b_dw = tl.zeros((BD,), dtype=tl.float32)
145
- if RECOMPUTE_OUTPUT and HAS_BIAS:
146
- b_b = tl.load(b + i_g * D + o_d, mask=mask, other=0.0).to(tl.float32)
147
- if HAS_BIAS:
148
- b_db = tl.zeros((BD,), dtype=tl.float32)
149
-
150
- for i_t in range(i_sg * BS * G + i_g, min((i_sg * BS + BS) * G + i_g, T), G):
151
- b_x = tl.load(x + i_t * D + o_d, mask=mask, other=0).to(tl.float32)
152
- b_dy = tl.load(dy + i_t * D + o_d, mask=mask, other=0).to(tl.float32)
153
-
154
- if not IS_RMS_NORM:
155
- b_mean = tl.load(mean + i_t)
156
- b_rstd = tl.load(rstd + i_t)
157
- b_xhat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
158
- b_xhat = tl.where(mask, b_xhat, 0.0)
159
- if RECOMPUTE_OUTPUT:
160
- b_y = b_xhat * b_w if HAS_WEIGHT else b_xhat
161
- if HAS_BIAS:
162
- b_y = b_y + b_b
163
- tl.store(y + i_t * D + o_d, b_y, mask=mask)
164
- b_wdy = b_dy
165
- if HAS_WEIGHT:
166
- b_wdy = b_dy * b_w
167
- b_dw += b_dy * b_xhat
168
- if HAS_BIAS:
169
- b_db += b_dy
170
- if not IS_RMS_NORM:
171
- b_c1 = tl.sum(b_xhat * b_wdy, axis=0) / D
172
- b_c2 = tl.sum(b_wdy, axis=0) / D
173
- b_dx = (b_wdy - (b_xhat * b_c1 + b_c2)) * b_rstd
174
- else:
175
- b_c1 = tl.sum(b_xhat * b_wdy, axis=0) / D
176
- b_dx = (b_wdy - b_xhat * b_c1) * b_rstd
177
- if HAS_DRESIDUAL:
178
- b_dres = tl.load(dres + i_t * D + o_d, mask=mask, other=0).to(tl.float32)
179
- b_dx += b_dres
180
- b_dx = tl.cast(b_dx, dtype=dx.dtype.element_ty, fp_downcast_rounding='rtne')
181
- if STORE_DRESIDUAL:
182
- tl.store(dres_in + i_t * D + o_d, b_dx, mask=mask)
183
- tl.store(dx + i_t * D + o_d, b_dx, mask=mask)
184
-
185
- if HAS_WEIGHT:
186
- tl.store(dw + i_s * D + o_d, b_dw, mask=mask)
187
- if HAS_BIAS:
188
- tl.store(db + i_s * D + o_d, b_db, mask=mask)
189
-
190
-
191
- def _launch_layer_norm_fwd_kernel1(
192
- x: torch.Tensor,
193
- y: torch.Tensor,
194
- weight: torch.Tensor,
195
- bias: torch.Tensor,
196
- residual: torch.Tensor,
197
- res_out: torch.Tensor,
198
- mean: torch.Tensor,
199
- rstd: torch.Tensor,
200
- eps: float,
201
- G: int,
202
- D: int,
203
- BD: int,
204
- is_rms_norm: bool,
205
- ):
206
- chunk_T = x.shape[0]
207
- layer_norm_fwd_kernel1[(chunk_T,)](
208
- x,
209
- y,
210
- weight,
211
- bias,
212
- residual,
213
- res_out,
214
- mean,
215
- rstd,
216
- eps,
217
- G=G,
218
- D=D,
219
- BD=BD,
220
- IS_RMS_NORM=is_rms_norm,
221
- HAS_RESIDUAL=residual is not None,
222
- STORE_RESIDUAL_OUT=res_out is not None,
223
- HAS_WEIGHT=weight is not None,
224
- HAS_BIAS=bias is not None,
225
- )
226
-
227
-
228
- def layer_norm_fwd_npu(
229
- x: torch.Tensor,
230
- weight: torch.Tensor,
231
- bias: torch.Tensor,
232
- eps: float = 1e-5,
233
- residual: torch.Tensor = None,
234
- out_dtype: torch.dtype = None,
235
- residual_dtype: torch.dtype = None,
236
- is_rms_norm: bool = False,
237
- num_groups: int = 1,
238
- ):
239
- if residual is not None:
240
- residual_dtype = residual.dtype
241
- T, D, G = *x.shape, num_groups
242
- if residual is not None:
243
- assert residual.shape == (T, D)
244
- if weight is not None:
245
- assert weight.shape == (G * D,)
246
- if bias is not None:
247
- assert bias.shape == (G * D,)
248
-
249
- y = torch.empty_like(x, dtype=x.dtype if out_dtype is None else out_dtype)
250
- if residual is not None or (residual_dtype is not None and residual_dtype != x.dtype):
251
- res_out = torch.empty(T, D, device=x.device, dtype=residual_dtype)
252
- else:
253
- res_out = None
254
- mean = torch.empty((T,), dtype=torch.float, device=x.device) if not is_rms_norm else None
255
- rstd = torch.empty((T,), dtype=torch.float, device=x.device)
256
-
257
- BD = _get_layer_norm_bd(D, is_forward=True)
258
- if D > BD:
259
- raise RuntimeError(
260
- f"LayerNorm feature dim {D} exceeds UB-safe block size {BD}. "
261
- "Column-tiled kernels are not yet implemented for this size."
262
- )
263
-
264
- # Ascend: use row-wise kernel1 (no make_block_ptr) for all feature dims.
265
- # Split along rows when T exceeds the Ascend grid limit.
266
- for row_start, row_len in iter_axis_launch_chunks(T, 1, max_grid=ASCEND_MAX_GRID_DIM):
267
- row_end = row_start + row_len
268
- _launch_layer_norm_fwd_kernel1(
269
- x[row_start:row_end],
270
- y[row_start:row_end],
271
- weight,
272
- bias,
273
- None if residual is None else residual[row_start:row_end],
274
- None if res_out is None else res_out[row_start:row_end],
275
- None if mean is None else mean[row_start:row_end],
276
- rstd[row_start:row_end],
277
- eps,
278
- G,
279
- D,
280
- BD,
281
- is_rms_norm,
282
- )
283
- return y, mean, rstd, res_out if res_out is not None else x
284
-
285
-
286
- def layer_norm_bwd_npu(
287
- dy: torch.Tensor,
288
- x: torch.Tensor,
289
- weight: torch.Tensor,
290
- bias: torch.Tensor,
291
- mean: torch.Tensor = None,
292
- rstd: torch.Tensor = None,
293
- dres: torch.Tensor = None,
294
- has_residual: bool = False,
295
- is_rms_norm: bool = False,
296
- x_dtype: torch.dtype = None,
297
- recompute_output: bool = False,
298
- num_groups: int = 1,
299
- ):
300
- T, D, G = *x.shape, num_groups
301
- assert dy.shape == (T, D)
302
- if dres is not None:
303
- assert dres.shape == (T, D)
304
- if weight is not None:
305
- assert weight.shape == (G * D,)
306
- if bias is not None:
307
- assert bias.shape == (G * D,)
308
-
309
- dx = torch.empty_like(x) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
310
- dres_in = torch.empty_like(x) if has_residual and dx.dtype != x.dtype else None
311
- y = torch.empty(T, D, dtype=dy.dtype, device=dy.device) if recompute_output else None
312
-
313
- BD = _get_layer_norm_bd(D, is_forward=False)
314
- if D > BD:
315
- raise RuntimeError(
316
- f"LayerNorm feature dim {D} exceeds UB-safe block size {BD}. "
317
- "Column-tiled kernels are not yet implemented for this size."
318
- )
319
-
320
- NS, BS, GS = _layer_norm_bwd_launch_config(T, G, x.device.index)
321
-
322
- dw = torch.empty((NS, D), dtype=torch.float, device=weight.device) if weight is not None else None
323
- db = torch.empty((NS, D), dtype=torch.float, device=bias.device) if bias is not None else None
324
- grid = (NS,)
325
-
326
- layer_norm_bwd_kernel1[grid](
327
- x,
328
- weight,
329
- bias,
330
- y,
331
- dy,
332
- dx,
333
- dw,
334
- db,
335
- dres,
336
- dres_in,
337
- mean,
338
- rstd,
339
- T=T,
340
- G=G,
341
- D=D,
342
- BS=BS,
343
- BD=BD,
344
- GS=GS,
345
- IS_RMS_NORM=is_rms_norm,
346
- HAS_DRESIDUAL=dres is not None,
347
- STORE_DRESIDUAL=dres_in is not None,
348
- HAS_WEIGHT=weight is not None,
349
- HAS_BIAS=bias is not None,
350
- )
351
- dw = dw.view(G, -1, D).sum(1).to(weight).view_as(weight) if weight is not None else None
352
- db = db.view(G, -1, D).sum(1).to(bias).view_as(bias) if bias is not None else None
353
- if has_residual and dx.dtype == x.dtype:
354
- dres_in = dx
355
- return (dx, dw, db, dres_in) if not recompute_output else (dx, dw, db, dres_in, y)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/backends/triton_ascend/rotary.py DELETED
@@ -1,211 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Rotary embedding kernels adapted for triton-ascend on Huawei NPU."""
9
-
10
- import torch
11
- import triton
12
- import triton.language as tl
13
-
14
- from ....ops.utils import prepare_chunk_indices
15
- from ....utils import autotune_cache_kwargs, get_multiprocessor_count
16
- from ....utils.ascend_ub_manager import (
17
- ASCEND_MAX_GRID_DIM,
18
- compute_grid_limited_tile_size,
19
- compute_row_tile_block_size,
20
- max_grid_axis_chunks,
21
- )
22
-
23
- # Peak live fp32 tiles in rotary kernel: cos, sin, x0, x1, o0, o1.
24
- _ROTARY_MEM_MULT = 6.0
25
- _ROTARY_SAFETY_MARGIN = 0.90
26
-
27
- # Ascend vector UB is small; large num_warps / stages explodes compile-time UB (see bishengir ub overflow).
28
- NUM_WARPS_AUTOTUNE = [2, 4]
29
- NUM_STAGES_AUTOTUNE = [1, 2]
30
-
31
-
32
- @triton.autotune(
33
- configs=[
34
- triton.Config({}, num_warps=num_warps, num_stages=num_stages)
35
- for num_warps in NUM_WARPS_AUTOTUNE
36
- for num_stages in NUM_STAGES_AUTOTUNE
37
- ],
38
- key=['B', 'H', 'D', 'INTERLEAVED'],
39
- **autotune_cache_kwargs,
40
- )
41
- @triton.jit(do_not_specialize=['T'])
42
- def rotary_embedding_kernel(
43
- x,
44
- cos,
45
- sin,
46
- y,
47
- cu_seqlens,
48
- chunk_indices,
49
- seq_offsets,
50
- T,
51
- B: tl.constexpr,
52
- H: tl.constexpr,
53
- D: tl.constexpr,
54
- R: tl.constexpr,
55
- TR: tl.constexpr,
56
- BT: tl.constexpr,
57
- BD: tl.constexpr,
58
- IS_SEQLEN_OFFSETS_TENSOR: tl.constexpr,
59
- IS_VARLEN: tl.constexpr,
60
- INTERLEAVED: tl.constexpr,
61
- CONJUGATE: tl.constexpr,
62
- NT_OFFSET: tl.constexpr,
63
- ):
64
- i_t, i_b, i_h = tl.program_id(0), tl.program_id(1), tl.program_id(2)
65
- i_t += NT_OFFSET
66
-
67
- if IS_VARLEN:
68
- i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
69
- bos, eos = tl.load(cu_seqlens + i_n), tl.load(cu_seqlens + i_n + 1)
70
- T = eos - bos
71
- x = x + bos * H*D + i_h * D
72
- y = y + bos * H*D + i_h * D
73
- else:
74
- i_n = i_b
75
- x = x + i_n * T*H*D + i_h * D
76
- y = y + i_n * T*H*D + i_h * D
77
-
78
- if i_t * BT >= T:
79
- return
80
-
81
- o_t = i_t * BT + tl.arange(0, BT)
82
- if not IS_SEQLEN_OFFSETS_TENSOR:
83
- o_cs = o_t + seq_offsets
84
- else:
85
- o_cs = o_t + tl.load(seq_offsets + i_n)
86
- m_t = (o_t >= 0) & (o_t < T) & (o_cs >= 0) & (o_cs < TR)
87
-
88
- if not INTERLEAVED:
89
- o_r = tl.arange(0, BD // 2)
90
- p_x = x + o_t[:, None] * H*D + o_r[None, :]
91
- p_cos = cos + (o_cs[:, None] * R + o_r[None, :])
92
- p_sin = sin + (o_cs[:, None] * R + o_r[None, :])
93
- mask = m_t[:, None] & (o_r < R)[None, :]
94
-
95
- b_cos = tl.load(p_cos, mask=mask, other=1.0).to(tl.float32)
96
- b_sin = tl.load(p_sin, mask=mask, other=0.0).to(tl.float32)
97
- b_x0 = tl.load(p_x, mask=mask, other=0.0).to(tl.float32)
98
- b_x1 = tl.load(p_x + R, mask=mask, other=0.0).to(tl.float32)
99
- if CONJUGATE:
100
- b_sin = -b_sin
101
- b_o0 = b_x0 * b_cos - b_x1 * b_sin
102
- b_o1 = b_x0 * b_sin + b_x1 * b_cos
103
- p_y = y + (o_t[:, None] * H*D + o_r[None, :])
104
- tl.store(p_y, b_o0, mask=mask)
105
- tl.store(p_y + R, b_o1, mask=mask)
106
- else:
107
- o_d = tl.arange(0, BD)
108
- o_d_swap = o_d + ((o_d + 1) % 2) * 2 - 1
109
- o_d_repeat = tl.arange(0, BD) // 2
110
- p_x0 = x + o_t[:, None] * H*D + o_d[None, :]
111
- p_x1 = x + o_t[:, None] * H*D + o_d_swap[None, :]
112
- p_cos = cos + (o_cs[:, None] * R + o_d_repeat[None, :])
113
- p_sin = sin + (o_cs[:, None] * R + o_d_repeat[None, :])
114
- mask = m_t[:, None] & (o_d_repeat < R)[None, :]
115
-
116
- b_cos = tl.load(p_cos, mask=mask, other=1.0).to(tl.float32)
117
- b_sin = tl.load(p_sin, mask=mask, other=0.0).to(tl.float32)
118
- b_x0 = tl.load(p_x0, mask=mask, other=0.0).to(tl.float32)
119
- b_x1 = tl.load(p_x1, mask=mask, other=0.0).to(tl.float32)
120
- if CONJUGATE:
121
- b_sin = -b_sin
122
- b_o0 = b_x0 * b_cos
123
- b_o1 = b_x1 * b_sin
124
- b_y = tl.where(o_d[None, :] % 2 == 0, b_o0 - b_o1, b_o0 + b_o1)
125
- p_y = y + (o_t[:, None] * H*D + o_d[None, :])
126
- tl.store(p_y, b_y, mask=mask)
127
-
128
-
129
- def rotary_embedding_fwdbwd_npu(
130
- x: torch.Tensor,
131
- cos: torch.Tensor,
132
- sin: torch.Tensor,
133
- seqlen_offsets: int | torch.Tensor = 0,
134
- cu_seqlens: torch.Tensor | None = None,
135
- interleaved: bool = False,
136
- inplace: bool = False,
137
- conjugate: bool = False,
138
- chunk_indices: torch.LongTensor | None = None,
139
- ) -> torch.Tensor:
140
- is_varlen = cu_seqlens is not None
141
-
142
- B, T, H, D = x.shape
143
- N = B if not is_varlen else cu_seqlens.shape[0] - 1
144
- TR, R = cos.shape
145
- R2 = R * 2
146
-
147
- assert D <= 256, "Only support D <= 256"
148
- assert TR >= T, f"TR must be >= T, got {TR} and {T}"
149
-
150
- assert cos.dtype == sin.dtype, f"cos and sin must have the same dtype, got {cos.dtype} and {sin.dtype}"
151
- assert x.dtype == cos.dtype, f"Input and cos/sin must have the same dtype, got {x.dtype} and {cos.dtype}"
152
-
153
- if isinstance(seqlen_offsets, torch.Tensor):
154
- assert seqlen_offsets.shape == (N,)
155
- assert seqlen_offsets.dtype in [torch.int32, torch.int64]
156
- else:
157
- assert seqlen_offsets + T <= TR
158
-
159
- y = torch.zeros_like(x) if not inplace else x
160
- if R2 < D and not inplace:
161
- y[..., R2:].copy_(x[..., R2:])
162
-
163
- BD = triton.next_power_of_2(R2)
164
- desired_bt = triton.next_power_of_2(triton.cdiv(T, get_multiprocessor_count(x.device.index)))
165
- bt_cap = compute_row_tile_block_size(
166
- desired_bt,
167
- R2,
168
- _ROTARY_MEM_MULT,
169
- safety_margin=_ROTARY_SAFETY_MARGIN,
170
- dtype_size=x.element_size(),
171
- fallback=16 if R >= 128 else (32 if R >= 64 else 64),
172
- min_block=1,
173
- )
174
- BT = min(bt_cap, desired_bt)
175
- BT = compute_grid_limited_tile_size(T, B * H, BT, max_grid=ASCEND_MAX_GRID_DIM)
176
- if chunk_indices is None and is_varlen:
177
- chunk_indices = prepare_chunk_indices(cu_seqlens, BT)
178
- NT = len(chunk_indices) if is_varlen else triton.cdiv(T, BT)
179
-
180
- kernel_kwargs = dict(
181
- x=x,
182
- cos=cos,
183
- sin=sin,
184
- y=y,
185
- cu_seqlens=cu_seqlens,
186
- chunk_indices=chunk_indices,
187
- seq_offsets=seqlen_offsets,
188
- B=B,
189
- T=T,
190
- H=H,
191
- D=D,
192
- R=R,
193
- TR=TR,
194
- BT=BT,
195
- BD=BD,
196
- IS_SEQLEN_OFFSETS_TENSOR=isinstance(seqlen_offsets, torch.Tensor),
197
- IS_VARLEN=is_varlen,
198
- INTERLEAVED=interleaved,
199
- CONJUGATE=conjugate,
200
- )
201
- max_nt = max_grid_axis_chunks(NT, B * H, max_grid=ASCEND_MAX_GRID_DIM)
202
- for nt_off in range(0, NT, max_nt):
203
- nt_len = min(max_nt, NT - nt_off)
204
- if is_varlen:
205
- kernel_kwargs['chunk_indices'] = chunk_indices[nt_off:nt_off + nt_len]
206
- kernel_kwargs['NT_OFFSET'] = 0
207
- else:
208
- kernel_kwargs['chunk_indices'] = chunk_indices
209
- kernel_kwargs['NT_OFFSET'] = nt_off
210
- rotary_embedding_kernel[(nt_len, B, H)](**kernel_kwargs)
211
- return y
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/{torch-xpu/modules/conv/triton → torch-cuda/modules/causal_conv1d}/__init__.py RENAMED
@@ -5,20 +5,27 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- from .ops import (
 
9
  CausalConv1dFunction,
 
 
10
  causal_conv1d_bwd,
11
  causal_conv1d_fwd,
12
  causal_conv1d_update,
13
  causal_conv1d_update_states,
14
- compute_dh0_triton,
15
  )
16
 
17
  __all__ = [
18
  'CausalConv1dFunction',
 
 
19
  'causal_conv1d_bwd',
20
  'causal_conv1d_fwd',
21
  'causal_conv1d_update',
22
  'causal_conv1d_update_states',
23
- 'compute_dh0_triton',
24
  ]
 
 
 
 
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ from ...backends import GluonBackend
9
+ from ...modules.causal_conv1d.ops import (
10
  CausalConv1dFunction,
11
+ ShortConvolution,
12
+ causal_conv1d,
13
  causal_conv1d_bwd,
14
  causal_conv1d_fwd,
15
  causal_conv1d_update,
16
  causal_conv1d_update_states,
 
17
  )
18
 
19
  __all__ = [
20
  'CausalConv1dFunction',
21
+ 'ShortConvolution',
22
+ 'causal_conv1d',
23
  'causal_conv1d_bwd',
24
  'causal_conv1d_fwd',
25
  'causal_conv1d_update',
26
  'causal_conv1d_update_states',
 
27
  ]
28
+
29
+
30
+ if GluonBackend.is_available():
31
+ from ...modules.causal_conv1d.backends import gluon # noqa: F401
build/torch-cuda/modules/causal_conv1d/backends/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
build/torch-cuda/modules/{conv → causal_conv1d/backends}/cuda/__init__.py RENAMED
@@ -5,10 +5,6 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- from .ops import FastCausalConv1dFn, causal_conv1d_cuda, fast_causal_conv1d_fn
9
 
10
- __all__ = [
11
- 'FastCausalConv1dFn',
12
- 'causal_conv1d_cuda',
13
- 'fast_causal_conv1d_fn',
14
- ]
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ from .....modules.causal_conv1d.backends.cuda.ops import FastCausalConv1dFn, causal_conv1d_cuda, fast_causal_conv1d_fn
9
 
10
+ __all__ = ['FastCausalConv1dFn', 'causal_conv1d_cuda', 'fast_causal_conv1d_fn']
 
 
 
 
build/torch-cuda/modules/causal_conv1d/backends/cuda/cp.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING
11
+
12
+ import torch
13
+ import torch.nn.functional as F
14
+
15
+ from .....ops.cp import conv_cp_send_recv_bwd, conv_cp_send_recv_fwd
16
+ from .....ops.utils import prepare_sequence_ids
17
+
18
+ if TYPE_CHECKING:
19
+ from torch.distributed import ProcessGroup
20
+
21
+ from .....ops.cp import FLACPContext
22
+
23
+
24
+ class ConvBoundaryExchangeFunction(torch.autograd.Function):
25
+ """Exchange convolution history and return its gradients to the sending rank."""
26
+
27
+ @staticmethod
28
+ def forward(ctx, tails: torch.Tensor, group: ProcessGroup) -> torch.Tensor:
29
+ ctx.group = group
30
+ return conv_cp_send_recv_fwd(tails=tails, group=group)
31
+
32
+ @staticmethod
33
+ def backward(ctx, dheads: torch.Tensor) -> tuple[torch.Tensor, None]:
34
+ return conv_cp_send_recv_bwd(d_initial_state=dheads.contiguous(), group=ctx.group), None
35
+
36
+
37
+ def causal_conv1d_cuda_cp(
38
+ x: torch.Tensor,
39
+ weight: torch.Tensor,
40
+ bias: torch.Tensor | None,
41
+ residual: torch.Tensor | None,
42
+ activation: str | None,
43
+ cp_context: FLACPContext,
44
+ ) -> torch.Tensor:
45
+ """Apply CUDA convolution to contiguous CP inputs of shape `[1, T, D]`.
46
+
47
+ Each rank needs at least `W - 1` tokens, with `W` in {2, 3, 4}.
48
+ Packed sequence boundaries mask both history values and their gradients.
49
+ """
50
+ from .....modules.causal_conv1d.backends.cuda.ops import causal_conv1d_fn_cuda
51
+
52
+ if causal_conv1d_fn_cuda is None:
53
+ raise ImportError("CUDA CP requires the causal-conv1d package")
54
+ if cp_context.layout != 'contiguous':
55
+ raise NotImplementedError("CUDA convolution supports only contiguous CP")
56
+ width = weight.shape[-1]
57
+ if width not in (2, 3, 4):
58
+ raise NotImplementedError("CUDA CP convolution supports kernel widths 2, 3, and 4")
59
+ if x.ndim != 3 or x.shape[0] != 1:
60
+ raise ValueError("CUDA CP convolution requires input shape [1, T, D]")
61
+ if x.shape[1] < width - 1:
62
+ raise ValueError("CUDA CP convolution requires at least kernel_width - 1 tokens per rank")
63
+ if cp_context.conv1d_kernel_size != width:
64
+ raise ValueError("CP context kernel width must match the convolution weight")
65
+ if activation not in (None, 'silu', 'swish'):
66
+ raise NotImplementedError("activation must be None, silu, or swish")
67
+
68
+ initial_state = None
69
+ if cp_context.group is not None:
70
+ tails = x[0, -(width - 1):].contiguous()
71
+ heads = ConvBoundaryExchangeFunction.apply(tails, cp_context.group)
72
+ valid_len = min(width - 1, cp_context.pre_num_conv_tokens or 0)
73
+ # history must not cross the first local document's global start
74
+ mask = torch.arange(width - 1, device=x.device) >= width - 1 - valid_len
75
+ initial_state = heads.masked_fill(~mask[:, None], 0).unsqueeze(0)
76
+
77
+ channels = x.shape[-1]
78
+ # the CUDA channel-last kernels require strides divisible by eight
79
+ padding = -channels % 8
80
+ x = F.pad(x, (0, padding)) if padding else x.contiguous()
81
+ if padding:
82
+ weight = F.pad(weight, (0, 0, 0, padding))
83
+ bias = F.pad(bias, (0, padding)) if bias is not None else None
84
+ initial_state = F.pad(initial_state, (0, padding)) if initial_state is not None else None
85
+
86
+ seq_idx = prepare_sequence_ids(cu_seqlens=cp_context.cu_seqlens, cu_seqlens_cpu=cp_context.cu_seqlens_cpu)
87
+ seq_idx = seq_idx.to(torch.int32).unsqueeze(0)
88
+
89
+ y = causal_conv1d_fn_cuda(
90
+ x=x.transpose(1, 2),
91
+ weight=weight,
92
+ bias=bias,
93
+ seq_idx=seq_idx,
94
+ initial_states=initial_state.transpose(1, 2) if initial_state is not None else None,
95
+ activation=activation,
96
+ )
97
+ y = y.transpose(1, 2)[..., :channels]
98
+ return y + residual if residual is not None else y
build/{torch-xpu/modules/conv → torch-cuda/modules/causal_conv1d/backends}/cuda/ops.py RENAMED
@@ -5,14 +5,14 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- """CUDA-based mixed-mode implementation for causal convolution."""
9
 
10
  import torch
11
  from einops import rearrange
12
 
13
- from ....modules.conv.triton import causal_conv1d_update_states
14
- from ....ops.utils import prepare_sequence_ids
15
- from ....utils import input_guard
16
 
17
  try:
18
  from causal_conv1d.cpp_functions import causal_conv1d_bwd_function
@@ -26,45 +26,27 @@ except ImportError:
26
 
27
 
28
  class FastCausalConv1dFn(torch.autograd.Function):
29
- """
30
- Mixed-mode (Mix) Causal Convolution Implementation - Combining Triton Forward and CUDA Backward Propagation
31
-
32
- This class implements forward propagation using FLA's Triton kernel, while using the optimized
33
- implementation from TriDao's causal_conv1d CUDA package for backward propagation.
34
- This hybrid strategy combines the advantages of both technologies:
35
-
36
- - Forward: Uses FLA's Triton implementation, optimized for the FLA framework
37
- - Backward: Uses TriDao's causal_conv1d_bwd_function CUDA implementation for faster speed
38
-
39
- Performance Benefits:
40
- - CUDA backward implementation is typically faster than the Triton version, reducing training time
41
- - Maintains the flexibility and compatibility of forward propagation
42
-
43
- Note:
44
- - Input/Output format is (batch, seqlen, dim)
45
- - Backward propagation requires causal_conv1d package: pip install causal-conv1d
46
- - Supports SILU/Swish activation functions
47
- - Current limitations (not yet supported):
48
- * output_final_state must be False
49
- * initial_states must be None
50
- * residual must be None
51
  """
52
  @staticmethod
53
  @input_guard(no_guard_contiguous=["x"])
54
  def forward(
55
  ctx,
56
- x,
57
- weight,
58
- bias=None,
59
  residual: torch.Tensor | None = None,
60
- initial_states=None,
61
- output_final_state=False,
62
- activation=None,
63
  cu_seqlens: torch.LongTensor | None = None,
64
  cu_seqlens_cpu: torch.LongTensor | None = None,
65
  chunk_indices: torch.LongTensor | None = None,
66
  seq_idx: torch.LongTensor | None = None,
67
- ):
68
  if activation not in [None, "silu", "swish"]:
69
  raise NotImplementedError("activation must be None, silu, or swish")
70
  assert output_final_state is False, "output_final_state must be False for FastCausalConv1dFn"
@@ -73,12 +55,11 @@ class FastCausalConv1dFn(torch.autograd.Function):
73
 
74
  bias = bias.contiguous() if bias is not None else None
75
  if cu_seqlens is not None and seq_idx is None:
76
- seq_idx = prepare_sequence_ids(cu_seqlens, cu_seqlens_cpu=cu_seqlens_cpu).to(
77
- torch.int32).unsqueeze(0)
78
  seq_idx = seq_idx.contiguous() if seq_idx is not None else None
79
 
80
- # Import here to avoid circular dependency
81
- from ....modules.conv.triton.ops import causal_conv1d_fwd
82
 
83
  ctx.activation = activation in ["silu", "swish"]
84
  out, _ = causal_conv1d_fwd(
@@ -96,14 +77,12 @@ class FastCausalConv1dFn(torch.autograd.Function):
96
 
97
  ctx.save_for_backward(x, weight, bias, seq_idx, initial_states)
98
  ctx.return_final_states = output_final_state
99
- ctx.return_dinitial_states = (
100
- initial_states is not None and initial_states.requires_grad
101
- )
102
  return out, None
103
 
104
  @staticmethod
105
  @input_guard
106
- def backward(ctx, dout, *args):
107
  x, weight, bias, seq_idx, initial_states = ctx.saved_tensors
108
  dx = torch.empty_like(x, memory_format=torch.contiguous_format)
109
  x = rearrange(x, 'b t d -> b d t')
@@ -113,35 +92,20 @@ class FastCausalConv1dFn(torch.autograd.Function):
113
 
114
  if dout.stride(2) != 1 and dout.stride(1) != 1:
115
  dout = dout.contiguous()
116
- # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the
117
- # backward of conv1d with the backward of chunk).
118
- # Here we just pass in None and dx will be allocated in the C++ code.
119
  dx, dweight, dbias, dinitial_states = causal_conv1d_bwd_function(
120
- x,
121
- weight,
122
- bias,
123
- dout,
124
- seq_idx,
125
- initial_states,
126
- dfinal_states,
127
- dx,
128
- ctx.return_dinitial_states,
129
- ctx.activation,
130
  )
131
  dx = rearrange(dx, 'b d t -> b t d')
132
- return (
133
- dx,
134
- dweight,
135
- dbias if bias is not None else None,
136
- None,
137
- None,
138
- None,
139
- None,
140
- None,
141
- None,
142
- None,
143
- None,
144
- )
145
 
146
 
147
  def fast_causal_conv1d_fn(
@@ -156,17 +120,36 @@ def fast_causal_conv1d_fn(
156
  cu_seqlens_cpu: torch.LongTensor | None = None,
157
  chunk_indices: torch.LongTensor | None = None,
158
  seq_idx: torch.LongTensor | None = None,
159
- ):
160
- """
161
- x: (batch, seqlen, dim)
162
- weight: (dim, width)
163
- bias: (dim,)
164
- seq_idx: (batch, seqlen)
165
- initial_states: (batch, dim, width - 1)
166
- final_states_out: (batch, dim, width - 1), to be written to
167
- activation: either None or "silu" or "swish"
168
-
169
- out: (batch, seqlen, dim)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  """
171
  assert causal_conv1d_bwd_function is not None, "causal_conv1d_bwd_function is not available"
172
  return FastCausalConv1dFn.apply(
@@ -195,7 +178,7 @@ def causal_conv1d_cuda(
195
  cu_seqlens: torch.Tensor | None = None,
196
  cu_seqlens_cpu: torch.LongTensor | None = None,
197
  **kwargs,
198
- ):
199
  assert causal_conv1d_fn_cuda is not None, "causal_conv1d_fn_cuda is not available"
200
  seq_idx = kwargs.get('seq_idx')
201
  if cu_seqlens is not None or seq_idx is not None:
@@ -205,29 +188,27 @@ def causal_conv1d_cuda(
205
  x = x.contiguous()
206
  x_conv1d = rearrange(x, 'b t d -> b d t')
207
  if cu_seqlens is not None and seq_idx is None:
208
- seq_idx = prepare_sequence_ids(cu_seqlens, cu_seqlens_cpu=cu_seqlens_cpu).to(torch.int32).unsqueeze(0)
209
 
210
  y = causal_conv1d_fn_cuda(
211
  x=x_conv1d,
212
  weight=weight,
213
  bias=bias,
214
- activation=activation,
215
  seq_idx=seq_idx,
216
  initial_states=None,
217
  return_final_states=False,
 
218
  )
219
 
220
  y = rearrange(y, 'b d t -> b t d')
221
  if output_final_state:
222
- final_state = causal_conv1d_update_states(
223
- x=x,
224
- state_len=W,
225
- initial_state=initial_state,
226
- cu_seqlens=cu_seqlens,
227
- )
228
  else:
229
  final_state = None
230
  if residual is not None:
231
  y.add_(residual)
232
 
233
  return y, final_state
 
 
 
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ """CUDA and mixed Triton/CUDA implementations of causal convolution."""
9
 
10
  import torch
11
  from einops import rearrange
12
 
13
+ from .....modules.causal_conv1d import causal_conv1d_update_states
14
+ from .....ops.utils import prepare_sequence_ids
15
+ from .....utils import input_guard
16
 
17
  try:
18
  from causal_conv1d.cpp_functions import causal_conv1d_bwd_function
 
26
 
27
 
28
  class FastCausalConv1dFn(torch.autograd.Function):
29
+ """Causal convolution with Triton forward and CUDA backward on `[B, T, D]` inputs.
30
+
31
+ Backward requires the `causal-conv1d` package. Activation may be `None`, `silu`, or `swish`.
32
+ Residuals and initial/final states are unsupported.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  """
34
  @staticmethod
35
  @input_guard(no_guard_contiguous=["x"])
36
  def forward(
37
  ctx,
38
+ x: torch.Tensor,
39
+ weight: torch.Tensor,
40
+ bias: torch.Tensor | None = None,
41
  residual: torch.Tensor | None = None,
42
+ initial_states: torch.Tensor | None = None,
43
+ output_final_state: bool = False,
44
+ activation: str | None = None,
45
  cu_seqlens: torch.LongTensor | None = None,
46
  cu_seqlens_cpu: torch.LongTensor | None = None,
47
  chunk_indices: torch.LongTensor | None = None,
48
  seq_idx: torch.LongTensor | None = None,
49
+ ) -> tuple[torch.Tensor, None]:
50
  if activation not in [None, "silu", "swish"]:
51
  raise NotImplementedError("activation must be None, silu, or swish")
52
  assert output_final_state is False, "output_final_state must be False for FastCausalConv1dFn"
 
55
 
56
  bias = bias.contiguous() if bias is not None else None
57
  if cu_seqlens is not None and seq_idx is None:
58
+ seq_idx = prepare_sequence_ids(cu_seqlens=cu_seqlens, cu_seqlens_cpu=cu_seqlens_cpu).to(torch.int32).unsqueeze(0)
 
59
  seq_idx = seq_idx.contiguous() if seq_idx is not None else None
60
 
61
+ # import here to avoid circular dependency.
62
+ from .....modules.causal_conv1d import causal_conv1d_fwd
63
 
64
  ctx.activation = activation in ["silu", "swish"]
65
  out, _ = causal_conv1d_fwd(
 
77
 
78
  ctx.save_for_backward(x, weight, bias, seq_idx, initial_states)
79
  ctx.return_final_states = output_final_state
80
+ ctx.return_dinitial_states = initial_states is not None and initial_states.requires_grad
 
 
81
  return out, None
82
 
83
  @staticmethod
84
  @input_guard
85
+ def backward(ctx, dout: torch.Tensor, *args) -> tuple[torch.Tensor | None, ...]:
86
  x, weight, bias, seq_idx, initial_states = ctx.saved_tensors
87
  dx = torch.empty_like(x, memory_format=torch.contiguous_format)
88
  x = rearrange(x, 'b t d -> b d t')
 
92
 
93
  if dout.stride(2) != 1 and dout.stride(1) != 1:
94
  dout = dout.contiguous()
 
 
 
95
  dx, dweight, dbias, dinitial_states = causal_conv1d_bwd_function(
96
+ x=x,
97
+ weight=weight,
98
+ bias=bias,
99
+ dout=dout,
100
+ seq_idx=seq_idx,
101
+ initial_states=initial_states,
102
+ dfinal_states=dfinal_states,
103
+ dx=dx,
104
+ return_dinitial_states=ctx.return_dinitial_states,
105
+ silu_activation=ctx.activation,
106
  )
107
  dx = rearrange(dx, 'b d t -> b t d')
108
+ return (dx, dweight, dbias if bias is not None else None, None, None, None, None, None, None, None, None)
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
 
111
  def fast_causal_conv1d_fn(
 
120
  cu_seqlens_cpu: torch.LongTensor | None = None,
121
  chunk_indices: torch.LongTensor | None = None,
122
  seq_idx: torch.LongTensor | None = None,
123
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
124
+ """Apply mixed Triton/CUDA causal convolution to `[B, T, D]` inputs.
125
+
126
+ Args:
127
+ x (torch.Tensor):
128
+ Input of shape `[B, T, D]`.
129
+ weight (torch.Tensor, Optional):
130
+ Convolution weights of shape `[D, W]`. Default: `None`.
131
+ bias (torch.Tensor, Optional):
132
+ Bias of shape `[D]`. Default: `None`.
133
+ residual (torch.Tensor, Optional):
134
+ Unsupported by this backend; must be `None`. Default: `None`.
135
+ initial_state (torch.Tensor, Optional):
136
+ Unsupported by this backend; must be `None`. Default: `None`.
137
+ output_final_state (bool, Optional):
138
+ Unsupported by this backend; must be `False`. Default: `False`.
139
+ activation (str, Optional):
140
+ Activation applied to the output: `None`, `silu`, or `swish`. Default: `None`.
141
+ cu_seqlens (torch.Tensor, Optional):
142
+ Cumulative sequence lengths for packed inputs. Default: `None`.
143
+ cu_seqlens_cpu (torch.LongTensor, Optional):
144
+ CPU copy of `cu_seqlens`. Default: `None`.
145
+ chunk_indices (torch.LongTensor, Optional):
146
+ Precomputed sequence chunk indices. Default: `None`.
147
+ seq_idx (torch.LongTensor, Optional):
148
+ Sequence IDs of shape `[B, T]`. Derived from `cu_seqlens` when omitted. Default: `None`.
149
+
150
+ Returns:
151
+ tuple[torch.Tensor, None]:
152
+ Output of shape `[B, T, D]` and no final state.
153
  """
154
  assert causal_conv1d_bwd_function is not None, "causal_conv1d_bwd_function is not available"
155
  return FastCausalConv1dFn.apply(
 
178
  cu_seqlens: torch.Tensor | None = None,
179
  cu_seqlens_cpu: torch.LongTensor | None = None,
180
  **kwargs,
181
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
182
  assert causal_conv1d_fn_cuda is not None, "causal_conv1d_fn_cuda is not available"
183
  seq_idx = kwargs.get('seq_idx')
184
  if cu_seqlens is not None or seq_idx is not None:
 
188
  x = x.contiguous()
189
  x_conv1d = rearrange(x, 'b t d -> b d t')
190
  if cu_seqlens is not None and seq_idx is None:
191
+ seq_idx = prepare_sequence_ids(cu_seqlens=cu_seqlens, cu_seqlens_cpu=cu_seqlens_cpu).to(torch.int32).unsqueeze(0)
192
 
193
  y = causal_conv1d_fn_cuda(
194
  x=x_conv1d,
195
  weight=weight,
196
  bias=bias,
 
197
  seq_idx=seq_idx,
198
  initial_states=None,
199
  return_final_states=False,
200
+ activation=activation,
201
  )
202
 
203
  y = rearrange(y, 'b d t -> b t d')
204
  if output_final_state:
205
+ final_state = causal_conv1d_update_states(x=x, state_len=W, initial_state=initial_state, cu_seqlens=cu_seqlens)
 
 
 
 
 
206
  else:
207
  final_state = None
208
  if residual is not None:
209
  y.add_(residual)
210
 
211
  return y, final_state
212
+
213
+
214
+ __all__ = ['FastCausalConv1dFn', 'causal_conv1d_cuda', 'fast_causal_conv1d_fn']
build/torch-cuda/modules/causal_conv1d/backends/gluon.py ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ """Causal depthwise convolution with register reuse and fused backward recomputation."""
9
+
10
+ import torch
11
+ import triton
12
+ from triton.experimental import gluon
13
+ from triton.experimental.gluon import language as gl
14
+
15
+ from ....backends import GluonBackend, register
16
+ from ....modules.causal_conv1d import causal_conv1d_bwd, causal_conv1d_fwd
17
+ from ....ops.utils import prepare_chunk_indices
18
+ from ....utils import input_guard
19
+
20
+
21
+ @gluon.jit(do_not_specialize=['T'])
22
+ def causal_conv1d_fwd_kernel(
23
+ x,
24
+ y,
25
+ weight,
26
+ bias,
27
+ residual,
28
+ cu_seqlens,
29
+ initial_state,
30
+ chunk_indices,
31
+ T,
32
+ SXN: gl.constexpr,
33
+ SXT: gl.constexpr,
34
+ D: gl.constexpr,
35
+ W: gl.constexpr,
36
+ BT: gl.constexpr,
37
+ BD: gl.constexpr,
38
+ BC: gl.constexpr,
39
+ ACTIVATION: gl.constexpr,
40
+ NUM_SPLITS: gl.constexpr = 1,
41
+ ):
42
+ layout: gl.constexpr = gl.BlockedLayout(
43
+ size_per_thread=[1, BD // 32],
44
+ threads_per_warp=[1, 32],
45
+ warps_per_cta=[4, 1],
46
+ order=[1, 0],
47
+ )
48
+ i_d = gl.program_id(0).to(gl.int64)
49
+ i_t = gl.program_id(1).to(gl.int64)
50
+ i_b = gl.program_id(2).to(gl.int64)
51
+ i_split = i_t % NUM_SPLITS
52
+ i_t //= NUM_SPLITS
53
+ if cu_seqlens is not None:
54
+ i_n = gl.load(chunk_indices + 2 * i_t).to(gl.int64)
55
+ i_t = gl.load(chunk_indices + 2 * i_t + 1).to(gl.int64)
56
+ bos = gl.load(cu_seqlens + i_n).to(gl.int64)
57
+ T = gl.load(cu_seqlens + i_n + 1).to(gl.int64) - bos
58
+ p_x = x + bos * SXT
59
+ else:
60
+ i_n = i_b
61
+ bos = i_b * T
62
+ p_x = x + i_b * SXN
63
+ o_d = i_d * BD + gl.arange(0, BD, layout=gl.SliceLayout(0, layout)).to(gl.int64)
64
+ o_t = (i_t * NUM_SPLITS + i_split) * BT
65
+ o_t += gl.arange(0, BT // BC, layout=gl.SliceLayout(1, layout)).to(gl.int64) * BC
66
+ b_x = ()
67
+ b_w = ()
68
+ for i_w in gl.static_range(W):
69
+ b_w += (gl.load(weight + o_d * W + i_w, mask=o_d < D, other=0).to(gl.float32),)
70
+ # each row unrolls BC outputs from one register window; adjacent outputs reuse inputs.
71
+ # with W=4:
72
+ # y[t] <- x[t-3] x[t-2] x[t-1] x[t]
73
+ # y[t+1] <- x[t-2] x[t-1] x[t] x[t+1]
74
+ for i_x in gl.static_range(BC + W - 1):
75
+ o_x = o_t + i_x - W + 1
76
+ b_xi = gl.load(
77
+ pointer=p_x + o_x[:, None] * SXT + o_d[None, :],
78
+ mask=(o_x[:, None] >= 0) & (o_x[:, None] < T) & (o_d[None, :] < D),
79
+ other=0,
80
+ ).to(gl.float32)
81
+ if initial_state is not None:
82
+ b_xi += gl.load(
83
+ pointer=initial_state + i_n * D * W + o_d[None, :] * W + (o_x[:, None] + W),
84
+ mask=(o_x[:, None] < 0) & (o_x[:, None] >= 1 - W) & (o_d[None, :] < D),
85
+ other=0,
86
+ ).to(gl.float32)
87
+ b_x += (b_xi,)
88
+ if bias is not None:
89
+ b_bias = gl.load(bias + o_d, mask=o_d < D, other=0).to(gl.float32)
90
+ for i_r in gl.static_range(BC):
91
+ b_y = gl.full((BT // BC, BD), 0, gl.float32, layout)
92
+ for i_w in gl.static_range(W):
93
+ b_y += b_x[i_r + i_w] * b_w[i_w][None, :]
94
+ if bias is not None:
95
+ b_y += b_bias[None, :]
96
+ if ACTIVATION == 'silu' or ACTIVATION == 'swish':
97
+ b_y *= 1. / (1. + gl.exp(-b_y))
98
+ o_y = (bos + o_t[:, None] + i_r) * D + o_d[None, :]
99
+ m_y = (o_t[:, None] + i_r < T) & (o_d[None, :] < D)
100
+ if residual is not None:
101
+ b_y += gl.load(residual + o_y, mask=m_y, other=0).to(gl.float32)
102
+ gl.store(y + o_y, b_y, mask=m_y)
103
+
104
+
105
+ @gluon.jit(do_not_specialize=['T'])
106
+ def causal_conv1d_bwd_kernel(
107
+ x,
108
+ weight,
109
+ bias,
110
+ dy,
111
+ dx,
112
+ dw_partial,
113
+ db_partial,
114
+ cu_seqlens,
115
+ chunk_indices,
116
+ T,
117
+ SXN: gl.constexpr,
118
+ SXT: gl.constexpr,
119
+ SYN: gl.constexpr,
120
+ SYT: gl.constexpr,
121
+ SYD: gl.constexpr,
122
+ D: gl.constexpr,
123
+ W: gl.constexpr,
124
+ BT: gl.constexpr,
125
+ BD: gl.constexpr,
126
+ BC: gl.constexpr,
127
+ ACTIVATION: gl.constexpr,
128
+ ):
129
+ layout: gl.constexpr = gl.BlockedLayout(
130
+ size_per_thread=[1, BD // 32],
131
+ threads_per_warp=[1, 32],
132
+ warps_per_cta=[4, 1],
133
+ order=[1, 0],
134
+ )
135
+ i_d = gl.program_id(0).to(gl.int64)
136
+ i_t = gl.program_id(1).to(gl.int64)
137
+ i_b = gl.program_id(2).to(gl.int64)
138
+ i_tg = i_b * gl.num_programs(1) + i_t
139
+ if cu_seqlens is not None:
140
+ i_n = gl.load(chunk_indices + 2 * i_t).to(gl.int64)
141
+ i_t = gl.load(chunk_indices + 2 * i_t + 1).to(gl.int64)
142
+ bos = gl.load(cu_seqlens + i_n).to(gl.int64)
143
+ T = gl.load(cu_seqlens + i_n + 1).to(gl.int64) - bos
144
+ p_x = x + bos * SXT
145
+ p_dy = dy + bos * SYT
146
+ else:
147
+ bos = i_b * T
148
+ p_x = x + i_b * SXN
149
+ p_dy = dy + i_b * SYN
150
+ o_d = i_d * BD + gl.arange(0, BD, layout=gl.SliceLayout(0, layout)).to(gl.int64)
151
+ # each row unrolls BC consecutive tokens within the BT-token tile.
152
+ # rows (BT=64, BC=16): [0..15] [16..31] [32..47] [48..63]
153
+ o_t = i_t * BT + gl.arange(0, BT // BC, layout=gl.SliceLayout(1, layout)).to(gl.int64) * BC
154
+ b_w = ()
155
+ b_x = ()
156
+ b_dy = ()
157
+ b_dw = ()
158
+ for i_w in gl.static_range(W):
159
+ b_w += (gl.load(weight + o_d * W + i_w, mask=o_d < D, other=0).to(gl.float32),)
160
+ b_dw += (gl.full((BT // BC, BD), 0, gl.float32, layout),)
161
+ if bias is not None:
162
+ b_bias = gl.load(bias + o_d, mask=o_d < D, other=0).to(gl.float32)
163
+ for i_x in gl.static_range(BC + 2 * W - 2):
164
+ o_x = o_t + i_x - W + 1
165
+ b_xi = gl.load(
166
+ pointer=p_x + o_x[:, None] * SXT + o_d[None, :],
167
+ mask=(o_x[:, None] >= 0) & (o_x[:, None] < T) & (o_d[None, :] < D),
168
+ other=0,
169
+ ).to(gl.float32)
170
+ b_x += (b_xi,)
171
+ for i_r in gl.static_range(BC + W - 1):
172
+ b_dyi = gl.load(
173
+ pointer=p_dy + (o_t[:, None] + i_r) * SYT + o_d[None, :] * SYD,
174
+ mask=(o_t[:, None] + i_r < T) & (o_d[None, :] < D),
175
+ other=0,
176
+ ).to(gl.float32)
177
+ if ACTIVATION == 'silu' or ACTIVATION == 'swish':
178
+ b_y = gl.full((BT // BC, BD), 0, gl.float32, layout)
179
+ for i_w in gl.static_range(W):
180
+ b_y += b_x[i_r + i_w] * b_w[i_w][None, :]
181
+ if bias is not None:
182
+ b_y += b_bias[None, :]
183
+ # backward preserves the baseline's rounded recomputed preactivation.
184
+ b_y = b_y.to(x.dtype.element_ty).to(gl.float32)
185
+ b_ys = 1. / (1. + gl.exp(-b_y))
186
+ b_dyi = b_dyi * b_ys * (1 + b_y * (1 - b_ys))
187
+ b_dy += (b_dyi,)
188
+ b_db = gl.full((BT // BC, BD), 0, gl.float32, layout)
189
+ for i_r in gl.static_range(BC):
190
+ b_dx = gl.full((BT // BC, BD), 0, gl.float32, layout)
191
+ b_dw_new = ()
192
+ for i_w in gl.static_range(W):
193
+ b_dyi = b_dy[i_r + i_w]
194
+ b_dx += b_dyi * b_w[W - i_w - 1][None, :]
195
+ b_dw_new += (b_dw[i_w] + b_dyi * b_x[i_r + W - 1],)
196
+ b_dw = b_dw_new
197
+ b_db += b_dy[i_r]
198
+ gl.store(
199
+ pointer=dx + (bos + o_t[:, None] + i_r) * D + o_d[None, :],
200
+ value=b_dx,
201
+ mask=(o_t[:, None] + i_r < T) & (o_d[None, :] < D),
202
+ )
203
+ for i_w in gl.static_range(W):
204
+ gl.store(pointer=dw_partial + i_tg * D * W + o_d * W + W - i_w - 1, value=gl.sum(b_dw[i_w], axis=0), mask=o_d < D)
205
+ if db_partial is not None:
206
+ gl.store(db_partial + i_tg * D + o_d, gl.sum(b_db, axis=0), mask=o_d < D)
207
+
208
+
209
+ @gluon.jit(do_not_specialize=['NP'])
210
+ def causal_conv1d_bwd_kernel_dwdb(dw_partial, db_partial, dw, db, NP, D: gl.constexpr, W: gl.constexpr, BD: gl.constexpr):
211
+ BN: gl.constexpr = 128
212
+ layout: gl.constexpr = gl.BlockedLayout(
213
+ size_per_thread=[1, BD // 32],
214
+ threads_per_warp=[1, 32],
215
+ warps_per_cta=[4, 1],
216
+ order=[1, 0],
217
+ )
218
+ i_d = gl.program_id(0).to(gl.int64)
219
+ o_d = i_d * BD + gl.arange(0, BD, layout=gl.SliceLayout(0, layout)).to(gl.int64)
220
+ b_dw = gl.full((BD,), 0, gl.float32, gl.SliceLayout(0, layout))
221
+ for i_n in range(0, NP, BN):
222
+ o_n = i_n + gl.arange(0, BN, layout=gl.SliceLayout(1, layout)).to(gl.int64)
223
+ b_partial = gl.load(
224
+ pointer=dw_partial + o_n[:, None] * D * W + o_d[None, :],
225
+ mask=(o_n[:, None] < NP) & (o_d[None, :] < D * W),
226
+ other=0,
227
+ )
228
+ b_dw += gl.sum(b_partial, axis=0)
229
+ gl.store(dw + o_d, b_dw.to(dw.dtype.element_ty), mask=o_d < D * W)
230
+ if db_partial is not None:
231
+ if i_d * BD < D:
232
+ b_db = gl.full((BD,), 0, gl.float32, gl.SliceLayout(0, layout))
233
+ for i_n in range(0, NP, BN):
234
+ o_n = i_n + gl.arange(0, BN, layout=gl.SliceLayout(1, layout)).to(gl.int64)
235
+ b_partial = gl.load(
236
+ pointer=db_partial + o_n[:, None] * D + o_d[None, :],
237
+ mask=(o_n[:, None] < NP) & (o_d[None, :] < D),
238
+ other=0,
239
+ )
240
+ b_db += gl.sum(b_partial, axis=0)
241
+ gl.store(db + o_d, b_db.to(db.dtype.element_ty), mask=o_d < D)
242
+
243
+
244
+ def causal_conv1d_fwd_verifier(
245
+ x: torch.Tensor,
246
+ weight: torch.Tensor | None,
247
+ bias: torch.Tensor | None = None,
248
+ residual: torch.Tensor | None = None,
249
+ initial_state: torch.Tensor | None = None,
250
+ output_final_state: bool = False,
251
+ activation: str | None = None,
252
+ cu_seqlens: torch.LongTensor | None = None,
253
+ cu_seqlens_cpu: torch.LongTensor | None = None,
254
+ chunk_indices: torch.LongTensor | None = None,
255
+ chunk_size: int = 64,
256
+ layout_fallback: bool = False,
257
+ ) -> tuple[bool, str | None]:
258
+ if torch.distributed.is_initialized():
259
+ return False, "Gluon convolution does not support distributed execution"
260
+ if x.dtype not in (torch.float16, torch.bfloat16, torch.float32):
261
+ return False, "Gluon convolution supports float16, bfloat16, and float32"
262
+ if x.ndim != 3 or x.stride(-1) != 1:
263
+ return False, "Gluon convolution requires [B, T, D] with contiguous channels"
264
+ if weight is None or weight.shape[0] != x.shape[-1] or weight.shape[1] not in (2, 3, 4):
265
+ return False, "Gluon convolution requires a width of 2, 3, or 4"
266
+ if cu_seqlens is not None and x.shape[0] != 1:
267
+ return False, "Gluon packed convolution requires batch size 1"
268
+ if chunk_size != 64:
269
+ return False, "Gluon convolution requires 64-token chunk indices"
270
+ return True, None
271
+
272
+
273
+ @register(causal_conv1d_fwd, backend=GluonBackend, verifier=causal_conv1d_fwd_verifier, env_var='FLA_CONV_GLUON')
274
+ @input_guard(no_guard_contiguous=['x'])
275
+ def causal_conv1d_fwd_gluon(
276
+ x: torch.Tensor,
277
+ weight: torch.Tensor,
278
+ bias: torch.Tensor | None = None,
279
+ residual: torch.Tensor | None = None,
280
+ initial_state: torch.Tensor | None = None,
281
+ output_final_state: bool = False,
282
+ activation: str | None = None,
283
+ cu_seqlens: torch.LongTensor | None = None,
284
+ cu_seqlens_cpu: torch.LongTensor | None = None,
285
+ chunk_indices: torch.LongTensor | None = None,
286
+ chunk_size: int = 64,
287
+ layout_fallback: bool = False,
288
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
289
+ del layout_fallback
290
+ BT = chunk_size
291
+ B, T, D = x.shape
292
+ if cu_seqlens is not None and chunk_indices is None:
293
+ chunk_indices = prepare_chunk_indices(cu_seqlens=cu_seqlens, chunk_size=BT, cu_seqlens_cpu=cu_seqlens_cpu)
294
+ NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
295
+ y = torch.empty_like(x, memory_format=torch.contiguous_format)
296
+ use_small_tile = B * T * D <= 1048576
297
+ BD, num_splits = (32, 2) if use_small_tile else (64, 1)
298
+ causal_conv1d_fwd_kernel[(triton.cdiv(D, BD), NT * num_splits, B)](
299
+ x=x,
300
+ y=y,
301
+ weight=weight,
302
+ bias=bias,
303
+ residual=residual,
304
+ cu_seqlens=cu_seqlens,
305
+ initial_state=initial_state,
306
+ chunk_indices=chunk_indices,
307
+ T=T,
308
+ SXN=x.stride(0),
309
+ SXT=x.stride(1),
310
+ D=D,
311
+ W=weight.shape[1],
312
+ BT=BT // num_splits,
313
+ BD=BD,
314
+ BC=8,
315
+ ACTIVATION=activation,
316
+ NUM_SPLITS=num_splits,
317
+ num_warps=4,
318
+ )
319
+ final_state = None
320
+ if output_final_state:
321
+ from ....modules.causal_conv1d import causal_conv1d_update_states
322
+
323
+ final_state = causal_conv1d_update_states(
324
+ x=x,
325
+ state_len=weight.shape[1],
326
+ initial_state=initial_state,
327
+ cu_seqlens=cu_seqlens,
328
+ )
329
+ return y, final_state
330
+
331
+
332
+ def causal_conv1d_bwd_verifier(
333
+ x: torch.Tensor,
334
+ dy: torch.Tensor,
335
+ dht: torch.Tensor | None,
336
+ weight: torch.Tensor | None = None,
337
+ bias: torch.Tensor | None = None,
338
+ residual: torch.Tensor | None = None,
339
+ initial_state: torch.Tensor | None = None,
340
+ activation: str | None = None,
341
+ cu_seqlens: torch.LongTensor | None = None,
342
+ cu_seqlens_cpu: torch.LongTensor | None = None,
343
+ chunk_indices: torch.LongTensor | None = None,
344
+ chunk_size: int = 64,
345
+ layout_fallback: bool = False,
346
+ ) -> tuple[bool, str | None]:
347
+ if initial_state is not None or dht is not None:
348
+ return False, "Gluon convolution does not support state gradients"
349
+ return causal_conv1d_fwd_verifier(x=x, weight=weight, cu_seqlens=cu_seqlens, chunk_size=chunk_size)
350
+
351
+
352
+ @register(causal_conv1d_bwd, backend=GluonBackend, verifier=causal_conv1d_bwd_verifier, env_var='FLA_CONV_GLUON')
353
+ def causal_conv1d_bwd_gluon(
354
+ x: torch.Tensor,
355
+ dy: torch.Tensor,
356
+ dht: torch.Tensor | None,
357
+ weight: torch.Tensor | None = None,
358
+ bias: torch.Tensor | None = None,
359
+ residual: torch.Tensor | None = None,
360
+ initial_state: torch.Tensor | None = None,
361
+ activation: str | None = None,
362
+ cu_seqlens: torch.LongTensor | None = None,
363
+ cu_seqlens_cpu: torch.LongTensor | None = None,
364
+ chunk_indices: torch.LongTensor | None = None,
365
+ chunk_size: int = 64,
366
+ layout_fallback: bool = False,
367
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor | None, torch.Tensor | None, None]:
368
+ del layout_fallback
369
+ BT = chunk_size
370
+ B, T, D = x.shape
371
+ W = weight.shape[1]
372
+ BD = 32
373
+ if cu_seqlens is not None and chunk_indices is None:
374
+ chunk_indices = prepare_chunk_indices(cu_seqlens=cu_seqlens, chunk_size=BT, cu_seqlens_cpu=cu_seqlens_cpu)
375
+ NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
376
+ dx = torch.empty_like(x, memory_format=torch.contiguous_format)
377
+ dw_partial = weight.new_empty((B * NT, D, W), dtype=torch.float32)
378
+ db_partial = bias.new_empty((B * NT, D), dtype=torch.float32) if bias is not None else None
379
+ causal_conv1d_bwd_kernel[(triton.cdiv(D, BD), NT, B)](
380
+ x=x,
381
+ weight=weight,
382
+ bias=bias,
383
+ dy=dy,
384
+ dx=dx,
385
+ dw_partial=dw_partial,
386
+ db_partial=db_partial,
387
+ cu_seqlens=cu_seqlens,
388
+ chunk_indices=chunk_indices,
389
+ T=T,
390
+ SXN=x.stride(0),
391
+ SXT=x.stride(1),
392
+ SYN=dy.stride(0),
393
+ SYT=dy.stride(1),
394
+ SYD=dy.stride(2),
395
+ D=D,
396
+ W=W,
397
+ BT=BT,
398
+ BD=BD,
399
+ BC=16,
400
+ ACTIVATION=activation,
401
+ num_warps=4,
402
+ )
403
+ dw = weight.new_empty((D, W))
404
+ db = bias.new_empty((D,)) if bias is not None else None
405
+ causal_conv1d_bwd_kernel_dwdb[(triton.cdiv(D * W, BD),)](
406
+ dw_partial=dw_partial,
407
+ db_partial=db_partial,
408
+ dw=dw,
409
+ db=db,
410
+ NP=B * NT,
411
+ D=D,
412
+ W=W,
413
+ BD=BD,
414
+ num_warps=4,
415
+ )
416
+ dr = dy if residual is not None else None
417
+ return dx, dw, db, dr, None
build/torch-cuda/modules/{conv/cp/ops.py → causal_conv1d/cp.py} RENAMED
@@ -8,8 +8,8 @@
8
  import torch
9
  import torch.distributed as dist
10
 
11
- from ....ops.cp import FLACPContext, conv_cp_send_recv_bwd, conv_cp_send_recv_fwd
12
- from ....ops.utils import prepare_chunk_indices
13
 
14
 
15
  class CausalConv1dFunctionCP(torch.autograd.Function):
@@ -36,28 +36,58 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
36
  """Prepare initial_state for CP forward pass by communicating with previous rank.
37
 
38
  Args:
39
- x: Input tensor of shape [1, T, D]
40
- weight: Weight tensor of shape [D, W]
41
- cu_seqlens: Cumulative sequence lengths
42
- context: CP context
43
- group: Process group for communication
 
 
 
 
 
44
 
45
  Returns:
46
- initial_state: Initial state tensor of shape [N, D, W] or None
 
47
  """
48
- if group is None:
 
49
  return None
50
 
51
- W = weight.shape[-1] # weight: [D, W]
52
  D = weight.shape[0]
53
  initial_state = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  if not context.is_first_rank:
55
- # Non-first rank needs initial_state
56
  assert x.dim() == 3 and x.shape[0] == 1, f"CP requires [1, T, D], got {x.shape}"
57
  x_2d = x.squeeze(0) # [T, D]
58
  tails = x_2d[-(W-1):].contiguous() # [W-1, D]
59
  heads = conv_cp_send_recv_fwd(tails, group) # [W-1, D]
60
- # Construct initial_state: [N, D, W]
61
  N = len(cu_seqlens) - 1
62
  initial_state = torch.zeros(N, D, W, device=x.device, dtype=x.dtype)
63
  valid_len = min(W - 1, context.pre_num_conv_tokens)
@@ -65,10 +95,11 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
65
  # heads[-valid_len:]: [valid_len, D] -> [D, valid_len]
66
  initial_state[0, :, -valid_len:] = heads[-valid_len:].T
67
  else:
68
- # First rank also needs to participate in communication (send tails)
69
  x_2d = x.squeeze(0)
70
  tails = x_2d[-(W-1):].contiguous()
71
- _ = conv_cp_send_recv_fwd(tails, group) # Send but don't use
 
72
 
73
  return initial_state
74
 
@@ -78,25 +109,46 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
78
  dh0: torch.Tensor | None,
79
  W: int,
80
  group: dist.ProcessGroup | None,
81
- is_first_rank: bool,
82
- pre_num_conv_tokens: int = 0,
83
  ) -> None:
84
  """Correct dx gradients for CP backward pass by communicating with next rank.
85
 
86
  Args:
87
- dx: Gradient tensor to be corrected, shape [1, T, D]
88
- dh0: Gradient w.r.t. initial_state, shape [N, D, W] or None
89
- W: Kernel size
90
- group: Process group for communication
91
- is_first_rank: Whether this is the first rank in the sequence's processing chain
92
- pre_num_conv_tokens: Number of tokens from the previous rank that
93
- belong to the first sequence on the current rank. Must match the
94
- value used in the forward pass to construct initial_state.
 
 
95
  """
96
- if group is None:
97
  return
98
 
99
  D = dx.shape[-1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  # dh0: [N, D, W] or None
101
  # We only care about the first sequence's initial_state gradient
102
  if dh0 is not None:
@@ -104,13 +156,13 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
104
  # previous rank. The forward fills only the last valid_len positions
105
  # of initial_state; gradients for the remaining (zero-padded) positions
106
  # must not flow back, otherwise they leak into unrelated sequences.
107
- valid_len = min(W - 1, pre_num_conv_tokens)
108
  d_initial_state = torch.zeros(W-1, D, device=dx.device, dtype=dx.dtype)
109
  if valid_len > 0:
110
  d_initial_state[-valid_len:] = dh0[0, :, -valid_len:].T
111
  else:
112
  # dh0 is None only when this is the first rank (no initial_state needed)
113
- assert is_first_rank, "dh0 should not be None when is_first_rank=False"
114
  d_initial_state = torch.zeros(W-1, D, device=dx.device, dtype=dx.dtype)
115
  # Sync communication: send d_initial_state to previous rank, receive from next rank
116
  recv_d_init = conv_cp_send_recv_bwd(d_initial_state, group) # [W-1, D]
@@ -128,9 +180,10 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
128
  cp_context: FLACPContext | None,
129
  chunk_size: int | None,
130
  backend: str = 'triton',
131
- ):
132
- # Import here to avoid circular dependency
133
- from ....modules.conv.triton.ops import causal_conv1d_fwd
 
134
 
135
  if cp_context is None:
136
  raise ValueError("cp_context must be provided for CausalConv1dFunctionCP")
@@ -138,9 +191,9 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
138
  cu_seqlens_cpu = cp_context.cu_seqlens_cpu
139
  group = cp_context.group
140
 
141
- # Get kernel_size
142
  W = weight.shape[-1] # weight: [D, W]
143
- # Prepare initial_state for CP
144
  initial_state = CausalConv1dFunctionCP._prepare_initial_state_for_cp(
145
  x=x,
146
  weight=weight,
@@ -149,7 +202,7 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
149
  group=group,
150
  )
151
 
152
- ctx.save_for_backward(x, weight, bias, initial_state)
153
  ctx.activation = activation
154
  ctx.cu_seqlens = cu_seqlens
155
  ctx.cu_seqlens_cpu = cu_seqlens_cpu
@@ -157,62 +210,54 @@ class CausalConv1dFunctionCP(torch.autograd.Function):
157
  ctx.chunk_size = chunk_size
158
  ctx.group = group
159
  ctx.W = W
160
- ctx.is_first_rank = cp_context.is_first_rank
161
- ctx.pre_num_conv_tokens = cp_context.pre_num_conv_tokens
162
 
163
- # Call original forward
164
  y, _ = causal_conv1d_fwd(
165
  x=x,
166
  weight=weight,
167
  bias=bias,
168
- residual=None,
169
  initial_state=initial_state,
170
  output_final_state=False,
171
  activation=activation,
172
  cu_seqlens=cu_seqlens,
173
  cu_seqlens_cpu=cu_seqlens_cpu,
174
  chunk_indices=chunk_indices,
175
- BT=chunk_size,
176
  )
177
 
178
  return y
179
 
180
  @staticmethod
181
- def backward(ctx, dy: torch.Tensor):
182
- # Import here to avoid circular dependency
183
- from ....modules.conv.triton.ops import causal_conv1d_bwd
184
 
185
- x, weight, bias, initial_state = ctx.saved_tensors
186
  group = ctx.group
187
  W = ctx.W
188
 
189
- # Call original backward
190
- dx, dw, db, _, dh0 = causal_conv1d_bwd(
191
  x=x,
192
  dy=dy,
193
  dht=None,
194
  weight=weight,
195
  bias=bias,
196
- residual=None,
197
  initial_state=initial_state,
198
  activation=ctx.activation,
199
  cu_seqlens=ctx.cu_seqlens,
200
  cu_seqlens_cpu=ctx.cu_seqlens_cpu,
201
  chunk_indices=ctx.chunk_indices,
202
- BT=ctx.chunk_size,
203
  )
204
 
205
- # Correct dx gradients for CP
206
- CausalConv1dFunctionCP._correct_dx_for_cp(
207
- dx=dx,
208
- dh0=dh0,
209
- W=W,
210
- group=group,
211
- is_first_rank=ctx.is_first_rank,
212
- pre_num_conv_tokens=ctx.pre_num_conv_tokens,
213
- )
214
 
215
- return dx, dw, db, None, None, None, None, None
216
 
217
 
218
  def causal_conv1d_cp(
@@ -224,35 +269,62 @@ def causal_conv1d_cp(
224
  cp_context: FLACPContext | None = None,
225
  chunk_size: int | None = None,
226
  backend: str = 'triton',
227
- ):
228
- """
229
- Context Parallel version of causal_conv1d.
230
-
231
- Automatically handles communication in CP environment:
232
- - Forward: get initial_state from previous rank
233
- - Backward: correct dx gradients
234
 
235
  Args:
236
- x: Input tensor of shape [1, T, D]
237
- weight: Weight tensor of shape [D, W]
238
- bias: Bias tensor of shape [D] or None
239
- activation: Activation function name or None
240
- cu_seqlens: Cumulative sequence lengths
241
- cu_seqlens_cpu: Cumulative sequence lengths on CPU
242
- chunk_indices: Chunk indices for variable-length sequences
243
- cp_context: CP context (required for CP mode)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  """
245
  if cp_context is None:
246
  raise ValueError("cp_context must be provided for causal_conv1d_cp")
247
 
248
  assert cp_context.conv1d_kernel_size is not None, "conv1d_kernel_size must be provided for causal_conv1d_cp"
249
  assert cp_context.cu_seqlens is not None, "cu_seqlens must be provided for causal_conv1d_cp"
250
- assert backend in ['triton'], "backend must be 'triton'"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  chunk_size = chunk_size or 64
252
  if chunk_indices is None:
253
- chunk_indices = prepare_chunk_indices(cp_context.cu_seqlens, chunk_size, cu_seqlens_cpu=cp_context.cu_seqlens_cpu)
 
 
 
 
254
 
255
- return CausalConv1dFunctionCP.apply(
256
- x, weight, bias, activation,
257
- chunk_indices, cp_context, chunk_size, backend
258
- )
 
8
  import torch
9
  import torch.distributed as dist
10
 
11
+ from ...ops.cp import FLACPContext, all_gather_into_tensor, conv_cp_send_recv_bwd, conv_cp_send_recv_fwd
12
+ from ...ops.utils import prepare_chunk_indices
13
 
14
 
15
  class CausalConv1dFunctionCP(torch.autograd.Function):
 
36
  """Prepare initial_state for CP forward pass by communicating with previous rank.
37
 
38
  Args:
39
+ x (torch.Tensor):
40
+ Input tensor of shape [1, T, D]
41
+ weight (torch.Tensor):
42
+ Weight tensor of shape [D, W]
43
+ cu_seqlens (torch.Tensor | None):
44
+ Cumulative sequence lengths
45
+ context (FLACPContext):
46
+ CP context
47
+ group (dist.ProcessGroup | None):
48
+ Process group for communication
49
 
50
  Returns:
51
+ initial_state (torch.Tensor | None):
52
+ Initial state tensor of shape [N, D, W] or None
53
  """
54
+ W = weight.shape[-1] # weight: [D, W]
55
+ if group is None or W == 1:
56
  return None
57
 
 
58
  D = weight.shape[0]
59
  initial_state = None
60
+ if context.layout == 'zigzag':
61
+ world_size = dist.get_world_size(group)
62
+ rank = dist.get_rank(group)
63
+ part_len = context.part_len
64
+ assert part_len >= W - 1, (
65
+ f"zigzag CP requires part_len ({part_len}) >= conv1d_kernel_size - 1 ({W - 1}); "
66
+ "increase total tokens or reduce world size"
67
+ )
68
+ assert x.dim() == 3 and x.shape[0] == 1, f"CP requires [1, T, D], got {x.shape}"
69
+ x_2d = x.squeeze(0) # [T, D]
70
+ # last W-1 tokens of each chain part (front, back)
71
+ tails = torch.stack([x_2d[part_len - (W - 1): part_len], x_2d[-(W - 1):]])
72
+ gathered, _ = all_gather_into_tensor(tails, group=group)
73
+ # chain order: front parts of ranks 0..W-1, then back parts of ranks W-1..0
74
+ slots = torch.cat([gathered[:, 0], gathered[:, 1].flip(0)])
75
+ N = len(cu_seqlens) - 1
76
+ initial_state = torch.zeros(N, D, W, device=x.device, dtype=x.dtype)
77
+ for part, row in enumerate((0, context.front_num_seqs)):
78
+ if not context.is_first_by_part[part]:
79
+ valid_len = min(W - 1, context.pre_num_conv_tokens_by_part[part])
80
+ if valid_len > 0:
81
+ chain_pos = rank if part == 0 else 2 * world_size - 1 - rank
82
+ initial_state[row, :, -valid_len:] = slots[chain_pos - 1][-valid_len:].T
83
+ return initial_state
84
  if not context.is_first_rank:
85
+ # non-first rank needs initial_state
86
  assert x.dim() == 3 and x.shape[0] == 1, f"CP requires [1, T, D], got {x.shape}"
87
  x_2d = x.squeeze(0) # [T, D]
88
  tails = x_2d[-(W-1):].contiguous() # [W-1, D]
89
  heads = conv_cp_send_recv_fwd(tails, group) # [W-1, D]
90
+ # construct initial_state: [N, D, W]
91
  N = len(cu_seqlens) - 1
92
  initial_state = torch.zeros(N, D, W, device=x.device, dtype=x.dtype)
93
  valid_len = min(W - 1, context.pre_num_conv_tokens)
 
95
  # heads[-valid_len:]: [valid_len, D] -> [D, valid_len]
96
  initial_state[0, :, -valid_len:] = heads[-valid_len:].T
97
  else:
98
+ # first rank also needs to participate in communication (send tails)
99
  x_2d = x.squeeze(0)
100
  tails = x_2d[-(W-1):].contiguous()
101
+ # send but don't use
102
+ _ = conv_cp_send_recv_fwd(tails, group)
103
 
104
  return initial_state
105
 
 
109
  dh0: torch.Tensor | None,
110
  W: int,
111
  group: dist.ProcessGroup | None,
112
+ cp_context: FLACPContext,
 
113
  ) -> None:
114
  """Correct dx gradients for CP backward pass by communicating with next rank.
115
 
116
  Args:
117
+ dx (torch.Tensor):
118
+ Gradient tensor to be corrected, shape [1, T, D]
119
+ dh0 (torch.Tensor | None):
120
+ Gradient w.r.t. initial_state, shape [N, D, W] or None
121
+ W (int):
122
+ Kernel size
123
+ group (dist.ProcessGroup | None):
124
+ Process group for communication
125
+ cp_context (FLACPContext):
126
+ CP context
127
  """
128
+ if group is None or W == 1:
129
  return
130
 
131
  D = dx.shape[-1]
132
+ if cp_context.layout == 'zigzag':
133
+ world_size = dist.get_world_size(group)
134
+ rank = dist.get_rank(group)
135
+ part_len = cp_context.part_len
136
+ # dh0 rows for chain-first parts stay zero: their forward initial_state was zeros
137
+ d_initial_state = torch.zeros(2, W - 1, D, device=dx.device, dtype=dx.dtype)
138
+ for part, row in enumerate((0, cp_context.front_num_seqs)):
139
+ if not cp_context.is_first_by_part[part]:
140
+ valid_len = min(W - 1, cp_context.pre_num_conv_tokens_by_part[part])
141
+ if valid_len > 0:
142
+ d_initial_state[part, -valid_len:] = dh0[row, :, -valid_len:].T
143
+ gathered, _ = all_gather_into_tensor(d_initial_state, group=group)
144
+ # chain order: front parts of ranks 0..W-1, then back parts of ranks W-1..0
145
+ slots = torch.cat([gathered[:, 0], gathered[:, 1].flip(0)])
146
+ # the front part's chain successor is the next rank's front or own back part
147
+ dx[0, part_len - (W - 1): part_len, :].add_(slots[rank + 1])
148
+ # the back part's successor exists unless it is chain-last (rank 0)
149
+ if rank > 0:
150
+ dx[0, -(W - 1):, :].add_(slots[2 * world_size - rank])
151
+ return
152
  # dh0: [N, D, W] or None
153
  # We only care about the first sequence's initial_state gradient
154
  if dh0 is not None:
 
156
  # previous rank. The forward fills only the last valid_len positions
157
  # of initial_state; gradients for the remaining (zero-padded) positions
158
  # must not flow back, otherwise they leak into unrelated sequences.
159
+ valid_len = min(W - 1, cp_context.pre_num_conv_tokens)
160
  d_initial_state = torch.zeros(W-1, D, device=dx.device, dtype=dx.dtype)
161
  if valid_len > 0:
162
  d_initial_state[-valid_len:] = dh0[0, :, -valid_len:].T
163
  else:
164
  # dh0 is None only when this is the first rank (no initial_state needed)
165
+ assert cp_context.is_first_rank, "dh0 should not be None when is_first_rank=False"
166
  d_initial_state = torch.zeros(W-1, D, device=dx.device, dtype=dx.dtype)
167
  # Sync communication: send d_initial_state to previous rank, receive from next rank
168
  recv_d_init = conv_cp_send_recv_bwd(d_initial_state, group) # [W-1, D]
 
180
  cp_context: FLACPContext | None,
181
  chunk_size: int | None,
182
  backend: str = 'triton',
183
+ residual: torch.Tensor | None = None,
184
+ ) -> torch.Tensor:
185
+ # import here to avoid circular dependency
186
+ from ...modules.causal_conv1d import causal_conv1d_fwd
187
 
188
  if cp_context is None:
189
  raise ValueError("cp_context must be provided for CausalConv1dFunctionCP")
 
191
  cu_seqlens_cpu = cp_context.cu_seqlens_cpu
192
  group = cp_context.group
193
 
194
+ # get kernel_size
195
  W = weight.shape[-1] # weight: [D, W]
196
+ # prepare initial_state for CP
197
  initial_state = CausalConv1dFunctionCP._prepare_initial_state_for_cp(
198
  x=x,
199
  weight=weight,
 
202
  group=group,
203
  )
204
 
205
+ ctx.save_for_backward(x, weight, bias, residual, initial_state)
206
  ctx.activation = activation
207
  ctx.cu_seqlens = cu_seqlens
208
  ctx.cu_seqlens_cpu = cu_seqlens_cpu
 
210
  ctx.chunk_size = chunk_size
211
  ctx.group = group
212
  ctx.W = W
213
+ ctx.cp_context = cp_context
 
214
 
215
+ # call original forward
216
  y, _ = causal_conv1d_fwd(
217
  x=x,
218
  weight=weight,
219
  bias=bias,
220
+ residual=residual,
221
  initial_state=initial_state,
222
  output_final_state=False,
223
  activation=activation,
224
  cu_seqlens=cu_seqlens,
225
  cu_seqlens_cpu=cu_seqlens_cpu,
226
  chunk_indices=chunk_indices,
227
+ chunk_size=chunk_size,
228
  )
229
 
230
  return y
231
 
232
  @staticmethod
233
+ def backward(ctx, dy: torch.Tensor) -> tuple[torch.Tensor | None, ...]:
234
+ # import here to avoid circular dependency
235
+ from ...modules.causal_conv1d import causal_conv1d_bwd
236
 
237
+ x, weight, bias, residual, initial_state = ctx.saved_tensors
238
  group = ctx.group
239
  W = ctx.W
240
 
241
+ # call original backward
242
+ dx, dw, db, dr, dh0 = causal_conv1d_bwd(
243
  x=x,
244
  dy=dy,
245
  dht=None,
246
  weight=weight,
247
  bias=bias,
248
+ residual=residual,
249
  initial_state=initial_state,
250
  activation=ctx.activation,
251
  cu_seqlens=ctx.cu_seqlens,
252
  cu_seqlens_cpu=ctx.cu_seqlens_cpu,
253
  chunk_indices=ctx.chunk_indices,
254
+ chunk_size=ctx.chunk_size,
255
  )
256
 
257
+ # correct dx gradients for CP
258
+ CausalConv1dFunctionCP._correct_dx_for_cp(dx=dx, dh0=dh0, W=W, group=group, cp_context=ctx.cp_context)
 
 
 
 
 
 
 
259
 
260
+ return dx, dw, db, None, None, None, None, None, dr
261
 
262
 
263
  def causal_conv1d_cp(
 
269
  cp_context: FLACPContext | None = None,
270
  chunk_size: int | None = None,
271
  backend: str = 'triton',
272
+ residual: torch.Tensor | None = None,
273
+ ) -> torch.Tensor:
274
+ """Apply causal convolution with cross-rank history and gradient exchange.
 
 
 
 
275
 
276
  Args:
277
+ x (torch.Tensor):
278
+ Local input of shape `[1, T, D]`.
279
+ weight (torch.Tensor):
280
+ Convolution weights of shape `[D, W]`.
281
+ bias (torch.Tensor, Optional):
282
+ Bias of shape `[D]`. Default: `None`.
283
+ activation (str, Optional):
284
+ Output activation: `None`, `silu`, or `swish`. Default: `None`.
285
+ chunk_indices (torch.Tensor, Optional):
286
+ Precomputed chunk indices for the Triton backend. Default: `None`.
287
+ cp_context (FLACPContext, Optional):
288
+ Required CP context with cumulative sequence lengths and convolution width. Default: `None`.
289
+ chunk_size (int, Optional):
290
+ Triton chunk size; `None` selects 64. Default: `None`.
291
+ backend (str, Optional):
292
+ Convolution backend: `triton`, `cuda`, or `mix`. `mix` retains the Triton CP path.
293
+ CUDA requires contiguous layout, width 2–4, and at least `W - 1` tokens per rank. Default: `'triton'`.
294
+ residual (torch.Tensor, Optional):
295
+ Residual added to the output, of shape `[1, T, D]`. Default: `None`.
296
+
297
+ Returns:
298
+ torch.Tensor:
299
+ Local output of shape `[1, T, D]`.
300
  """
301
  if cp_context is None:
302
  raise ValueError("cp_context must be provided for causal_conv1d_cp")
303
 
304
  assert cp_context.conv1d_kernel_size is not None, "conv1d_kernel_size must be provided for causal_conv1d_cp"
305
  assert cp_context.cu_seqlens is not None, "cu_seqlens must be provided for causal_conv1d_cp"
306
+ if backend == 'mix':
307
+ backend = 'triton'
308
+ if backend not in ('triton', 'cuda'):
309
+ raise ValueError(f"Unsupported CP convolution backend: {backend!r}; expected 'triton', 'cuda', or 'mix'")
310
+ if backend == 'cuda':
311
+ from ...modules.causal_conv1d.backends.cuda.cp import causal_conv1d_cuda_cp
312
+
313
+ return causal_conv1d_cuda_cp(
314
+ x=x,
315
+ weight=weight,
316
+ bias=bias,
317
+ residual=residual,
318
+ activation=activation,
319
+ cp_context=cp_context,
320
+ )
321
+
322
  chunk_size = chunk_size or 64
323
  if chunk_indices is None:
324
+ chunk_indices = prepare_chunk_indices(
325
+ cu_seqlens=cp_context.cu_seqlens,
326
+ chunk_size=chunk_size,
327
+ cu_seqlens_cpu=cp_context.cu_seqlens_cpu,
328
+ )
329
 
330
+ return CausalConv1dFunctionCP.apply(x, weight, bias, activation, chunk_indices, cp_context, chunk_size, backend, residual)
 
 
 
build/torch-cuda/modules/{conv/triton/kernels.py → causal_conv1d/ops.py} RENAMED
@@ -5,13 +5,27 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
 
 
 
8
  import torch
 
9
  import triton
10
  import triton.language as tl
11
  from einops import rearrange
12
 
13
- from ....ops.utils.cache import fla_cache_autotune
14
- from ....utils import IS_AMD, autotune_cache_kwargs, input_guard
 
 
 
 
 
 
 
 
 
 
15
 
16
  NUM_WARPS_AUTOTUNE = [2, 4, 8, 16] if IS_AMD else [4, 8, 16, 32]
17
  STATIC_WARPS = 32 if not IS_AMD else 16
@@ -30,7 +44,7 @@ STATIC_WARPS = 32 if not IS_AMD else 16
30
  for BD in [16, 32, 64, 128]
31
  for num_warps in NUM_WARPS_AUTOTUNE
32
  ],
33
- key=['D', 'W', 'NB'],
34
  **autotune_cache_kwargs,
35
  )
36
  @triton.jit
@@ -53,7 +67,6 @@ def causal_conv1d_fwd_kernel(
53
  BT: tl.constexpr,
54
  BW: tl.constexpr,
55
  BD: tl.constexpr,
56
- NB: tl.constexpr,
57
  ACTIVATION: tl.constexpr,
58
  HAS_WEIGHT: tl.constexpr,
59
  HAS_BIAS: tl.constexpr,
@@ -74,9 +87,11 @@ def causal_conv1d_fwd_kernel(
74
  p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
75
 
76
  o_d = i_d * BD + tl.arange(0, BD)
 
77
  o_w = tl.arange(0, BW) + W - BW
78
  m_d = o_d < D
79
  m_w = o_w >= 0
 
80
 
81
  if HAS_WEIGHT:
82
  # [BD, BW]
@@ -85,33 +100,31 @@ def causal_conv1d_fwd_kernel(
85
  b_y = tl.zeros((BT, BD), dtype=tl.float32)
86
  if not USE_INITIAL_STATE:
87
  for i_w in tl.static_range(-W + 1, 1):
88
- p_yi = tl.make_block_ptr(p_x, (T, D), (stride_x_t, stride_x_d), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
 
89
  # [BT, BD]
90
- b_yi = tl.load(p_yi, boundary_check=(0, 1)).to(tl.float32)
91
  if HAS_WEIGHT:
92
  b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
93
  b_y += b_yi
94
  elif i_t * BT >= W:
95
- # to make Triton compiler happy, we need to copy codes
96
  for i_w in tl.static_range(-W + 1, 1):
97
- p_yi = tl.make_block_ptr(p_x, (T, D), (stride_x_t, stride_x_d), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
 
98
  # [BT, BD]
99
- b_yi = tl.load(p_yi, boundary_check=(0, 1)).to(tl.float32)
100
  if HAS_WEIGHT:
101
  b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
102
  b_y += b_yi
103
  else:
104
- o_t = i_t * BT + tl.arange(0, BT)
105
  for i_w in tl.static_range(-W + 1, 1):
106
  o_x = o_t + i_w
107
  m_x = ((o_x >= 0) & (o_x < T))[:, None] & m_d
108
  m_c = ((o_x + W >= 0) & (o_x < 0))[:, None] & m_d
109
 
110
- b_yi = tl.load(
111
- p_x + o_x[:, None] * stride_x_t + o_d * stride_x_d,
112
- mask=m_x,
113
- other=0
114
- ).to(tl.float32)
115
 
116
  b_yi += tl.load(initial_state + i_n * D*W + o_d * W + (o_x + W)[:, None], mask=m_c, other=0).to(tl.float32)
117
 
@@ -126,12 +139,12 @@ def causal_conv1d_fwd_kernel(
126
  b_y = b_y * tl.sigmoid(b_y)
127
 
128
  if HAS_RESIDUAL:
129
- p_residual = tl.make_block_ptr(residual + bos * D, (T, D), (D, 1), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
130
- b_residual = tl.load(p_residual, boundary_check=(0, 1))
131
  b_y += b_residual
132
 
133
- p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
134
- tl.store(p_y, tl.cast(b_y, dtype=p_y.dtype.element_ty, fp_downcast_rounding='rtne'), boundary_check=(0, 1))
135
 
136
 
137
  @triton.heuristics({
@@ -145,9 +158,9 @@ def causal_conv1d_fwd_kernel(
145
  configs=[
146
  triton.Config({'BD': BD}, num_warps=num_warps)
147
  for BD in [16, 32, 64, 128]
148
- for num_warps in [4, 8, 16, 32]
149
  ],
150
- key=['D', 'W', 'NB'],
151
  **autotune_cache_kwargs,
152
  )
153
  @triton.jit
@@ -165,21 +178,20 @@ def causal_conv1d_bwd_kernel(
165
  chunk_indices,
166
  B,
167
  T,
168
- stride_x_n, # x batch stride
169
- stride_x_t, # x time stride
170
- stride_x_d, # x dim stride
171
- stride_dx_n, # dx batch stride
172
- stride_dx_t, # dx time stride
173
- stride_dx_d, # dx dim stride
174
- stride_dy_n, # dy batch stride
175
- stride_dy_t, # dy time stride
176
- stride_dy_d, # dy dim stride
177
  D: tl.constexpr,
178
  W: tl.constexpr,
179
  BT: tl.constexpr,
180
  BW: tl.constexpr,
181
  BD: tl.constexpr,
182
- NB: tl.constexpr,
183
  ACTIVATION: tl.constexpr,
184
  HAS_WEIGHT: tl.constexpr,
185
  HAS_BIAS: tl.constexpr,
@@ -206,13 +218,15 @@ def causal_conv1d_bwd_kernel(
206
  p_dy = dy + tl.cast(i_b, tl.int64) * stride_dy_n
207
 
208
  o_d = i_d * BD + tl.arange(0, BD)
 
209
  o_w = tl.arange(0, BW) + W - BW
210
  m_d = o_d < D
211
  m_w = o_w >= 0
 
212
 
213
  if HAS_WEIGHT:
214
- p_x = tl.make_block_ptr(p_x, (T, D), (stride_x_t, stride_x_d), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
215
- b_x = tl.load(p_x, boundary_check=(0, 1))
216
  # [BD, BW]
217
  b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0)
218
 
@@ -222,13 +236,13 @@ def causal_conv1d_bwd_kernel(
222
 
223
  if not USE_FINAL_STATE and not USE_INITIAL_STATE:
224
  for i_w in tl.static_range(0, W):
225
- p_dy_blk = tl.make_block_ptr(p_dy, (T, D), (stride_dy_t, stride_dy_d),
226
- (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
227
  # [BT, BD]
228
- b_dy = tl.load(p_dy_blk, boundary_check=(0, 1)).to(tl.float32)
229
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
230
- p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
231
- b_y = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
232
  b_ys = tl.sigmoid(b_y)
233
  b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
234
  b_wdy = b_dy
@@ -242,15 +256,15 @@ def causal_conv1d_bwd_kernel(
242
  b_db += tl.sum(b_dy, 0)
243
  b_dx += b_wdy
244
  elif i_t * BT >= W:
245
- # to make Triton compiler happy, we need to copy codes
246
  for i_w in tl.static_range(0, W):
247
- p_dy_blk = tl.make_block_ptr(p_dy, (T, D), (stride_dy_t, stride_dy_d),
248
- (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
249
  # [BT, BD]
250
- b_dy = tl.load(p_dy_blk, boundary_check=(0, 1)).to(tl.float32)
251
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
252
- p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
253
- b_y = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
254
  b_ys = tl.sigmoid(b_y)
255
  b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
256
  b_wdy = b_dy
@@ -264,38 +278,42 @@ def causal_conv1d_bwd_kernel(
264
  b_db += tl.sum(b_dy, 0)
265
  b_dx += b_wdy
266
  else:
267
- # which may use initial state
268
- o_t = i_t * BT + tl.arange(0, BT)
269
  for i_w in tl.static_range(0, W):
270
- p_dy_blk = tl.make_block_ptr(p_dy, (T, D), (stride_dy_t, stride_dy_d),
271
- (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
272
- b_dy_shift = tl.load(p_dy_blk, boundary_check=(0, 1)).to(tl.float32)
273
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
274
- p_y = tl.make_block_ptr(y + bos * D, (T, D), (D, 1), (i_t * BT + i_w, i_d * BD), (BT, BD), (1, 0))
275
- b_y_shift = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
276
  b_ys = tl.sigmoid(b_y_shift)
277
  b_dy_shift = b_dy_shift * b_ys * (1 + b_y_shift * (1 - b_ys))
278
  if HAS_WEIGHT:
279
- # gradient comes from x:sum_t dy[t+i_w] * x[t]
280
  b_dw = tl.sum(b_dy_shift * b_x, 0)
281
- # index of cache:c = W - i_w + t
282
  if USE_INITIAL_STATE:
283
  mask_head_rows = (o_t < i_w) & (o_t < T)
284
- # dy_head = dy[t]
285
- b_dy_head = tl.load(p_dy + o_t[:, None] * stride_dy_t + o_d * stride_dy_d, mask=(mask_head_rows[:, None] & m_d[None, :]),
286
- other=0.0).to(tl.float32)
 
 
287
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
288
- # use y[t] (not y[t+i_w])
289
- b_y_head = tl.load(y + bos * D + o_t[:, None] * D + o_d,
290
- mask=(mask_head_rows[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
 
 
 
291
  b_ys_head = tl.sigmoid(b_y_head)
292
  b_dy_head = b_dy_head * b_ys_head * (1 + b_y_head * (1 - b_ys_head))
293
  o_c = W - i_w + o_t
294
  # index 0 is padding 0
295
  mask_c = (mask_head_rows & (o_c >= 1) & (o_c < W))
296
- b_xc = tl.load(initial_state + i_n * D * W + o_d[None, :] * W + o_c[:, None],
297
- mask=(mask_c[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
298
- # add the gradient comes from initial_state
 
 
299
  b_dw += tl.sum(b_dy_head * b_xc, 0)
300
  tl.store(dw + i_tg * D * W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
301
 
@@ -312,9 +330,11 @@ def causal_conv1d_bwd_kernel(
312
  if i_t * BT + BT >= T-W:
313
  start_tok = max(0, T - (W - 1))
314
  offset = i_t * BT + tl.arange(0, BT)
315
- tok_idx = offset - start_tok
316
  mask = (offset >= start_tok) & (offset < T)
317
- w_idx = 1 + tok_idx
 
 
 
318
  dht_off = i_n * D * W + o_d[None, :] * W + w_idx[:, None]
319
  b_dht = tl.load(dht + dht_off, mask=mask[:, None] & m_d[None, :], other=0.).to(tl.float32)
320
  b_dx += b_dht
@@ -324,8 +344,8 @@ def causal_conv1d_bwd_kernel(
324
  else:
325
  p_dx = dx + tl.cast(i_b, tl.int64) * stride_dx_n
326
 
327
- p_dx = tl.make_block_ptr(p_dx, (T, D), (stride_dx_t, stride_dx_d), (i_t * BT, i_d * BD), (BT, BD), (1, 0))
328
- tl.store(p_dx, tl.cast(b_dx, dtype=p_dx.dtype.element_ty, fp_downcast_rounding='rtne'), boundary_check=(0, 1))
329
 
330
 
331
  @triton.heuristics({
@@ -352,10 +372,10 @@ def causal_conv1d_update_kernel(
352
  y,
353
  weight,
354
  bias,
355
- stride_x_n, # batch stride
356
- stride_x_d, # dim stride
357
- stride_y_n, # batch stride
358
- stride_y_d, # dim stride
359
  D: tl.constexpr,
360
  W: tl.constexpr,
361
  BD: tl.constexpr,
@@ -366,7 +386,9 @@ def causal_conv1d_update_kernel(
366
  HAS_BIAS: tl.constexpr,
367
  HAS_RESIDUAL: tl.constexpr,
368
  ):
369
- i_d, i_n = tl.program_id(0), tl.program_id(1)
 
 
370
 
371
  o_d = i_d * BD + tl.arange(0, BD)
372
  o_w = tl.arange(0, BW)
@@ -379,18 +401,9 @@ def causal_conv1d_update_kernel(
379
  b_cache = tl.zeros((BD, BW), dtype=tl.float32)
380
 
381
  if USE_INITIAL_STATE:
382
- # 2. Shift Cache (Read [1:])
383
- p_cache_read = tl.make_block_ptr(
384
- cache + i_n * D*W,
385
- shape=(D, W),
386
- strides=(W, 1),
387
- offsets=(i_d * BD, 1),
388
- block_shape=(BD, BW),
389
- order=(1, 0)
390
- )
391
- b_cache = tl.load(p_cache_read, boundary_check=(0, 1)).to(tl.float32)
392
 
393
- # 3. Fill x to the last position
394
  m_update = o_w == (W - 1)
395
  b_cache = tl.where(m_update[None, :], b_x[:, None], b_cache)
396
 
@@ -409,24 +422,24 @@ def causal_conv1d_update_kernel(
409
  if HAS_RESIDUAL:
410
  b_y += tl.load(residual + i_n * D + o_d, mask=m_d, other=0)
411
 
412
- tl.store(y + i_n * stride_y_n + o_d * stride_y_d, tl.cast(b_y,
413
- dtype=y.dtype.element_ty, fp_downcast_rounding='rtne'), mask=m_d)
 
 
 
414
 
415
  if USE_INITIAL_STATE:
416
- p_cache_write = tl.make_block_ptr(
417
- cache + i_n * D*W,
418
- shape=(D, W),
419
- strides=(W, 1),
420
- offsets=(i_d * BD, 0),
421
- block_shape=(BD, BW),
422
- order=(1, 0)
423
  )
424
- tl.store(p_cache_write, tl.cast(b_cache, dtype=cache.dtype.element_ty,
425
- fp_downcast_rounding='rtne'), boundary_check=(0, 1))
426
 
427
 
428
  @triton.heuristics({
429
  'USE_ACTIVATION': lambda args: args['y'] is not None,
 
430
  'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
431
  })
432
  @triton.jit
@@ -435,69 +448,75 @@ def compute_dh0_kernel(
435
  y,
436
  weight,
437
  dh0,
 
438
  cu_seqlens,
439
  stride_dy_n,
440
  stride_dy_t,
 
441
  T,
442
  D: tl.constexpr,
443
  W: tl.constexpr,
444
  BD: tl.constexpr,
445
  USE_ACTIVATION: tl.constexpr,
 
446
  IS_VARLEN: tl.constexpr,
447
  ):
448
  """
449
  Compute dh0 (gradient w.r.t. initial_state) in a separate kernel.
450
  This avoids Triton compiler bugs on some architectures (e.g., GB200).
451
 
452
- Grid: (cdiv(D, BD), N)
453
  """
454
- i_d, i_n = tl.program_id(0), tl.program_id(1)
 
 
455
 
456
- # Get sequence boundaries
457
  if IS_VARLEN:
458
  bos = tl.load(cu_seqlens + i_n).to(tl.int64)
459
  eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
460
  seq_len = eos - bos
461
- # For varlen, dy is [1, total_T, D], offset by bos
462
  dy_base = dy + bos * stride_dy_t
463
  else:
464
  seq_len = T
465
- # For non-varlen, dy is [B, T, D], offset by i_n * stride_dy_n
466
  dy_base = dy + tl.cast(i_n, tl.int64) * stride_dy_n
467
 
468
  o_d = i_d * BD + tl.arange(0, BD)
469
  m_d = o_d < D
470
 
471
- # For each i_w in [1, W), compute dh0[i_n, :, i_w]
472
  for i_w in tl.static_range(1, W):
473
  b_dh0 = tl.zeros([BD], dtype=tl.float32)
474
 
475
- # Accumulate contributions from t = 0 to min(i_w, seq_len) - 1
 
 
 
 
 
 
476
  for t in tl.static_range(0, W - 1):
477
  if t < i_w:
478
  w_idx = i_w - 1 - t
479
 
480
- # Load dy[t, :] relative to dy_base
481
- p_dy = dy_base + t * stride_dy_t + o_d
482
  m_t = (t < seq_len) & m_d
483
  b_dy = tl.load(p_dy, mask=m_t, other=0).to(tl.float32)
484
 
485
  if USE_ACTIVATION:
 
486
  if IS_VARLEN:
487
- p_y = y + bos * stride_dy_t + t * stride_dy_t + o_d
488
  else:
489
- p_y = y + tl.cast(i_n, tl.int64) * stride_dy_n + t * stride_dy_t + o_d
490
  b_y = tl.load(p_y, mask=m_t, other=0).to(tl.float32)
491
  b_ys = tl.sigmoid(b_y)
492
  b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
493
 
494
- # Get weight[:, w_idx]
495
  b_w_col = tl.load(weight + o_d * W + w_idx, mask=m_d, other=0).to(tl.float32)
496
 
497
- # Accumulate
498
  b_dh0 += tl.where(m_t, b_dy * b_w_col, 0)
499
 
500
- # Store dh0[i_n, :, i_w]
501
  p_dh0 = dh0 + i_n * D * W + o_d * W + i_w
502
  tl.store(p_dh0, b_dh0.to(dh0.dtype.element_ty), mask=m_d)
503
 
@@ -523,9 +542,11 @@ def causal_conv1d_states_fwd_kernel(
523
  USE_INITIAL_STATE: tl.constexpr,
524
  IS_VARLEN: tl.constexpr,
525
  ):
526
- i_d, i_n = tl.program_id(0), tl.program_id(1)
 
 
527
 
528
- # o_d Shape: [BD]
529
  o_d = i_d * BD + tl.arange(0, BD)
530
  m_d = o_d < D
531
 
@@ -538,10 +559,11 @@ def causal_conv1d_states_fwd_kernel(
538
  seq_len = T
539
  p_x = x + tl.cast(i_n, tl.int64) * stride_x_n
540
 
541
- p_x = tl.make_block_ptr(p_x, (seq_len, D), (stride_x_t, stride_x_d), (seq_len - BW, i_d * BD), (BW, BD), (1, 0))
 
542
 
543
- # b_x Shape: [BW, BD]
544
- b_x = tl.load(p_x, boundary_check=(0, 1), padding_option="zero").to(tl.float32)
545
 
546
  if USE_INITIAL_STATE:
547
  if seq_len < BW:
@@ -554,21 +576,245 @@ def causal_conv1d_states_fwd_kernel(
554
  b_cache = tl.load(p_init, mask=mask_init, other=0)
555
  b_x += b_cache
556
 
557
- # final_state: [N, D, W] (Channel Major inside sample)
558
- # o_w Shape: [BW]
559
  o_w = W - BW + tl.arange(0, BW)
560
 
561
  # o_d[:, None] -> [BD, 1]
562
  # o_w[None, :] -> [1, BW]
563
- # p_final Shape -> [BD, BW]
564
  p_final = final_state + tl.cast(i_n, tl.int64) * D*W + o_d[:, None] * W + o_w[None, :]
565
 
566
- # m_final Shape -> [BD, BW]
567
  m_final = m_d[:, None] & (o_w[None, :] >= 0)
568
 
569
  tl.store(p_final, tl.trans(b_x).to(final_state.dtype.element_ty), mask=m_final)
570
 
571
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  @input_guard(no_guard_contiguous=["x"])
573
  def causal_conv1d_update_states(
574
  x: torch.Tensor,
@@ -598,7 +844,7 @@ def causal_conv1d_update_states(
598
  BD = min(triton.next_power_of_2(D), 256)
599
  BW = triton.next_power_of_2(W)
600
 
601
- grid = (triton.cdiv(D, BD), N)
602
 
603
  causal_conv1d_states_fwd_kernel[grid](
604
  x=x,
@@ -611,12 +857,13 @@ def causal_conv1d_update_states(
611
  stride_x_n=stride_x_n,
612
  stride_x_t=stride_x_t,
613
  stride_x_d=stride_x_d,
614
- BW=BW,
615
  BD=BD,
 
616
  )
617
  return final_state
618
 
619
 
 
620
  @input_guard(no_guard_contiguous=["x"])
621
  def causal_conv1d_update(
622
  x: torch.Tensor,
@@ -636,21 +883,18 @@ def causal_conv1d_update(
636
  BW = triton.next_power_of_2(W)
637
 
638
  if x.dim() == 2:
639
- # Case: (N, D)
640
  stride_x_n = x.stride(0)
641
  stride_x_d = x.stride(1)
642
  elif x.dim() == 3 and x.shape[0] == 1:
643
- # Case: (1, N, D) -> Time=1, Batch=N, Dim=D
644
- # Batch 在 dim 1
645
  stride_x_n = x.stride(1)
646
  stride_x_d = x.stride(2)
647
  elif x.dim() == 3:
648
- # Case: (N, 1, D) -> Batch=N, Time=1, Dim=D
649
- # Batch 在 dim 0
650
  stride_x_n = x.stride(0)
651
  stride_x_d = x.stride(2)
652
  else:
653
- # Fallback / Error case
654
  raise ValueError(f"Unsupported input shape: {x.shape}")
655
 
656
  y = torch.empty_like(x, memory_format=torch.contiguous_format)
@@ -662,7 +906,9 @@ def causal_conv1d_update(
662
  elif y.dim() == 3:
663
  stride_y_n, stride_y_d = y.stride(0), y.stride(2)
664
 
665
- def grid(meta): return (triton.cdiv(D, meta['BD']), N)
 
 
666
 
667
  causal_conv1d_update_kernel[grid](
668
  x=x,
@@ -681,3 +927,398 @@ def causal_conv1d_update(
681
  ACTIVATION=activation,
682
  )
683
  return y.view(shape), cache
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ import os
9
+ import warnings
10
+
11
  import torch
12
+ import torch.nn as nn
13
  import triton
14
  import triton.language as tl
15
  from einops import rearrange
16
 
17
+ from ...backends import dispatch
18
+ from ...ops.cp import FLACPContext
19
+ from ...ops.utils import prepare_chunk_indices
20
+ from ...ops.utils.cache import fla_cache_autotune
21
+ from ...utils import IS_AMD, autotune_cache_kwargs, deprecate_kwarg, input_guard
22
+
23
+ try:
24
+ from causal_conv1d import causal_conv1d_fn as causal_conv1d_fn_cuda
25
+ from causal_conv1d import causal_conv1d_update as causal_conv1d_update_cuda
26
+ except ImportError:
27
+ causal_conv1d_fn_cuda = None
28
+ causal_conv1d_update_cuda = None
29
 
30
  NUM_WARPS_AUTOTUNE = [2, 4, 8, 16] if IS_AMD else [4, 8, 16, 32]
31
  STATIC_WARPS = 32 if not IS_AMD else 16
 
44
  for BD in [16, 32, 64, 128]
45
  for num_warps in NUM_WARPS_AUTOTUNE
46
  ],
47
+ key=['D', 'W'],
48
  **autotune_cache_kwargs,
49
  )
50
  @triton.jit
 
67
  BT: tl.constexpr,
68
  BW: tl.constexpr,
69
  BD: tl.constexpr,
 
70
  ACTIVATION: tl.constexpr,
71
  HAS_WEIGHT: tl.constexpr,
72
  HAS_BIAS: tl.constexpr,
 
87
  p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
88
 
89
  o_d = i_d * BD + tl.arange(0, BD)
90
+ o_t = i_t.to(tl.int64) * BT + tl.arange(0, BT)
91
  o_w = tl.arange(0, BW) + W - BW
92
  m_d = o_d < D
93
  m_w = o_w >= 0
94
+ m_y = (o_t < T)[:, None] & m_d[None, :]
95
 
96
  if HAS_WEIGHT:
97
  # [BD, BW]
 
100
  b_y = tl.zeros((BT, BD), dtype=tl.float32)
101
  if not USE_INITIAL_STATE:
102
  for i_w in tl.static_range(-W + 1, 1):
103
+ o_x = o_t + i_w
104
+ p_yi = p_x + o_x[:, None] * stride_x_t + o_d[None, :] * stride_x_d
105
  # [BT, BD]
106
+ b_yi = tl.load(p_yi, mask=((o_x >= 0) & (o_x < T))[:, None] & m_d[None, :], other=0.0).to(tl.float32)
107
  if HAS_WEIGHT:
108
  b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
109
  b_y += b_yi
110
  elif i_t * BT >= W:
111
+ # separate branches avoid a Triton compiler issue.
112
  for i_w in tl.static_range(-W + 1, 1):
113
+ o_x = o_t + i_w
114
+ p_yi = p_x + o_x[:, None] * stride_x_t + o_d[None, :] * stride_x_d
115
  # [BT, BD]
116
+ b_yi = tl.load(p_yi, mask=((o_x >= 0) & (o_x < T))[:, None] & m_d[None, :], other=0.0).to(tl.float32)
117
  if HAS_WEIGHT:
118
  b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
119
  b_y += b_yi
120
  else:
121
+ o_t = i_t.to(tl.int64) * BT + tl.arange(0, BT)
122
  for i_w in tl.static_range(-W + 1, 1):
123
  o_x = o_t + i_w
124
  m_x = ((o_x >= 0) & (o_x < T))[:, None] & m_d
125
  m_c = ((o_x + W >= 0) & (o_x < 0))[:, None] & m_d
126
 
127
+ b_yi = tl.load(p_x + o_x[:, None] * stride_x_t + o_d * stride_x_d, mask=m_x, other=0).to(tl.float32)
 
 
 
 
128
 
129
  b_yi += tl.load(initial_state + i_n * D*W + o_d * W + (o_x + W)[:, None], mask=m_c, other=0).to(tl.float32)
130
 
 
139
  b_y = b_y * tl.sigmoid(b_y)
140
 
141
  if HAS_RESIDUAL:
142
+ p_residual = residual + bos * D + o_t[:, None] * D + o_d[None, :]
143
+ b_residual = tl.load(p_residual, mask=m_y, other=0.0)
144
  b_y += b_residual
145
 
146
+ p_y = y + bos * D + o_t[:, None] * D + o_d[None, :]
147
+ tl.store(p_y, tl.cast(b_y, dtype=p_y.dtype.element_ty, fp_downcast_rounding='rtne'), mask=m_y)
148
 
149
 
150
  @triton.heuristics({
 
158
  configs=[
159
  triton.Config({'BD': BD}, num_warps=num_warps)
160
  for BD in [16, 32, 64, 128]
161
+ for num_warps in NUM_WARPS_AUTOTUNE
162
  ],
163
+ key=['D', 'W'],
164
  **autotune_cache_kwargs,
165
  )
166
  @triton.jit
 
178
  chunk_indices,
179
  B,
180
  T,
181
+ stride_x_n,
182
+ stride_x_t,
183
+ stride_x_d,
184
+ stride_dx_n,
185
+ stride_dx_t,
186
+ stride_dx_d,
187
+ stride_dy_n,
188
+ stride_dy_t,
189
+ stride_dy_d,
190
  D: tl.constexpr,
191
  W: tl.constexpr,
192
  BT: tl.constexpr,
193
  BW: tl.constexpr,
194
  BD: tl.constexpr,
 
195
  ACTIVATION: tl.constexpr,
196
  HAS_WEIGHT: tl.constexpr,
197
  HAS_BIAS: tl.constexpr,
 
218
  p_dy = dy + tl.cast(i_b, tl.int64) * stride_dy_n
219
 
220
  o_d = i_d * BD + tl.arange(0, BD)
221
+ o_t = i_t.to(tl.int64) * BT + tl.arange(0, BT)
222
  o_w = tl.arange(0, BW) + W - BW
223
  m_d = o_d < D
224
  m_w = o_w >= 0
225
+ m_x = (o_t < T)[:, None] & m_d[None, :]
226
 
227
  if HAS_WEIGHT:
228
+ p_x = p_x + o_t[:, None] * stride_x_t + o_d[None, :] * stride_x_d
229
+ b_x = tl.load(p_x, mask=m_x, other=0.0)
230
  # [BD, BW]
231
  b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0)
232
 
 
236
 
237
  if not USE_FINAL_STATE and not USE_INITIAL_STATE:
238
  for i_w in tl.static_range(0, W):
239
+ o_dy = o_t + i_w
240
+ p_dy_blk = p_dy + o_dy[:, None] * stride_dy_t + o_d[None, :] * stride_dy_d
241
  # [BT, BD]
242
+ b_dy = tl.load(p_dy_blk, mask=((o_dy < T)[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
243
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
244
+ p_y = y + bos * D + o_dy[:, None] * D + o_d[None, :]
245
+ b_y = tl.load(p_y, mask=((o_dy < T)[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
246
  b_ys = tl.sigmoid(b_y)
247
  b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
248
  b_wdy = b_dy
 
256
  b_db += tl.sum(b_dy, 0)
257
  b_dx += b_wdy
258
  elif i_t * BT >= W:
259
+ # separate branches avoid a Triton compiler issue.
260
  for i_w in tl.static_range(0, W):
261
+ o_dy = o_t + i_w
262
+ p_dy_blk = p_dy + o_dy[:, None] * stride_dy_t + o_d[None, :] * stride_dy_d
263
  # [BT, BD]
264
+ b_dy = tl.load(p_dy_blk, mask=((o_dy < T)[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
265
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
266
+ p_y = y + bos * D + o_dy[:, None] * D + o_d[None, :]
267
+ b_y = tl.load(p_y, mask=((o_dy < T)[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
268
  b_ys = tl.sigmoid(b_y)
269
  b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
270
  b_wdy = b_dy
 
278
  b_db += tl.sum(b_dy, 0)
279
  b_dx += b_wdy
280
  else:
281
+ o_t = i_t.to(tl.int64) * BT + tl.arange(0, BT)
 
282
  for i_w in tl.static_range(0, W):
283
+ o_dy = o_t + i_w
284
+ p_dy_blk = p_dy + o_dy[:, None] * stride_dy_t + o_d[None, :] * stride_dy_d
285
+ b_dy_shift = tl.load(p_dy_blk, mask=((o_dy < T)[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
286
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
287
+ p_y = y + bos * D + o_dy[:, None] * D + o_d[None, :]
288
+ b_y_shift = tl.load(p_y, mask=((o_dy < T)[:, None] & m_d[None, :]), other=0.0).to(tl.float32)
289
  b_ys = tl.sigmoid(b_y_shift)
290
  b_dy_shift = b_dy_shift * b_ys * (1 + b_y_shift * (1 - b_ys))
291
  if HAS_WEIGHT:
 
292
  b_dw = tl.sum(b_dy_shift * b_x, 0)
 
293
  if USE_INITIAL_STATE:
294
  mask_head_rows = (o_t < i_w) & (o_t < T)
295
+ b_dy_head = tl.load(
296
+ p_dy + o_t[:, None] * stride_dy_t + o_d * stride_dy_d,
297
+ mask=mask_head_rows[:, None] & m_d[None, :],
298
+ other=0.0,
299
+ ).to(tl.float32)
300
  if ACTIVATION == 'swish' or ACTIVATION == 'silu':
301
+ # cache gradients use the unshifted preactivation y[t].
302
+ b_y_head = tl.load(
303
+ y + bos * D + o_t[:, None] * D + o_d,
304
+ mask=mask_head_rows[:, None] & m_d[None, :],
305
+ other=0.0,
306
+ ).to(tl.float32)
307
  b_ys_head = tl.sigmoid(b_y_head)
308
  b_dy_head = b_dy_head * b_ys_head * (1 + b_y_head * (1 - b_ys_head))
309
  o_c = W - i_w + o_t
310
  # index 0 is padding 0
311
  mask_c = (mask_head_rows & (o_c >= 1) & (o_c < W))
312
+ b_xc = tl.load(
313
+ initial_state + i_n * D * W + o_d[None, :] * W + o_c[:, None],
314
+ mask=mask_c[:, None] & m_d[None, :],
315
+ other=0.0,
316
+ ).to(tl.float32)
317
  b_dw += tl.sum(b_dy_head * b_xc, 0)
318
  tl.store(dw + i_tg * D * W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
319
 
 
330
  if i_t * BT + BT >= T-W:
331
  start_tok = max(0, T - (W - 1))
332
  offset = i_t * BT + tl.arange(0, BT)
 
333
  mask = (offset >= start_tok) & (offset < T)
334
+ # state slot w is token T - W + w, so token t takes dht[:, t + W - T].
335
+ # start_tok is clamped, which only matters for the mask (offset >= 0 anyway),
336
+ # so the slot index has to come from the unclamped expression.
337
+ w_idx = offset + (W - T)
338
  dht_off = i_n * D * W + o_d[None, :] * W + w_idx[:, None]
339
  b_dht = tl.load(dht + dht_off, mask=mask[:, None] & m_d[None, :], other=0.).to(tl.float32)
340
  b_dx += b_dht
 
344
  else:
345
  p_dx = dx + tl.cast(i_b, tl.int64) * stride_dx_n
346
 
347
+ p_dx = p_dx + o_t[:, None] * stride_dx_t + o_d[None, :] * stride_dx_d
348
+ tl.store(p_dx, tl.cast(b_dx, dtype=p_dx.dtype.element_ty, fp_downcast_rounding='rtne'), mask=m_x)
349
 
350
 
351
  @triton.heuristics({
 
372
  y,
373
  weight,
374
  bias,
375
+ stride_x_n,
376
+ stride_x_d,
377
+ stride_y_n,
378
+ stride_y_d,
379
  D: tl.constexpr,
380
  W: tl.constexpr,
381
  BD: tl.constexpr,
 
386
  HAS_BIAS: tl.constexpr,
387
  HAS_RESIDUAL: tl.constexpr,
388
  ):
389
+ pid = tl.program_id(0)
390
+ ND = tl.cdiv(D, BD)
391
+ i_d, i_n = pid % ND, (pid // ND).to(tl.int64)
392
 
393
  o_d = i_d * BD + tl.arange(0, BD)
394
  o_w = tl.arange(0, BW)
 
401
  b_cache = tl.zeros((BD, BW), dtype=tl.float32)
402
 
403
  if USE_INITIAL_STATE:
404
+ p_cache_read = cache + i_n * D*W + o_d[:, None] * W + (o_w + 1)[None, :]
405
+ b_cache = tl.load(p_cache_read, mask=m_d[:, None] & ((o_w + 1) < W)[None, :], other=0.0).to(tl.float32)
 
 
 
 
 
 
 
 
406
 
 
407
  m_update = o_w == (W - 1)
408
  b_cache = tl.where(m_update[None, :], b_x[:, None], b_cache)
409
 
 
422
  if HAS_RESIDUAL:
423
  b_y += tl.load(residual + i_n * D + o_d, mask=m_d, other=0)
424
 
425
+ tl.store(
426
+ y + i_n * stride_y_n + o_d * stride_y_d,
427
+ tl.cast(b_y, dtype=y.dtype.element_ty, fp_downcast_rounding='rtne'),
428
+ mask=m_d,
429
+ )
430
 
431
  if USE_INITIAL_STATE:
432
+ p_cache_write = cache + i_n * D*W + o_d[:, None] * W + o_w[None, :]
433
+ tl.store(
434
+ p_cache_write,
435
+ tl.cast(b_cache, dtype=cache.dtype.element_ty, fp_downcast_rounding='rtne'),
436
+ mask=m_d[:, None] & m_w[None, :],
 
 
437
  )
 
 
438
 
439
 
440
  @triton.heuristics({
441
  'USE_ACTIVATION': lambda args: args['y'] is not None,
442
+ 'USE_FINAL_STATE': lambda args: args['dht'] is not None,
443
  'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
444
  })
445
  @triton.jit
 
448
  y,
449
  weight,
450
  dh0,
451
+ dht,
452
  cu_seqlens,
453
  stride_dy_n,
454
  stride_dy_t,
455
+ stride_dy_d,
456
  T,
457
  D: tl.constexpr,
458
  W: tl.constexpr,
459
  BD: tl.constexpr,
460
  USE_ACTIVATION: tl.constexpr,
461
+ USE_FINAL_STATE: tl.constexpr,
462
  IS_VARLEN: tl.constexpr,
463
  ):
464
  """
465
  Compute dh0 (gradient w.r.t. initial_state) in a separate kernel.
466
  This avoids Triton compiler bugs on some architectures (e.g., GB200).
467
 
468
+ Grid: (cdiv(D, BD) * N,)
469
  """
470
+ pid = tl.program_id(0)
471
+ ND = tl.cdiv(D, BD)
472
+ i_d, i_n = pid % ND, (pid // ND).to(tl.int64)
473
 
 
474
  if IS_VARLEN:
475
  bos = tl.load(cu_seqlens + i_n).to(tl.int64)
476
  eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
477
  seq_len = eos - bos
478
+ # for varlen, dy is [1, total_T, D], offset by bos
479
  dy_base = dy + bos * stride_dy_t
480
  else:
481
  seq_len = T
482
+ # for non-varlen, dy is [B, T, D], offset by i_n * stride_dy_n
483
  dy_base = dy + tl.cast(i_n, tl.int64) * stride_dy_n
484
 
485
  o_d = i_d * BD + tl.arange(0, BD)
486
  m_d = o_d < D
487
 
 
488
  for i_w in tl.static_range(1, W):
489
  b_dh0 = tl.zeros([BD], dtype=tl.float32)
490
 
491
+ if USE_FINAL_STATE:
492
+ # short sequences retain initial_state[:, i_w] in final_state[:, i_w - seq_len],
493
+ # so that slot's gradient passes straight through.
494
+ if i_w >= seq_len:
495
+ p_dht = dht + i_n * D * W + o_d * W + (i_w - seq_len)
496
+ b_dh0 += tl.load(p_dht, mask=m_d, other=0).to(tl.float32)
497
+
498
  for t in tl.static_range(0, W - 1):
499
  if t < i_w:
500
  w_idx = i_w - 1 - t
501
 
502
+ p_dy = dy_base + t * stride_dy_t + o_d * stride_dy_d
 
503
  m_t = (t < seq_len) & m_d
504
  b_dy = tl.load(p_dy, mask=m_t, other=0).to(tl.float32)
505
 
506
  if USE_ACTIVATION:
507
+ # `y` is recomputed contiguous [*, T, D]; only `dy` may carry other strides
508
  if IS_VARLEN:
509
+ p_y = y + bos * D + t * D + o_d
510
  else:
511
+ p_y = y + tl.cast(i_n, tl.int64) * T * D + t * D + o_d
512
  b_y = tl.load(p_y, mask=m_t, other=0).to(tl.float32)
513
  b_ys = tl.sigmoid(b_y)
514
  b_dy = b_dy * b_ys * (1 + b_y * (1 - b_ys))
515
 
 
516
  b_w_col = tl.load(weight + o_d * W + w_idx, mask=m_d, other=0).to(tl.float32)
517
 
 
518
  b_dh0 += tl.where(m_t, b_dy * b_w_col, 0)
519
 
 
520
  p_dh0 = dh0 + i_n * D * W + o_d * W + i_w
521
  tl.store(p_dh0, b_dh0.to(dh0.dtype.element_ty), mask=m_d)
522
 
 
542
  USE_INITIAL_STATE: tl.constexpr,
543
  IS_VARLEN: tl.constexpr,
544
  ):
545
+ pid = tl.program_id(0)
546
+ ND = tl.cdiv(D, BD)
547
+ i_d, i_n = pid % ND, (pid // ND).to(tl.int64)
548
 
549
+ # [BD]
550
  o_d = i_d * BD + tl.arange(0, BD)
551
  m_d = o_d < D
552
 
 
559
  seq_len = T
560
  p_x = x + tl.cast(i_n, tl.int64) * stride_x_n
561
 
562
+ o_x = tl.cast(seq_len, tl.int64) - BW + tl.arange(0, BW)
563
+ p_x = p_x + o_x[:, None] * stride_x_t + o_d[None, :] * stride_x_d
564
 
565
+ # [BW, BD]
566
+ b_x = tl.load(p_x, mask=((o_x >= 0) & (o_x < seq_len))[:, None] & m_d[None, :], other=0.0).to(tl.float32)
567
 
568
  if USE_INITIAL_STATE:
569
  if seq_len < BW:
 
576
  b_cache = tl.load(p_init, mask=mask_init, other=0)
577
  b_x += b_cache
578
 
579
+ # final_state: [N, D, W]
580
+ # [BW]
581
  o_w = W - BW + tl.arange(0, BW)
582
 
583
  # o_d[:, None] -> [BD, 1]
584
  # o_w[None, :] -> [1, BW]
585
+ # [BD, BW]
586
  p_final = final_state + tl.cast(i_n, tl.int64) * D*W + o_d[:, None] * W + o_w[None, :]
587
 
588
+ # [BD, BW]
589
  m_final = m_d[:, None] & (o_w[None, :] >= 0)
590
 
591
  tl.store(p_final, tl.trans(b_x).to(final_state.dtype.element_ty), mask=m_final)
592
 
593
 
594
+ def _has_non_standard_layout(x: torch.Tensor) -> bool:
595
+ """QKV-style views (stride_t != D) break triton-ascend masked kernels."""
596
+ if x.dtype not in (torch.float16, torch.bfloat16):
597
+ return False
598
+ if x.dim() != 3:
599
+ return not x.is_contiguous()
600
+ _, stride_t, stride_d = x.stride()
601
+ return stride_d == 1 and stride_t != x.shape[-1]
602
+
603
+
604
+ @deprecate_kwarg('BT', version='0.7.0', new_name='chunk_size')
605
+ @dispatch
606
+ @input_guard(no_guard_contiguous=["x"])
607
+ def causal_conv1d_fwd(
608
+ x: torch.Tensor,
609
+ weight: torch.Tensor,
610
+ bias: torch.Tensor,
611
+ residual: torch.Tensor,
612
+ initial_state: torch.Tensor | None = None,
613
+ output_final_state: bool = False,
614
+ activation: str | None = None,
615
+ cu_seqlens: torch.LongTensor | None = None,
616
+ cu_seqlens_cpu: torch.LongTensor | None = None,
617
+ chunk_indices: torch.LongTensor | None = None,
618
+ chunk_size: int = 64,
619
+ layout_fallback: bool = False,
620
+ ) -> torch.Tensor:
621
+ BT = chunk_size
622
+ shape = x.shape
623
+ if x.shape[-1] != weight.shape[0]:
624
+ x = rearrange(x, 'b t ... -> b t (...)')
625
+ B, T, D = x.shape[0], x.shape[1], weight.shape[0]
626
+ W = weight.shape[1]
627
+ stride_x_n, stride_x_t, stride_x_d = x.stride()
628
+
629
+ BW = triton.next_power_of_2(W)
630
+ if cu_seqlens is not None and chunk_indices is None:
631
+ chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
632
+ NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
633
+
634
+ y = torch.empty_like(x, memory_format=torch.contiguous_format)
635
+
636
+ def grid(meta) -> tuple[int, ...]:
637
+
638
+ return (triton.cdiv(D, meta['BD']), NT, B)
639
+ causal_conv1d_fwd_kernel[grid](
640
+ x=x,
641
+ y=y,
642
+ weight=weight,
643
+ bias=bias,
644
+ residual=residual,
645
+ cu_seqlens=cu_seqlens,
646
+ initial_state=initial_state,
647
+ chunk_indices=chunk_indices,
648
+ B=B,
649
+ T=T,
650
+ stride_x_n=stride_x_n,
651
+ stride_x_t=stride_x_t,
652
+ stride_x_d=stride_x_d,
653
+ D=D,
654
+ W=W,
655
+ BT=BT,
656
+ BW=BW,
657
+ ACTIVATION=activation,
658
+ )
659
+ final_state = None
660
+ if output_final_state:
661
+ final_state = causal_conv1d_update_states(x=x, state_len=W, initial_state=initial_state, cu_seqlens=cu_seqlens)
662
+ return y.view(shape), final_state
663
+
664
+
665
+ @dispatch
666
+ def compute_dh0_triton(
667
+ dy: torch.Tensor,
668
+ y: torch.Tensor | None,
669
+ weight: torch.Tensor,
670
+ initial_state: torch.Tensor,
671
+ activation: str | None,
672
+ cu_seqlens: torch.Tensor | None,
673
+ dht: torch.Tensor | None = None,
674
+ ) -> torch.Tensor:
675
+ """
676
+ Compute dh0 (gradient w.r.t. initial_state) using a separate Triton kernel.
677
+ This is a workaround for Triton compiler bugs on some architectures (e.g., GB200).
678
+ """
679
+ D, W = weight.shape
680
+ N = initial_state.shape[0]
681
+ T = dy.shape[1]
682
+
683
+ dh0 = torch.zeros_like(initial_state)
684
+
685
+ BD = 32
686
+ grid = (triton.cdiv(D, BD) * N,)
687
+
688
+ y_to_pass = y if activation in ('swish', 'silu') else None
689
+ # dy is [B, T, D] but may be a strided view; `y` is always contiguous
690
+ stride_dy_n, stride_dy_t, stride_dy_d = dy.stride()
691
+
692
+ compute_dh0_kernel[grid](
693
+ dy=dy,
694
+ y=y_to_pass,
695
+ weight=weight,
696
+ dh0=dh0,
697
+ dht=dht,
698
+ cu_seqlens=cu_seqlens,
699
+ stride_dy_n=stride_dy_n,
700
+ stride_dy_t=stride_dy_t,
701
+ stride_dy_d=stride_dy_d,
702
+ T=T,
703
+ D=D,
704
+ W=W,
705
+ BD=BD,
706
+ )
707
+
708
+ return dh0
709
+
710
+
711
+ @deprecate_kwarg('BT', version='0.7.0', new_name='chunk_size')
712
+ @dispatch
713
+ def causal_conv1d_bwd(
714
+ x: torch.Tensor,
715
+ dy: torch.Tensor,
716
+ dht: torch.Tensor,
717
+ weight: torch.Tensor | None = None,
718
+ bias: torch.Tensor | None = None,
719
+ residual: torch.Tensor | None = None,
720
+ initial_state: torch.Tensor | None = None,
721
+ activation: str | None = None,
722
+ cu_seqlens: torch.Tensor | None = None,
723
+ cu_seqlens_cpu: torch.LongTensor | None = None,
724
+ chunk_indices: torch.LongTensor | None = None,
725
+ chunk_size: int = 64,
726
+ layout_fallback: bool = False,
727
+ ) -> tuple[torch.Tensor, torch.Tensor | None, torch.Tensor | None, torch.Tensor | None, torch.Tensor | None]:
728
+ BT = chunk_size
729
+ shape = x.shape
730
+ if x.shape[-1] != weight.shape[0]:
731
+ x = rearrange(x, 'b t ... -> b t (...)')
732
+ B, T, D = x.shape
733
+ W = weight.shape[1] if weight is not None else None
734
+
735
+ stride_x_n, stride_x_t, stride_x_d = x.stride()
736
+ stride_dy_n, stride_dy_t, stride_dy_d = dy.stride()
737
+
738
+ BW = triton.next_power_of_2(W)
739
+ if cu_seqlens is not None and chunk_indices is None:
740
+ chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
741
+ NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
742
+
743
+ y = None
744
+ if activation is not None:
745
+ y, _ = causal_conv1d_fwd(
746
+ x=x,
747
+ weight=weight,
748
+ bias=bias,
749
+ residual=None,
750
+ initial_state=initial_state,
751
+ output_final_state=False,
752
+ activation=None,
753
+ cu_seqlens=cu_seqlens,
754
+ cu_seqlens_cpu=cu_seqlens_cpu,
755
+ chunk_indices=chunk_indices,
756
+ )
757
+ dx = torch.empty_like(x)
758
+ dw = weight.new_empty(B*NT, *weight.shape, dtype=torch.float) if weight is not None else None
759
+ db = bias.new_empty(B*NT, *bias.shape, dtype=torch.float) if bias is not None else None
760
+ dr = dy if residual is not None else None
761
+
762
+ stride_dx_n, stride_dx_t, stride_dx_d = dx.stride()
763
+
764
+ def grid(meta) -> tuple[int, ...]:
765
+
766
+ return (triton.cdiv(D, meta['BD']), NT, B)
767
+ causal_conv1d_bwd_kernel[grid](
768
+ x=x,
769
+ y=y,
770
+ weight=weight,
771
+ initial_state=initial_state,
772
+ dht=dht,
773
+ dy=dy,
774
+ dx=dx,
775
+ dw=dw,
776
+ db=db,
777
+ cu_seqlens=cu_seqlens,
778
+ chunk_indices=chunk_indices,
779
+ B=B,
780
+ T=T,
781
+ stride_x_n=stride_x_n,
782
+ stride_x_t=stride_x_t,
783
+ stride_x_d=stride_x_d,
784
+ stride_dx_n=stride_dx_n,
785
+ stride_dx_t=stride_dx_t,
786
+ stride_dx_d=stride_dx_d,
787
+ stride_dy_n=stride_dy_n,
788
+ stride_dy_t=stride_dy_t,
789
+ stride_dy_d=stride_dy_d,
790
+ D=D,
791
+ W=W,
792
+ BT=BT,
793
+ BW=BW,
794
+ ACTIVATION=activation,
795
+ )
796
+ if weight is not None:
797
+ dw = dw.sum(0).to(weight)
798
+ if bias is not None:
799
+ db = db.sum(0).to(bias)
800
+
801
+ # compute dh0 using separate Triton kernel to avoid compiler bugs on some architectures (e.g., GB200)
802
+ dh0 = None
803
+ if initial_state is not None:
804
+ dh0 = compute_dh0_triton(
805
+ dy=dy,
806
+ y=y,
807
+ weight=weight,
808
+ initial_state=initial_state,
809
+ activation=activation,
810
+ cu_seqlens=cu_seqlens,
811
+ dht=dht,
812
+ )
813
+
814
+ return dx.view(shape), dw, db, dr, dh0
815
+
816
+
817
+ @dispatch
818
  @input_guard(no_guard_contiguous=["x"])
819
  def causal_conv1d_update_states(
820
  x: torch.Tensor,
 
844
  BD = min(triton.next_power_of_2(D), 256)
845
  BW = triton.next_power_of_2(W)
846
 
847
+ grid = (triton.cdiv(D, BD) * N,)
848
 
849
  causal_conv1d_states_fwd_kernel[grid](
850
  x=x,
 
857
  stride_x_n=stride_x_n,
858
  stride_x_t=stride_x_t,
859
  stride_x_d=stride_x_d,
 
860
  BD=BD,
861
+ BW=BW,
862
  )
863
  return final_state
864
 
865
 
866
+ @dispatch
867
  @input_guard(no_guard_contiguous=["x"])
868
  def causal_conv1d_update(
869
  x: torch.Tensor,
 
883
  BW = triton.next_power_of_2(W)
884
 
885
  if x.dim() == 2:
886
+ # case: (N, D)
887
  stride_x_n = x.stride(0)
888
  stride_x_d = x.stride(1)
889
  elif x.dim() == 3 and x.shape[0] == 1:
890
+ # [1, N, D]: one token per sequence
 
891
  stride_x_n = x.stride(1)
892
  stride_x_d = x.stride(2)
893
  elif x.dim() == 3:
894
+ # [N, 1, D]: one token per sequence
 
895
  stride_x_n = x.stride(0)
896
  stride_x_d = x.stride(2)
897
  else:
 
898
  raise ValueError(f"Unsupported input shape: {x.shape}")
899
 
900
  y = torch.empty_like(x, memory_format=torch.contiguous_format)
 
906
  elif y.dim() == 3:
907
  stride_y_n, stride_y_d = y.stride(0), y.stride(2)
908
 
909
+ def grid(meta) -> tuple[int, ...]:
910
+
911
+ return (triton.cdiv(D, meta['BD']) * N,)
912
 
913
  causal_conv1d_update_kernel[grid](
914
  x=x,
 
927
  ACTIVATION=activation,
928
  )
929
  return y.view(shape), cache
930
+
931
+
932
+ class CausalConv1dFunction(torch.autograd.Function):
933
+
934
+ @staticmethod
935
+ @input_guard(no_guard_contiguous=["x"])
936
+ def forward(
937
+ ctx,
938
+ x: torch.Tensor,
939
+ weight: torch.Tensor | None = None,
940
+ bias: torch.Tensor | None = None,
941
+ residual: torch.Tensor | None = None,
942
+ initial_state: torch.Tensor | None = None,
943
+ output_final_state: bool | None = False,
944
+ activation: str | None = None,
945
+ cu_seqlens: torch.Tensor | None = None,
946
+ cu_seqlens_cpu: torch.LongTensor | None = None,
947
+ chunk_indices: torch.LongTensor | None = None,
948
+ chunk_size: int = 64,
949
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
950
+ if cu_seqlens is not None and chunk_indices is None:
951
+ chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size, cu_seqlens_cpu=cu_seqlens_cpu)
952
+ ctx.activation = activation
953
+ ctx.cu_seqlens = cu_seqlens
954
+ ctx.cu_seqlens_cpu = cu_seqlens_cpu
955
+ ctx.chunk_indices = chunk_indices
956
+ ctx.layout_fallback = _has_non_standard_layout(x=x)
957
+ ctx.save_for_backward(x, weight, bias, residual, initial_state)
958
+ y, final_state = causal_conv1d_fwd(
959
+ x=x,
960
+ weight=weight,
961
+ bias=bias,
962
+ residual=residual,
963
+ initial_state=initial_state,
964
+ output_final_state=output_final_state,
965
+ activation=activation,
966
+ cu_seqlens=cu_seqlens,
967
+ cu_seqlens_cpu=cu_seqlens_cpu,
968
+ chunk_indices=chunk_indices,
969
+ chunk_size=chunk_size,
970
+ layout_fallback=ctx.layout_fallback,
971
+ )
972
+ return y, final_state
973
+
974
+ @staticmethod
975
+ @input_guard(no_guard_contiguous=["dy"])
976
+ def backward(ctx, dy: torch.Tensor, dht: torch.Tensor | None = None) -> tuple[torch.Tensor | None, ...]:
977
+ x, weight, bias, residual, initial_state = ctx.saved_tensors
978
+ dx, dw, db, dr, dh0 = causal_conv1d_bwd(
979
+ x=x,
980
+ dy=dy,
981
+ dht=dht,
982
+ weight=weight,
983
+ bias=bias,
984
+ residual=residual,
985
+ initial_state=initial_state,
986
+ activation=ctx.activation,
987
+ cu_seqlens=ctx.cu_seqlens,
988
+ cu_seqlens_cpu=ctx.cu_seqlens_cpu,
989
+ chunk_indices=ctx.chunk_indices,
990
+ layout_fallback=ctx.layout_fallback,
991
+ )
992
+ return dx, dw, db, dr, dh0, None, None, None, None, None, None
993
+
994
+
995
+ @input_guard(no_guard_contiguous=["x"])
996
+ def causal_conv1d(
997
+ x: torch.Tensor,
998
+ weight: torch.Tensor | None = None,
999
+ bias: torch.Tensor | None = None,
1000
+ residual: torch.Tensor | None = None,
1001
+ initial_state: torch.Tensor | None = None,
1002
+ output_final_state: bool | None = False,
1003
+ activation: str | None = None,
1004
+ backend: str | None = 'triton',
1005
+ cu_seqlens: torch.Tensor | None = None,
1006
+ cu_seqlens_cpu: torch.LongTensor | None = None,
1007
+ chunk_indices: torch.LongTensor | None = None,
1008
+ cp_context: FLACPContext | None = None,
1009
+ **kwargs,
1010
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
1011
+ """
1012
+ A causal 1D convolution implementation that powers Mamba/Mamba2 and DeltaNet architectures.
1013
+
1014
+ With a residual connection, this implements the Canon operation:
1015
+ https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5240330.
1016
+
1017
+ Args:
1018
+ x (torch.Tensor):
1019
+ Input tensor of shape [B, T, D].
1020
+ weight (torch.Tensor, Optional):
1021
+ Weight tensor of shape [D, W]. Default: `None`.
1022
+ bias (torch.Tensor, Optional):
1023
+ Bias tensor of shape [D]. Default: `None`.
1024
+ residual (torch.Tensor, Optional):
1025
+ Residual tensor of shape [B, T, D]. Default: `None`.
1026
+ initial_state (torch.Tensor, Optional):
1027
+ Initial state tensor of shape [N, D, W],
1028
+ where `N` is the number of sequences in the batch and `W` is the kernel size.
1029
+ If provided, the initial state is used to initialize the cache. Default: `None`.
1030
+ output_final_state (bool, Optional):
1031
+ Whether to output the final state of shape [N, D, W]. Default: `False`.
1032
+ activation (str, Optional):
1033
+ Activations applied to output, only `swish`/`silu` or `None` (i.e., no activation) are supported.
1034
+ Default: `None`.
1035
+ backend (str, Optional):
1036
+ Specifies the backend to use for the convolution operation. Supported values are `'cuda'`, `'triton'`, and `'mix'`.
1037
+ Default: `'triton'`.
1038
+ cu_seqlens (torch.Tensor, Optional):
1039
+ Cumulative sequence lengths. Default: `None`.
1040
+ cu_seqlens_cpu (torch.LongTensor, Optional):
1041
+ CPU copy of the cumulative sequence lengths. Default: `None`.
1042
+ chunk_indices (torch.Tensor, Optional):
1043
+ Chunk indices for variable-length sequences. Default: `None`.
1044
+ cp_context (FLACPContext, Optional):
1045
+ Context-parallel sequence metadata. CP does not support initial or final states.
1046
+ CUDA CP requires contiguous layout, width 2–4, and at least `W - 1` tokens per rank.
1047
+ The `mix` backend uses Triton in CP mode. Default: `None`.
1048
+
1049
+ Returns:
1050
+ tuple[torch.Tensor, torch.Tensor | None]:
1051
+ Output and final state. If `output_final_state` is `False`, the final state is `None`.
1052
+ """
1053
+ # import here to avoid circular dependencies
1054
+ from ...modules.causal_conv1d.backends.cuda import causal_conv1d_cuda, fast_causal_conv1d_fn
1055
+ from ...modules.causal_conv1d.cp import causal_conv1d_cp
1056
+
1057
+ if cp_context is not None:
1058
+ assert initial_state is None, "Initial state is not supported for CP"
1059
+ assert output_final_state is False, "Output final state is not supported for CP"
1060
+ output = causal_conv1d_cp(
1061
+ x=x,
1062
+ weight=weight,
1063
+ bias=bias,
1064
+ activation=activation,
1065
+ chunk_indices=chunk_indices,
1066
+ cp_context=cp_context,
1067
+ backend=backend,
1068
+ residual=residual,
1069
+ )
1070
+ return output, None
1071
+
1072
+ if backend == 'triton':
1073
+ y, final_state = CausalConv1dFunction.apply(
1074
+ x,
1075
+ weight,
1076
+ bias,
1077
+ residual,
1078
+ initial_state,
1079
+ output_final_state,
1080
+ activation,
1081
+ cu_seqlens,
1082
+ cu_seqlens_cpu,
1083
+ chunk_indices,
1084
+ )
1085
+ return y, final_state
1086
+ elif backend == 'mix':
1087
+ seq_idx = kwargs.get('seq_idx')
1088
+ return fast_causal_conv1d_fn(
1089
+ x=x,
1090
+ weight=weight,
1091
+ bias=bias,
1092
+ residual=residual,
1093
+ initial_state=initial_state,
1094
+ output_final_state=output_final_state,
1095
+ activation=activation,
1096
+ cu_seqlens=cu_seqlens,
1097
+ cu_seqlens_cpu=cu_seqlens_cpu,
1098
+ chunk_indices=chunk_indices,
1099
+ seq_idx=seq_idx,
1100
+ )
1101
+ elif backend == 'cuda':
1102
+ return causal_conv1d_cuda(
1103
+ x=x,
1104
+ weight=weight,
1105
+ bias=bias,
1106
+ residual=residual,
1107
+ initial_state=initial_state,
1108
+ output_final_state=output_final_state,
1109
+ activation=activation,
1110
+ cu_seqlens=cu_seqlens,
1111
+ cu_seqlens_cpu=cu_seqlens_cpu,
1112
+ **kwargs,
1113
+ )
1114
+ else:
1115
+ raise ValueError(f"Unsupported backend: {backend}")
1116
+
1117
+
1118
+ class ShortConvolution(nn.Conv1d):
1119
+ """Depthwise causal convolution with optional activation and state caching.
1120
+
1121
+ Args:
1122
+ hidden_size (int):
1123
+ Number of input and output channels.
1124
+ kernel_size (int):
1125
+ Width of the convolution kernel.
1126
+ bias (bool, Optional):
1127
+ Whether to use a channel bias. Default: `False`.
1128
+ activation (str, Optional):
1129
+ Activation applied to the convolution output. Default: `"silu"`.
1130
+ backend (str, Optional):
1131
+ Backend selected unless overridden by `FLA_CONV_BACKEND`. Default: `"triton"`.
1132
+ device (torch.device, Optional):
1133
+ Device for the parameters. Default: `None`.
1134
+ dtype (torch.dtype, Optional):
1135
+ Parameter dtype. Default: `None`.
1136
+ **kwargs:
1137
+ Accepts the deprecated `use_fast_conv1d` option.
1138
+ """
1139
+
1140
+ def __init__(
1141
+ self,
1142
+ hidden_size: int,
1143
+ kernel_size: int,
1144
+ bias: bool = False,
1145
+ activation: str | None = 'silu',
1146
+ backend: str | None = 'triton',
1147
+ device: torch.device | None = None,
1148
+ dtype: torch.dtype | None = None,
1149
+ **kwargs,
1150
+ ) -> None:
1151
+ super().__init__(
1152
+ in_channels=hidden_size,
1153
+ out_channels=hidden_size,
1154
+ kernel_size=kernel_size,
1155
+ padding=kernel_size - 1,
1156
+ groups=hidden_size,
1157
+ bias=bias,
1158
+ device=device,
1159
+ dtype=dtype,
1160
+ )
1161
+
1162
+ self.hidden_size = hidden_size
1163
+ self.activation = None
1164
+
1165
+ if activation is not None:
1166
+ assert activation in ['silu', 'swish'], f"Activation `{activation}` not supported yet."
1167
+ self.activation = activation
1168
+
1169
+ if 'use_fast_conv1d' in kwargs:
1170
+ warnings.warn(
1171
+ "The `use_fast_conv1d` parameter is deprecated and will be ignored. "
1172
+ "Please use the `backend` parameter instead.",
1173
+ )
1174
+ self.backend = os.environ.get('FLA_CONV_BACKEND', backend)
1175
+ if self.backend not in ['cuda', 'triton']:
1176
+ raise ValueError(f"Invalid backend: {self.backend}, must be one of ['cuda', 'triton']")
1177
+ if self.backend == 'cuda':
1178
+ if causal_conv1d_fn_cuda is None:
1179
+ warnings.warn(
1180
+ "The `backend` parameter is set to `cuda`, but `causal_conv1d_fn` is not available. "
1181
+ "Switching to the Triton implementation instead. "
1182
+ "Consider installing `causal_conv1d` to enable the CUDA backend.",
1183
+ )
1184
+ self.backend = 'triton'
1185
+
1186
+ def extra_repr(self) -> str:
1187
+ s = '{in_channels}, {out_channels}, kernel_size={kernel_size}, stride={stride}'
1188
+ if self.padding != (0,) * len(self.padding):
1189
+ s += ', padding={padding}'
1190
+ if self.dilation != (1,) * len(self.dilation):
1191
+ s += ', dilation={dilation}'
1192
+ if self.output_padding != (0,) * len(self.output_padding):
1193
+ s += ', output_padding={output_padding}'
1194
+ if self.groups != 1:
1195
+ s += ', groups={groups}'
1196
+ if self.bias is None:
1197
+ s += ', bias=False'
1198
+ if self.padding_mode != 'zeros':
1199
+ s += ', padding_mode={padding_mode}'
1200
+ if self.activation is not None:
1201
+ s += ', activation={activation}'
1202
+ s += f', backend={self.backend}'
1203
+ return s.format(**self.__dict__)
1204
+
1205
+ def forward(
1206
+ self,
1207
+ x: torch.Tensor,
1208
+ residual: torch.Tensor | None = None,
1209
+ mask: torch.Tensor | None = None,
1210
+ cache: torch.Tensor | None = None,
1211
+ output_final_state: bool = False,
1212
+ cu_seqlens: torch.LongTensor | None = None,
1213
+ chunk_indices: torch.LongTensor | None = None,
1214
+ **kwargs,
1215
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
1216
+ """
1217
+ Args:
1218
+ x (torch.Tensor):
1219
+ Tensor of shape `[B, T, D]`. `B` must be 1 if `cu_seqlens` is provided.
1220
+ residual (torch.Tensor, Optional):
1221
+ Residual tensor of shape `[B, T, D]`. Default: `None`.
1222
+ mask (torch.Tensor, Optional):
1223
+ Attention mask for padded positions. Default: `None`.
1224
+ cache (torch.Tensor, Optional):
1225
+ Previous cache of shape `[N, D, W]`, where `W` is the kernel size. Default: `None`.
1226
+ Single-token calls without autograd update it **inplace**.
1227
+ Other calls return the updated state when `output_final_state=True`.
1228
+ output_final_state (bool, Optional):
1229
+ Whether to output the final state of shape `[N, D, W]`. Default: `False`.
1230
+ cu_seqlens (torch.Tensor, Optional):
1231
+ Cumulative sequence lengths of shape `[N+1]`. Default: `None`.
1232
+ chunk_indices (torch.Tensor, Optional):
1233
+ Chunk indices for variable-length sequences. Default: `None`.
1234
+
1235
+ Returns:
1236
+ y (torch.Tensor):
1237
+ Convolution output of shape `[B, T, D]`.
1238
+ final_state (torch.Tensor, Optional):
1239
+ Cache of shape `[N, D, W]` when requested, otherwise `None`.
1240
+ """
1241
+ B, T, *_ = x.shape
1242
+ N = B if cu_seqlens is None else len(cu_seqlens) - 1
1243
+ if mask is not None:
1244
+ if cu_seqlens is not None:
1245
+ raise ValueError("`mask` and `cu_seqlens` cannot be provided at the same time")
1246
+ x = x.mul_(mask.unsqueeze(-1))
1247
+
1248
+ # the in-place decode kernel requires autograd off and exactly one token per sequence, including packed inputs.
1249
+ if not torch.is_grad_enabled() and B * T == N and (cu_seqlens is None or bool((cu_seqlens.diff() == 1).all())):
1250
+ y, cache = self.step(
1251
+ x=x,
1252
+ residual=residual,
1253
+ cache=cache,
1254
+ output_final_state=output_final_state,
1255
+ cu_seqlens=cu_seqlens,
1256
+ )
1257
+ return y, cache
1258
+
1259
+ # use Triton for stateful calls unsupported by the CUDA backend.
1260
+ if self.backend == 'cuda' and cache is not None:
1261
+ warnings.warn(
1262
+ "The CUDA backend does not support both `cu_seqlens` and `cache` being provided, "
1263
+ "or both `cu_seqlens` and `output_final_state` being provided. "
1264
+ "Switching to the Triton backend instead. ",
1265
+ stacklevel=2,
1266
+ )
1267
+ self.backend = 'triton'
1268
+
1269
+ return causal_conv1d(
1270
+ x=x,
1271
+ weight=rearrange(self.weight, "d 1 w -> d w"),
1272
+ bias=self.bias,
1273
+ residual=residual,
1274
+ initial_state=cache,
1275
+ output_final_state=output_final_state,
1276
+ activation=self.activation,
1277
+ backend=self.backend,
1278
+ cu_seqlens=cu_seqlens,
1279
+ chunk_indices=chunk_indices,
1280
+ **kwargs,
1281
+ )
1282
+
1283
+ def step(
1284
+ self,
1285
+ x: torch.Tensor,
1286
+ residual: torch.Tensor | None,
1287
+ cache: torch.Tensor | None,
1288
+ output_final_state: bool = False,
1289
+ cu_seqlens: torch.LongTensor | None = None,
1290
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
1291
+ B, _, D, W = *x.shape, self.kernel_size[0]
1292
+ N = B if cu_seqlens is None else len(cu_seqlens) - 1
1293
+ # the update kernel requires a cache even when the final state is not requested.
1294
+ if cache is None:
1295
+ cache = x.new_zeros(N, D, W)
1296
+ if self.backend == 'triton':
1297
+ y, cache = causal_conv1d_update(
1298
+ x=x,
1299
+ cache=cache,
1300
+ residual=residual,
1301
+ weight=rearrange(self.weight, "d 1 w -> d w"),
1302
+ bias=self.bias,
1303
+ activation=self.activation,
1304
+ )
1305
+ return y, (cache if output_final_state else None)
1306
+
1307
+ shape = x.shape
1308
+ x = x.squeeze(0) if cu_seqlens is not None else x.squeeze(1)
1309
+ # the CUDA update mutates cache in place.
1310
+ y = causal_conv1d_update_cuda(
1311
+ x=x,
1312
+ conv_state=cache,
1313
+ weight=rearrange(self.weight, "d 1 w -> d w"),
1314
+ bias=self.bias,
1315
+ activation=self.activation,
1316
+ )
1317
+ y = y.view(shape)
1318
+ if residual is not None:
1319
+ y.add_(residual)
1320
+ return y, (cache if output_final_state else None)
1321
+
1322
+ @property
1323
+ def state_size(self) -> int:
1324
+ return self.hidden_size * self.kernel_size[0]
build/torch-cuda/modules/conv/causal_conv1d.py DELETED
@@ -1,129 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Main interface for causal 1D convolution operations."""
9
-
10
- import torch
11
-
12
- from ...ops.cp import FLACPContext
13
- from ...utils import input_guard
14
-
15
-
16
- @input_guard(no_guard_contiguous=["x"])
17
- def causal_conv1d(
18
- x: torch.Tensor,
19
- weight: torch.Tensor | None = None,
20
- bias: torch.Tensor | None = None,
21
- residual: torch.Tensor | None = None,
22
- initial_state: torch.Tensor | None = None,
23
- output_final_state: bool | None = False,
24
- activation: str | None = None,
25
- backend: str | None = 'triton',
26
- cu_seqlens: torch.Tensor | None = None,
27
- cu_seqlens_cpu: torch.LongTensor | None = None,
28
- chunk_indices: torch.LongTensor | None = None,
29
- cp_context: FLACPContext | None = None,
30
- **kwargs,
31
- ):
32
- """
33
- A causal 1D convolution implementation that powers Mamba/Mamba2 and DeltaNet architectures.
34
-
35
- When a residual connection is provided, this implements the Canon operation
36
- described in the paper at https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5240330.
37
-
38
- Args:
39
- x (torch.Tensor):
40
- Input tensor of shape [B, T, D].
41
- weight (Optional[torch.Tensor]):
42
- Weight tensor of shape [D, W]. Default: `None`.
43
- bias (Optional[torch.Tensor]):
44
- Bias tensor of shape [D]. Default: `None`.
45
- residual (Optional[torch.Tensor]):
46
- Residual tensor of shape [B, T, D]. Default: `None`.
47
- initial_state (Optional[torch.Tensor]):
48
- Initial state tensor of shape [N, D, W],
49
- where `N` is the number of sequences in the batch and `W` is the kernel size.
50
- If provided, the initial state is used to initialize the cache. Default: `None`.
51
- output_final_state (Optional[bool]):
52
- Whether to output the final state of shape [N, D, W]. Default: `False`.
53
- activation (Optional[str]):
54
- Activations applied to output, only `swish`/`silu` or `None` (i.e., no activation) are supported.
55
- Default: `None`.
56
- backend (Optional[str]):
57
- Specifies the backend to use for the convolution operation. Supported values are `'cuda'` 、 `'triton'` and `'mix'`.
58
- Default: `'triton'`.
59
- cu_seqlens (Optional[torch.Tensor]):
60
- Cumulative sequence lengths (optional)
61
- chunk_indices (Optional[torch.LongTensor]):
62
- Chunk indices for variable-length sequences (optional)
63
-
64
- Returns:
65
- Tuple of (output, final_state).
66
- If `output_final_state` is `False`, the final state is `None`.
67
- """
68
- # Import here to avoid circular dependencies
69
- from ...modules.conv.cp import causal_conv1d_cp
70
- from ...modules.conv.cuda import causal_conv1d_cuda, fast_causal_conv1d_fn
71
- from ...modules.conv.triton import CausalConv1dFunction
72
-
73
- if cp_context is not None:
74
- assert initial_state is None, "Initial state is not supported for CP"
75
- assert output_final_state is False, "Output final state is not supported for CP"
76
- output = causal_conv1d_cp(
77
- x=x,
78
- weight=weight,
79
- bias=bias,
80
- activation=activation,
81
- chunk_indices=chunk_indices,
82
- cp_context=cp_context,
83
- )
84
- return output, None
85
-
86
- if backend == 'triton':
87
- y, final_state = CausalConv1dFunction.apply(
88
- x,
89
- weight,
90
- bias,
91
- residual,
92
- initial_state,
93
- output_final_state,
94
- activation,
95
- cu_seqlens,
96
- cu_seqlens_cpu,
97
- chunk_indices,
98
- )
99
- return y, final_state
100
- elif backend == 'mix':
101
- seq_idx = kwargs.get('seq_idx')
102
- return fast_causal_conv1d_fn(
103
- x,
104
- weight,
105
- bias,
106
- residual,
107
- initial_state,
108
- output_final_state,
109
- activation,
110
- cu_seqlens,
111
- cu_seqlens_cpu=cu_seqlens_cpu,
112
- chunk_indices=chunk_indices,
113
- seq_idx=seq_idx,
114
- )
115
- elif backend == 'cuda':
116
- return causal_conv1d_cuda(
117
- x,
118
- weight,
119
- bias,
120
- residual,
121
- initial_state,
122
- output_final_state,
123
- activation,
124
- cu_seqlens,
125
- cu_seqlens_cpu=cu_seqlens_cpu,
126
- **kwargs,
127
- )
128
- else:
129
- raise ValueError(f"Unsupported backend: {backend}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/conv/cuda/ops.py DELETED
@@ -1,233 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """CUDA-based mixed-mode implementation for causal convolution."""
9
-
10
- import torch
11
- from einops import rearrange
12
-
13
- from ....modules.conv.triton import causal_conv1d_update_states
14
- from ....ops.utils import prepare_sequence_ids
15
- from ....utils import input_guard
16
-
17
- try:
18
- from causal_conv1d.cpp_functions import causal_conv1d_bwd_function
19
- except ImportError:
20
- causal_conv1d_bwd_function = None
21
-
22
- try:
23
- from causal_conv1d import causal_conv1d_fn as causal_conv1d_fn_cuda
24
- except ImportError:
25
- causal_conv1d_fn_cuda = None
26
-
27
-
28
- class FastCausalConv1dFn(torch.autograd.Function):
29
- """
30
- Mixed-mode (Mix) Causal Convolution Implementation - Combining Triton Forward and CUDA Backward Propagation
31
-
32
- This class implements forward propagation using FLA's Triton kernel, while using the optimized
33
- implementation from TriDao's causal_conv1d CUDA package for backward propagation.
34
- This hybrid strategy combines the advantages of both technologies:
35
-
36
- - Forward: Uses FLA's Triton implementation, optimized for the FLA framework
37
- - Backward: Uses TriDao's causal_conv1d_bwd_function CUDA implementation for faster speed
38
-
39
- Performance Benefits:
40
- - CUDA backward implementation is typically faster than the Triton version, reducing training time
41
- - Maintains the flexibility and compatibility of forward propagation
42
-
43
- Note:
44
- - Input/Output format is (batch, seqlen, dim)
45
- - Backward propagation requires causal_conv1d package: pip install causal-conv1d
46
- - Supports SILU/Swish activation functions
47
- - Current limitations (not yet supported):
48
- * output_final_state must be False
49
- * initial_states must be None
50
- * residual must be None
51
- """
52
- @staticmethod
53
- @input_guard(no_guard_contiguous=["x"])
54
- def forward(
55
- ctx,
56
- x,
57
- weight,
58
- bias=None,
59
- residual: torch.Tensor | None = None,
60
- initial_states=None,
61
- output_final_state=False,
62
- activation=None,
63
- cu_seqlens: torch.LongTensor | None = None,
64
- cu_seqlens_cpu: torch.LongTensor | None = None,
65
- chunk_indices: torch.LongTensor | None = None,
66
- seq_idx: torch.LongTensor | None = None,
67
- ):
68
- if activation not in [None, "silu", "swish"]:
69
- raise NotImplementedError("activation must be None, silu, or swish")
70
- assert output_final_state is False, "output_final_state must be False for FastCausalConv1dFn"
71
- assert initial_states is None, "initial_states must be None for FastCausalConv1dFn"
72
- assert residual is None, "residual must be None for FastCausalConv1dFn"
73
-
74
- bias = bias.contiguous() if bias is not None else None
75
- if cu_seqlens is not None and seq_idx is None:
76
- seq_idx = prepare_sequence_ids(cu_seqlens, cu_seqlens_cpu=cu_seqlens_cpu).to(
77
- torch.int32).unsqueeze(0)
78
- seq_idx = seq_idx.contiguous() if seq_idx is not None else None
79
-
80
- # Import here to avoid circular dependency
81
- from ....modules.conv.triton.ops import causal_conv1d_fwd
82
-
83
- ctx.activation = activation in ["silu", "swish"]
84
- out, _ = causal_conv1d_fwd(
85
- x=x,
86
- weight=weight,
87
- bias=bias,
88
- residual=None,
89
- initial_state=None,
90
- output_final_state=output_final_state,
91
- activation=activation,
92
- cu_seqlens=cu_seqlens,
93
- cu_seqlens_cpu=cu_seqlens_cpu,
94
- chunk_indices=chunk_indices,
95
- )
96
-
97
- ctx.save_for_backward(x, weight, bias, seq_idx, initial_states)
98
- ctx.return_final_states = output_final_state
99
- ctx.return_dinitial_states = (
100
- initial_states is not None and initial_states.requires_grad
101
- )
102
- return out, None
103
-
104
- @staticmethod
105
- @input_guard
106
- def backward(ctx, dout, *args):
107
- x, weight, bias, seq_idx, initial_states = ctx.saved_tensors
108
- dx = torch.empty_like(x, memory_format=torch.contiguous_format)
109
- x = rearrange(x, 'b t d -> b d t')
110
- dx = rearrange(dx, 'b t d -> b d t')
111
- dout = rearrange(dout, 'b t d -> b d t')
112
- dfinal_states = args[0] if ctx.return_final_states else None
113
-
114
- if dout.stride(2) != 1 and dout.stride(1) != 1:
115
- dout = dout.contiguous()
116
- # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the
117
- # backward of conv1d with the backward of chunk).
118
- # Here we just pass in None and dx will be allocated in the C++ code.
119
- dx, dweight, dbias, dinitial_states = causal_conv1d_bwd_function(
120
- x,
121
- weight,
122
- bias,
123
- dout,
124
- seq_idx,
125
- initial_states,
126
- dfinal_states,
127
- dx,
128
- ctx.return_dinitial_states,
129
- ctx.activation,
130
- )
131
- dx = rearrange(dx, 'b d t -> b t d')
132
- return (
133
- dx,
134
- dweight,
135
- dbias if bias is not None else None,
136
- None,
137
- None,
138
- None,
139
- None,
140
- None,
141
- None,
142
- None,
143
- None,
144
- )
145
-
146
-
147
- def fast_causal_conv1d_fn(
148
- x: torch.Tensor,
149
- weight: torch.Tensor | None = None,
150
- bias: torch.Tensor | None = None,
151
- residual: torch.Tensor | None = None,
152
- initial_state: torch.Tensor | None = None,
153
- output_final_state: bool | None = False,
154
- activation: str | None = None,
155
- cu_seqlens: torch.Tensor | None = None,
156
- cu_seqlens_cpu: torch.LongTensor | None = None,
157
- chunk_indices: torch.LongTensor | None = None,
158
- seq_idx: torch.LongTensor | None = None,
159
- ):
160
- """
161
- x: (batch, seqlen, dim)
162
- weight: (dim, width)
163
- bias: (dim,)
164
- seq_idx: (batch, seqlen)
165
- initial_states: (batch, dim, width - 1)
166
- final_states_out: (batch, dim, width - 1), to be written to
167
- activation: either None or "silu" or "swish"
168
-
169
- out: (batch, seqlen, dim)
170
- """
171
- assert causal_conv1d_bwd_function is not None, "causal_conv1d_bwd_function is not available"
172
- return FastCausalConv1dFn.apply(
173
- x,
174
- weight,
175
- bias,
176
- residual,
177
- initial_state,
178
- output_final_state,
179
- activation,
180
- cu_seqlens,
181
- cu_seqlens_cpu,
182
- chunk_indices,
183
- seq_idx,
184
- )
185
-
186
-
187
- def causal_conv1d_cuda(
188
- x: torch.Tensor,
189
- weight: torch.Tensor,
190
- bias: torch.Tensor | None = None,
191
- residual: torch.Tensor | None = None,
192
- initial_state: torch.Tensor | None = None,
193
- output_final_state: bool | None = False,
194
- activation: str | None = None,
195
- cu_seqlens: torch.Tensor | None = None,
196
- cu_seqlens_cpu: torch.LongTensor | None = None,
197
- **kwargs,
198
- ):
199
- assert causal_conv1d_fn_cuda is not None, "causal_conv1d_fn_cuda is not available"
200
- seq_idx = kwargs.get('seq_idx')
201
- if cu_seqlens is not None or seq_idx is not None:
202
- assert initial_state is None, "For CUDA backend, initial_state must be None if cu_seqlens or seq_idx is provided"
203
- W = weight.shape[-1]
204
- if x.stride(-1) != 1:
205
- x = x.contiguous()
206
- x_conv1d = rearrange(x, 'b t d -> b d t')
207
- if cu_seqlens is not None and seq_idx is None:
208
- seq_idx = prepare_sequence_ids(cu_seqlens, cu_seqlens_cpu=cu_seqlens_cpu).to(torch.int32).unsqueeze(0)
209
-
210
- y = causal_conv1d_fn_cuda(
211
- x=x_conv1d,
212
- weight=weight,
213
- bias=bias,
214
- activation=activation,
215
- seq_idx=seq_idx,
216
- initial_states=None,
217
- return_final_states=False,
218
- )
219
-
220
- y = rearrange(y, 'b d t -> b t d')
221
- if output_final_state:
222
- final_state = causal_conv1d_update_states(
223
- x=x,
224
- state_len=W,
225
- initial_state=initial_state,
226
- cu_seqlens=cu_seqlens,
227
- )
228
- else:
229
- final_state = None
230
- if residual is not None:
231
- y.add_(residual)
232
-
233
- return y, final_state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/conv/short_conv.py DELETED
@@ -1,250 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- """Short convolution implementation for efficient causal convolutions."""
9
-
10
- import warnings
11
-
12
- import torch
13
- import torch.nn as nn
14
- from einops import rearrange
15
-
16
- try:
17
- from causal_conv1d import causal_conv1d_fn as causal_conv1d_fn_cuda
18
- from causal_conv1d import causal_conv1d_update as causal_conv1d_update_cuda
19
- except ImportError:
20
- causal_conv1d_fn_cuda = None
21
- causal_conv1d_update_cuda = None
22
-
23
-
24
- class ShortConvolution(nn.Conv1d):
25
- """Short convolution layer for efficient causal convolution operations.
26
-
27
- This class implements a depthwise 1D convolution with causal padding,
28
- designed for efficient sequence processing. It supports multiple backends (Triton/CUDA)
29
- and optional activation functions.
30
-
31
- Args:
32
- hidden_size (int): Number of input/output channels (must be equal for depthwise conv)
33
- kernel_size (int): Size of the convolution kernel
34
- bias (bool, optional): Whether to include learnable bias. Defaults to False.
35
- activation (Optional[str], optional): Activation function ('silu' or 'swish'). Defaults to 'silu'.
36
- backend (Optional[str], optional): Backend implementation ('triton' or 'cuda'). Defaults to 'triton'.
37
- device (Optional[torch.device], optional): Device to place the layer on. Defaults to None.
38
- dtype (Optional[torch.dtype], optional): Data type for layer parameters. Defaults to None.
39
- **kwargs: Additional keyword arguments (deprecated 'use_fast_conv1d' supported for compatibility)
40
-
41
- Attributes:
42
- hidden_size (int): Number of channels
43
- activation (Optional[str]): Selected activation function
44
- backend (str): Actual backend being used (may differ from input due to availability)
45
-
46
- Note:
47
- - Uses depthwise convolution (groups=hidden_size) for efficiency
48
- - Applies causal padding (kernel_size-1) to ensure no future information leakage
49
- - Falls back to Triton backend if CUDA backend is unavailable
50
- """
51
-
52
- def __init__(
53
- self,
54
- hidden_size: int,
55
- kernel_size: int,
56
- bias: bool = False,
57
- activation: str | None = 'silu',
58
- backend: str | None = 'triton',
59
- device: torch.device | None = None,
60
- dtype: torch.dtype | None = None,
61
- **kwargs,
62
- ):
63
- super().__init__(
64
- in_channels=hidden_size,
65
- out_channels=hidden_size,
66
- kernel_size=kernel_size,
67
- groups=hidden_size,
68
- bias=bias,
69
- padding=kernel_size - 1,
70
- device=device,
71
- dtype=dtype,
72
- )
73
-
74
- self.hidden_size = hidden_size
75
- self.activation = None
76
-
77
- if activation is not None:
78
- assert activation in ['silu', 'swish'], f"Activation `{activation}` not supported yet."
79
- self.activation = activation
80
-
81
- if 'use_fast_conv1d' in kwargs:
82
- warnings.warn(
83
- "The `use_fast_conv1d` parameter is deprecated and will be ignored. "
84
- "Please use the `backend` parameter instead.",
85
- )
86
- import os
87
- self.backend = os.environ.get('FLA_CONV_BACKEND', backend)
88
- if backend not in ['cuda', 'triton']:
89
- raise ValueError(f"Invalid backend: {backend}, must be one of ['cuda', 'triton']")
90
- if backend == 'cuda':
91
- if causal_conv1d_fn_cuda is None:
92
- warnings.warn(
93
- "The `backend` parameter is set to `cuda`, but `causal_conv1d_fn` is not available. "
94
- "Switching to the Triton implementation instead. "
95
- "Consider installing `causal_conv1d` to enable the CUDA backend.",
96
- )
97
- self.backend = 'triton'
98
-
99
- def extra_repr(self):
100
- s = ('{in_channels}, {out_channels}, kernel_size={kernel_size}'
101
- ', stride={stride}')
102
- if self.padding != (0,) * len(self.padding):
103
- s += ', padding={padding}'
104
- if self.dilation != (1,) * len(self.dilation):
105
- s += ', dilation={dilation}'
106
- if self.output_padding != (0,) * len(self.output_padding):
107
- s += ', output_padding={output_padding}'
108
- if self.groups != 1:
109
- s += ', groups={groups}'
110
- if self.bias is None:
111
- s += ', bias=False'
112
- if self.padding_mode != 'zeros':
113
- s += ', padding_mode={padding_mode}'
114
- if self.activation is not None:
115
- s += ', activation={activation}'
116
- s += f', backend={self.backend}'
117
- return s.format(**self.__dict__)
118
-
119
- def forward(
120
- self,
121
- x: torch.Tensor,
122
- residual: torch.Tensor | None = None,
123
- mask: torch.Tensor | None = None,
124
- cache: torch.Tensor | None = None,
125
- output_final_state: bool = False,
126
- cu_seqlens: torch.LongTensor | None = None,
127
- chunk_indices: torch.LongTensor | None = None,
128
- **kwargs,
129
- ) -> tuple[torch.Tensor, torch.Tensor | None]:
130
- """
131
- Args:
132
- x (`torch.Tensor`):
133
- Tensor of shape `[B, T, D]`. `B` must be 1 if `cu_seqlens` is provided.
134
- residual (`Optional[torch.Tensor]`):
135
- Residual tensor of shape `[B, T, D]`. Default: `None`.
136
- mask (`Optional[torch.Tensor]`):
137
- Attention mask dealing with padded positions.
138
- cache (`Optional[torch.Tensor]`):
139
- Previous cache tensor of shape `[N, D, W]`, where `W` is the kernel size.
140
- If provided, the cache is updated **inplace**.
141
- output_final_state (Optional[bool]):
142
- Whether to output the final state of shape `[N, D, W]`. Default: `False`.
143
- cu_seqlens (Optional[torch.LongTensor]):
144
- Cumulative sequence lengths for each batch. Used for varlen. Default: `None`.
145
- Shape: [B+1]
146
- chunk_indices (Optional[torch.LongTensor]):
147
- Chunk indices for variable-length sequences. Default: `None`.
148
-
149
- Returns:
150
- Tensor of shape `[B, T, D]`.
151
- """
152
- # Import here to avoid circular dependency
153
- from ...modules.conv.causal_conv1d import causal_conv1d
154
-
155
- B, T, *_ = x.shape
156
- N = B if cu_seqlens is None else len(cu_seqlens) - 1
157
- if mask is not None:
158
- if cu_seqlens is not None:
159
- raise ValueError("`mask` and `cu_seqlens` cannot be provided at the same time")
160
- x = x.mul_(mask.unsqueeze(-1))
161
-
162
- # in decoding phase, the cache (if provided) is updated inplace
163
- if B * T == N:
164
- y, cache = self.step(
165
- x=x,
166
- residual=residual,
167
- cache=cache,
168
- output_final_state=output_final_state,
169
- cu_seqlens=cu_seqlens,
170
- )
171
- return y, cache
172
-
173
- # cuda backend do not support:
174
- # 1. both `cu_seqlens` and `cache` being provided
175
- # 2. both `cu_seqlens` and `output_final_state` being provided
176
- # and other small issues
177
- # to simplify the implementation, we just switch to triton backend
178
- if self.backend == 'cuda' and cache is not None:
179
- warnings.warn(
180
- "The CUDA backend does not support both `cu_seqlens` and `cache` being provided, "
181
- "or both `cu_seqlens` and `output_final_state` being provided. "
182
- "Switching to the Triton backend instead. ",
183
- stacklevel=2,
184
- )
185
- self.backend = 'triton'
186
-
187
- return causal_conv1d(
188
- x=x,
189
- weight=rearrange(self.weight, "d 1 w -> d w"),
190
- bias=self.bias,
191
- residual=residual,
192
- initial_state=cache,
193
- output_final_state=output_final_state,
194
- activation=self.activation,
195
- backend=self.backend,
196
- cu_seqlens=cu_seqlens,
197
- chunk_indices=chunk_indices,
198
- **kwargs,
199
- )
200
-
201
- def step(
202
- self,
203
- x: torch.Tensor,
204
- residual: torch.Tensor | None,
205
- cache: torch.Tensor | None,
206
- output_final_state: bool = False,
207
- cu_seqlens: torch.LongTensor | None = None,
208
- ) -> tuple[torch.Tensor, torch.Tensor | None]:
209
- from ...modules.conv.triton.ops import causal_conv1d_update
210
-
211
- B, _, D, W = *x.shape, self.kernel_size[0]
212
- N = B if cu_seqlens is None else len(cu_seqlens) - 1
213
- # Always initialise cache when None so the Triton kernel never
214
- # receives a None tensor. Return value still respects output_final_state
215
- # to maintain consistency with the non-step path in forward().
216
- if cache is None:
217
- cache = x.new_zeros(N, D, W)
218
- # NOTE: we follow the fast mode that updates the cache in-place
219
- if self.backend == 'triton':
220
- y, cache = causal_conv1d_update(
221
- x=x,
222
- cache=cache,
223
- residual=residual,
224
- weight=rearrange(self.weight, "d 1 w -> d w"),
225
- bias=self.bias,
226
- activation=self.activation,
227
- )
228
- return y, (cache if output_final_state else None)
229
-
230
- shape = x.shape
231
- x = x.squeeze(0) if cu_seqlens is not None else x.squeeze(1)
232
- # equivalent to:
233
- # cache.copy_(cache.roll(shifts=-1, dims=-1))
234
- # cache[:, :, -1] = x
235
- # y = torch.sum(cache * rearrange(self.weight, "d 1 w -> d w"), dim=-1)
236
- y = causal_conv1d_update_cuda(
237
- x=x,
238
- conv_state=cache,
239
- weight=rearrange(self.weight, "d 1 w -> d w"),
240
- bias=self.bias,
241
- activation=self.activation,
242
- )
243
- y = y.view(shape)
244
- if residual is not None:
245
- y.add_(residual)
246
- return y, (cache if output_final_state else None)
247
-
248
- @property
249
- def state_size(self) -> int:
250
- return self.hidden_size * self.kernel_size
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/conv/triton/ops.py DELETED
@@ -1,424 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- import torch
9
- import triton
10
- from einops import rearrange
11
-
12
- from ....modules.backends import dispatch
13
- from ....ops.utils import prepare_chunk_indices
14
- from ....utils import input_guard
15
-
16
- from .kernels import (
17
- causal_conv1d_bwd_kernel,
18
- causal_conv1d_fwd_kernel,
19
- causal_conv1d_states_fwd_kernel,
20
- causal_conv1d_update_kernel,
21
- compute_dh0_kernel,
22
- )
23
-
24
-
25
- def _has_non_standard_layout(x: torch.Tensor) -> bool:
26
- """QKV-style views (stride_t != D) break triton-ascend masked kernels."""
27
- if x.dtype not in (torch.float16, torch.bfloat16):
28
- return False
29
- if x.dim() != 3:
30
- return not x.is_contiguous()
31
- _, stride_t, stride_d = x.stride()
32
- return stride_d == 1 and stride_t != x.shape[-1]
33
-
34
-
35
- @dispatch('modules')
36
- @input_guard(no_guard_contiguous=["x"])
37
- def causal_conv1d_fwd(
38
- x: torch.Tensor,
39
- weight: torch.Tensor,
40
- bias: torch.Tensor,
41
- residual: torch.Tensor,
42
- initial_state: torch.Tensor | None = None,
43
- output_final_state: bool = False,
44
- activation: str | None = None,
45
- cu_seqlens: torch.LongTensor | None = None,
46
- cu_seqlens_cpu: torch.LongTensor | None = None,
47
- chunk_indices: torch.LongTensor | None = None,
48
- BT: int = 64,
49
- layout_fallback: bool = False,
50
- ) -> torch.Tensor:
51
- shape = x.shape
52
- if x.shape[-1] != weight.shape[0]:
53
- x = rearrange(x, 'b t ... -> b t (...)')
54
- B, T, D = x.shape[0], x.shape[1], weight.shape[0]
55
- W = weight.shape[1]
56
- stride_x_n, stride_x_t, stride_x_d = x.stride()
57
-
58
- BW = triton.next_power_of_2(W)
59
- if cu_seqlens is not None and chunk_indices is None:
60
- chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
61
- NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
62
- NB = triton.cdiv(B*T, 1024)
63
-
64
- y = torch.empty_like(x, memory_format=torch.contiguous_format)
65
-
66
- def grid(meta): return (triton.cdiv(D, meta['BD']), NT, B)
67
- causal_conv1d_fwd_kernel[grid](
68
- x=x,
69
- y=y,
70
- weight=weight,
71
- bias=bias,
72
- residual=residual,
73
- cu_seqlens=cu_seqlens,
74
- initial_state=initial_state,
75
- chunk_indices=chunk_indices,
76
- B=B,
77
- T=T,
78
- D=D,
79
- W=W,
80
- BT=BT,
81
- BW=BW,
82
- NB=NB,
83
- stride_x_n=stride_x_n,
84
- stride_x_t=stride_x_t,
85
- stride_x_d=stride_x_d,
86
- ACTIVATION=activation,
87
- )
88
- final_state = None
89
- if output_final_state:
90
- final_state = causal_conv1d_update_states(
91
- x=x,
92
- state_len=W,
93
- initial_state=initial_state,
94
- cu_seqlens=cu_seqlens,
95
- )
96
- return y.view(shape), final_state
97
-
98
-
99
- @dispatch('modules')
100
- def compute_dh0_triton(
101
- dy: torch.Tensor,
102
- y: torch.Tensor | None,
103
- weight: torch.Tensor,
104
- initial_state: torch.Tensor,
105
- activation: str | None,
106
- cu_seqlens: torch.Tensor | None,
107
- ) -> torch.Tensor:
108
- """
109
- Compute dh0 (gradient w.r.t. initial_state) using a separate Triton kernel.
110
- This is a workaround for Triton compiler bugs on some architectures (e.g., GB200).
111
- """
112
- D, W = weight.shape
113
- N = initial_state.shape[0]
114
- T = dy.shape[1]
115
-
116
- # Initialize dh0
117
- dh0 = torch.zeros_like(initial_state)
118
-
119
- BD = 32
120
- grid = (triton.cdiv(D, BD), N)
121
-
122
- y_to_pass = y if activation in ('swish', 'silu') else None
123
- # dy is [B, T, D], stride_n = T*D, stride_t = D
124
- stride_dy_n = dy.stride(0)
125
- stride_dy_t = dy.stride(1)
126
-
127
- compute_dh0_kernel[grid](
128
- dy=dy,
129
- y=y_to_pass,
130
- weight=weight,
131
- dh0=dh0,
132
- cu_seqlens=cu_seqlens,
133
- stride_dy_n=stride_dy_n,
134
- stride_dy_t=stride_dy_t,
135
- T=T,
136
- D=D,
137
- W=W,
138
- BD=BD,
139
- )
140
-
141
- return dh0
142
-
143
-
144
- @dispatch('modules')
145
- def causal_conv1d_bwd(
146
- x: torch.Tensor,
147
- dy: torch.Tensor,
148
- dht: torch.Tensor,
149
- weight: torch.Tensor | None = None,
150
- bias: torch.Tensor | None = None,
151
- residual: torch.Tensor | None = None,
152
- initial_state: torch.Tensor | None = None,
153
- activation: str | None = None,
154
- cu_seqlens: torch.Tensor | None = None,
155
- cu_seqlens_cpu: torch.LongTensor | None = None,
156
- chunk_indices: torch.LongTensor | None = None,
157
- BT: int = 64,
158
- layout_fallback: bool = False,
159
- ):
160
- shape = x.shape
161
- if x.shape[-1] != weight.shape[0]:
162
- x = rearrange(x, 'b t ... -> b t (...)')
163
- B, T, D = x.shape
164
- W = weight.shape[1] if weight is not None else None
165
-
166
- stride_x_n, stride_x_t, stride_x_d = x.stride()
167
- stride_dy_n, stride_dy_t, stride_dy_d = dy.stride()
168
-
169
- BW = triton.next_power_of_2(W)
170
- if cu_seqlens is not None and chunk_indices is None:
171
- chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
172
- NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
173
- NB = triton.cdiv(B*T, 1024)
174
-
175
- y = None
176
- if activation is not None:
177
- y, _ = causal_conv1d_fwd(
178
- x=x,
179
- weight=weight,
180
- bias=bias,
181
- residual=None,
182
- initial_state=initial_state,
183
- activation=None,
184
- cu_seqlens=cu_seqlens,
185
- cu_seqlens_cpu=cu_seqlens_cpu,
186
- output_final_state=False,
187
- chunk_indices=chunk_indices,
188
- )
189
- dx = torch.empty_like(x)
190
- dw = weight.new_empty(B*NT, *weight.shape, dtype=torch.float) if weight is not None else None
191
- db = bias.new_empty(B*NT, *bias.shape, dtype=torch.float) if bias is not None else None
192
- dr = dy if residual is not None else None
193
-
194
- stride_dx_n, stride_dx_t, stride_dx_d = dx.stride()
195
-
196
- def grid(meta): return (triton.cdiv(D, meta['BD']), NT, B)
197
- causal_conv1d_bwd_kernel[grid](
198
- x=x,
199
- y=y,
200
- weight=weight,
201
- initial_state=initial_state,
202
- dht=dht,
203
- dy=dy,
204
- dx=dx,
205
- dw=dw,
206
- db=db,
207
- cu_seqlens=cu_seqlens,
208
- chunk_indices=chunk_indices,
209
- B=B,
210
- T=T,
211
- D=D,
212
- W=W,
213
- BT=BT,
214
- BW=BW,
215
- NB=NB,
216
- stride_x_n=stride_x_n,
217
- stride_x_t=stride_x_t,
218
- stride_x_d=stride_x_d,
219
- stride_dx_n=stride_dx_n,
220
- stride_dx_t=stride_dx_t,
221
- stride_dx_d=stride_dx_d,
222
- stride_dy_n=stride_dy_n,
223
- stride_dy_t=stride_dy_t,
224
- stride_dy_d=stride_dy_d,
225
- ACTIVATION=activation,
226
- )
227
- if weight is not None:
228
- dw = dw.sum(0).to(weight)
229
- if bias is not None:
230
- db = db.sum(0).to(bias)
231
-
232
- # Compute dh0 using separate Triton kernel to avoid compiler bugs on some architectures (e.g., GB200)
233
- dh0 = None
234
- if initial_state is not None:
235
- dh0 = compute_dh0_triton(
236
- dy=dy,
237
- y=y,
238
- weight=weight,
239
- initial_state=initial_state,
240
- activation=activation,
241
- cu_seqlens=cu_seqlens,
242
- )
243
-
244
- return dx.view(shape), dw, db, dr, dh0
245
-
246
-
247
- @dispatch('modules')
248
- @input_guard(no_guard_contiguous=["x"])
249
- def causal_conv1d_update_states(
250
- x: torch.Tensor,
251
- state_len: int,
252
- initial_state: torch.Tensor | None = None,
253
- cu_seqlens: torch.Tensor | None = None,
254
- ) -> torch.Tensor:
255
- if cu_seqlens is not None:
256
- N = len(cu_seqlens) - 1
257
- if x.dim() == 2:
258
- stride_x_n = 0
259
- stride_x_t, stride_x_d = x.stride()
260
- T = x.shape[0]
261
- else:
262
- stride_x_n = x.stride(0)
263
- stride_x_t, stride_x_d = x.stride(1), x.stride(2)
264
- T = x.shape[1]
265
- D = x.shape[-1]
266
- else:
267
- B, T, D = x.shape
268
- N = B
269
- stride_x_n, stride_x_t, stride_x_d = x.stride()
270
-
271
- W = state_len
272
- final_state = torch.empty(N, D, W, dtype=x.dtype, device=x.device)
273
-
274
- BD = min(triton.next_power_of_2(D), 256)
275
- BW = triton.next_power_of_2(W)
276
-
277
- grid = (triton.cdiv(D, BD), N)
278
-
279
- causal_conv1d_states_fwd_kernel[grid](
280
- x=x,
281
- initial_state=initial_state,
282
- final_state=final_state,
283
- cu_seqlens=cu_seqlens,
284
- T=T,
285
- D=D,
286
- W=W,
287
- stride_x_n=stride_x_n,
288
- stride_x_t=stride_x_t,
289
- stride_x_d=stride_x_d,
290
- BW=BW,
291
- BD=BD,
292
- )
293
- return final_state
294
-
295
-
296
- @dispatch('modules')
297
- @input_guard(no_guard_contiguous=["x"])
298
- def causal_conv1d_update(
299
- x: torch.Tensor,
300
- cache: torch.Tensor,
301
- residual: torch.Tensor | None = None,
302
- weight: torch.Tensor | None = None,
303
- bias: torch.Tensor | None = None,
304
- activation: str | None = None,
305
- ) -> torch.Tensor:
306
- shape = x.shape
307
- if weight is not None and x.shape[-1] != weight.shape[0]:
308
- x = rearrange(x, 'b t ... -> b t (...)')
309
-
310
- D = x.shape[-1]
311
- N = x.numel() // D
312
- W = weight.shape[1] if weight is not None else None
313
- BW = triton.next_power_of_2(W)
314
-
315
- if x.dim() == 2:
316
- # Case: (N, D)
317
- stride_x_n = x.stride(0)
318
- stride_x_d = x.stride(1)
319
- elif x.dim() == 3 and x.shape[0] == 1:
320
- # Case: (1, N, D) -> Time=1, Batch=N, Dim=D
321
- # Batch 在 dim 1
322
- stride_x_n = x.stride(1)
323
- stride_x_d = x.stride(2)
324
- elif x.dim() == 3:
325
- # Case: (N, 1, D) -> Batch=N, Time=1, Dim=D
326
- # Batch 在 dim 0
327
- stride_x_n = x.stride(0)
328
- stride_x_d = x.stride(2)
329
- else:
330
- # Fallback / Error case
331
- raise ValueError(f"Unsupported input shape: {x.shape}")
332
-
333
- y = torch.empty_like(x, memory_format=torch.contiguous_format)
334
-
335
- if y.dim() == 2:
336
- stride_y_n, stride_y_d = y.stride(0), y.stride(1)
337
- elif y.dim() == 3 and y.shape[0] == 1:
338
- stride_y_n, stride_y_d = y.stride(1), y.stride(2)
339
- elif y.dim() == 3:
340
- stride_y_n, stride_y_d = y.stride(0), y.stride(2)
341
-
342
- def grid(meta): return (triton.cdiv(D, meta['BD']), N)
343
-
344
- causal_conv1d_update_kernel[grid](
345
- x=x,
346
- cache=cache,
347
- residual=residual,
348
- y=y,
349
- weight=weight,
350
- bias=bias,
351
- stride_x_n=stride_x_n,
352
- stride_x_d=stride_x_d,
353
- stride_y_n=stride_y_n,
354
- stride_y_d=stride_y_d,
355
- D=D,
356
- W=W,
357
- BW=BW,
358
- ACTIVATION=activation,
359
- )
360
- return y.view(shape), cache
361
-
362
-
363
- class CausalConv1dFunction(torch.autograd.Function):
364
-
365
- @staticmethod
366
- @input_guard(no_guard_contiguous=["x"])
367
- def forward(
368
- ctx,
369
- x: torch.Tensor,
370
- weight: torch.Tensor | None = None,
371
- bias: torch.Tensor | None = None,
372
- residual: torch.Tensor | None = None,
373
- initial_state: torch.Tensor | None = None,
374
- output_final_state: bool | None = False,
375
- activation: str | None = None,
376
- cu_seqlens: torch.Tensor | None = None,
377
- cu_seqlens_cpu: torch.LongTensor | None = None,
378
- chunk_indices: torch.LongTensor | None = None,
379
- chunk_size: int = 64,
380
- ):
381
- BT = chunk_size
382
- if cu_seqlens is not None and chunk_indices is None:
383
- chunk_indices = prepare_chunk_indices(cu_seqlens, BT, cu_seqlens_cpu=cu_seqlens_cpu)
384
- ctx.activation = activation
385
- ctx.cu_seqlens = cu_seqlens
386
- ctx.cu_seqlens_cpu = cu_seqlens_cpu
387
- ctx.chunk_indices = chunk_indices
388
- ctx.layout_fallback = _has_non_standard_layout(x)
389
- ctx.save_for_backward(x, weight, bias, residual, initial_state)
390
- y, final_state = causal_conv1d_fwd(
391
- x=x,
392
- weight=weight,
393
- bias=bias,
394
- residual=residual,
395
- initial_state=initial_state,
396
- output_final_state=output_final_state,
397
- activation=activation,
398
- cu_seqlens=cu_seqlens,
399
- cu_seqlens_cpu=cu_seqlens_cpu,
400
- chunk_indices=chunk_indices,
401
- BT=BT,
402
- layout_fallback=ctx.layout_fallback,
403
- )
404
- return y, final_state
405
-
406
- @staticmethod
407
- @input_guard(no_guard_contiguous=["dy"])
408
- def backward(ctx, dy: torch.Tensor, dht: torch.Tensor | None = None):
409
- x, weight, bias, residual, initial_state = ctx.saved_tensors
410
- dx, dw, db, dr, dh0 = causal_conv1d_bwd(
411
- x=x,
412
- dy=dy,
413
- dht=dht,
414
- weight=weight,
415
- bias=bias,
416
- residual=residual,
417
- initial_state=initial_state,
418
- activation=ctx.activation,
419
- cu_seqlens=ctx.cu_seqlens,
420
- cu_seqlens_cpu=ctx.cu_seqlens_cpu,
421
- chunk_indices=ctx.chunk_indices,
422
- layout_fallback=ctx.layout_fallback,
423
- )
424
- return dx, dw, db, dr, dh0, None, None, None, None, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/convolution.py DELETED
@@ -1,42 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- from ..modules.conv import (
9
- ImplicitLongConvolution,
10
- LongConvolution,
11
- PositionalEmbedding,
12
- ShortConvolution,
13
- causal_conv1d,
14
- fft_conv,
15
- )
16
- from ..modules.conv.cp import CausalConv1dFunctionCP, causal_conv1d_cp
17
- from ..modules.conv.cuda import FastCausalConv1dFn, fast_causal_conv1d_fn
18
- from ..modules.conv.triton import (
19
- CausalConv1dFunction,
20
- causal_conv1d_bwd,
21
- causal_conv1d_fwd,
22
- causal_conv1d_update,
23
- causal_conv1d_update_states,
24
- )
25
-
26
- __all__ = [
27
- 'CausalConv1dFunction',
28
- 'CausalConv1dFunctionCP',
29
- 'FastCausalConv1dFn',
30
- 'ImplicitLongConvolution',
31
- 'LongConvolution',
32
- 'PositionalEmbedding',
33
- 'ShortConvolution',
34
- 'causal_conv1d',
35
- 'causal_conv1d_bwd',
36
- 'causal_conv1d_cp',
37
- 'causal_conv1d_fwd',
38
- 'causal_conv1d_update',
39
- 'causal_conv1d_update_states',
40
- 'fast_causal_conv1d_fn',
41
- 'fft_conv',
42
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/feature_map.py CHANGED
@@ -121,8 +121,7 @@ class DPFPFeatureMap(nn.Module):
121
  self.nu = nu
122
 
123
  def forward(self, x: torch.Tensor) -> torch.Tensor:
124
- relu = x.relu()
125
- x = torch.cat([relu, -relu], dim=-1)
126
  x_rolled = torch.cat([x.roll(shifts=j, dims=-1) for j in range(1, self.nu+1)], dim=-1)
127
  x_repeat = torch.cat([x] * self.nu, dim=-1)
128
  return x_repeat * x_rolled
 
121
  self.nu = nu
122
 
123
  def forward(self, x: torch.Tensor) -> torch.Tensor:
124
+ x = torch.cat([x.relu(), (-x).relu()], dim=-1)
 
125
  x_rolled = torch.cat([x.roll(shifts=j, dims=-1) for j in range(1, self.nu+1)], dim=-1)
126
  x_repeat = torch.cat([x] * self.nu, dim=-1)
127
  return x_repeat * x_rolled
build/torch-cuda/modules/fused_bitlinear.py CHANGED
@@ -5,491 +5,42 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- # Implementations of BitLinear layer with fused LayerNorm and quantized Linear layer.
9
  # [The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits](https://arxiv.org/abs/2402.17764)
10
  # [Scalable MatMul-free Language Modeling](https://arxiv.org/abs/2406.02528)
11
  #
12
- # Code adapted from https://github.com/ridgerchu/matmulfreellm/
13
 
14
  from __future__ import annotations
15
 
16
- import math
17
-
18
  import torch
19
  import torch.nn as nn
20
  import torch.nn.functional as F
21
- import triton
22
- import triton.language as tl
23
-
24
- from ..modules.layernorm import RMSNorm
25
- from ..utils import IS_AMD, autotune_cache_kwargs, get_multiprocessor_count, input_guard, require_version
26
-
27
- NUM_WARPS_AUTOTUNE = [1, 2, 4, 8, 16] if IS_AMD else [1, 2, 4, 8, 16, 32]
28
 
 
 
29
 
30
- def activation_quant(x):
31
- """
32
- Per-token quantization to 8 bits. No grouping is needed for quantization.
33
-
34
- Args:
35
- x: An activation tensor with shape [n, d].
36
 
37
- Returns:
38
- A quantized activation tensor with shape [n, d].
39
- """
40
- # Compute the scale factor
41
  scale = 127.0 / x.abs().max(dim=-1, keepdim=True).values.clamp_(min=1e-5)
42
- # Quantize and then de-quantize the tensor
43
- y = (x * scale).round().clamp_(-128, 127) / scale
44
  return y
45
 
46
 
47
- def weight_quant(w):
48
- """
49
- Per-tensor quantization to 1.58 bits. No grouping is needed for quantization.
50
-
51
- Args:
52
- w: A weight tensor with shape [d, k].
53
-
54
- Returns:
55
- A quantized weight tensor with shape [d, k].
56
- """
57
- # Compute the scale factor
58
- scale = 1.0 / w.abs().mean().clamp_(min=1e-5)
59
- # Quantize and then de-quantize the tensor
60
- u = (w * scale).round().clamp_(-1, 1) / scale
61
- return u
62
-
63
-
64
- @triton.autotune(
65
- configs=[
66
- triton.Config({}, num_warps=num_warps)
67
- for num_warps in NUM_WARPS_AUTOTUNE
68
- ],
69
- key=["N", "HAS_RESIDUAL", "STORE_RESIDUAL_OUT", "IS_RMS_NORM", "HAS_BIAS"],
70
- **autotune_cache_kwargs,
71
- )
72
- @triton.jit
73
- def layer_norm_fwd_kernel_quant(
74
- X, # pointer to the input
75
- Y, # pointer to the output
76
- W, # pointer to the weights
77
- B, # pointer to the biases
78
- RESIDUAL, # pointer to the residual
79
- RESIDUAL_OUT, # pointer to the residual
80
- Mean, # pointer to the mean
81
- Rstd, # pointer to the 1/std
82
- stride_x_row, # how much to increase the pointer when moving by 1 row
83
- stride_y_row,
84
- stride_res_row,
85
- stride_res_out_row,
86
- N, # number of columns in X
87
- eps, # epsilon to avoid division by zero
88
- IS_RMS_NORM: tl.constexpr,
89
- BLOCK_N: tl.constexpr,
90
- HAS_RESIDUAL: tl.constexpr,
91
- STORE_RESIDUAL_OUT: tl.constexpr,
92
- HAS_WEIGHT: tl.constexpr,
93
- HAS_BIAS: tl.constexpr,
94
- ):
95
- # Map the program id to the row of X and Y it should compute.
96
- row = tl.program_id(0)
97
- X += row * stride_x_row
98
- Y += row * stride_y_row
99
- if HAS_RESIDUAL:
100
- RESIDUAL += row * stride_res_row
101
- if STORE_RESIDUAL_OUT:
102
- RESIDUAL_OUT += row * stride_res_out_row
103
- # Compute mean and variance
104
- cols = tl.arange(0, BLOCK_N)
105
- x = tl.load(X + cols, mask=cols < N, other=0.0).to(tl.float32)
106
- if HAS_RESIDUAL:
107
- residual = tl.load(RESIDUAL + cols, mask=cols < N, other=0.0).to(tl.float32)
108
- x += residual
109
- if STORE_RESIDUAL_OUT:
110
- tl.store(RESIDUAL_OUT + cols, x, mask=cols < N)
111
- if not IS_RMS_NORM:
112
- mean = tl.sum(x, axis=0) / N
113
- tl.store(Mean + row, mean)
114
- xbar = tl.where(cols < N, x - mean, 0.0)
115
- var = tl.sum(xbar * xbar, axis=0) / N
116
- else:
117
- xbar = tl.where(cols < N, x, 0.0)
118
- var = tl.sum(xbar * xbar, axis=0) / N
119
- rstd = 1 / tl.sqrt(var + eps)
120
- tl.store(Rstd + row, rstd)
121
- # Normalize and apply linear transformation
122
- mask = cols < N
123
- if HAS_WEIGHT:
124
- w = tl.load(W + cols, mask=mask).to(tl.float32)
125
- if HAS_BIAS:
126
- b = tl.load(B + cols, mask=mask).to(tl.float32)
127
- x_hat = (x - mean) * rstd if not IS_RMS_NORM else x * rstd
128
-
129
- y = x_hat * w if HAS_WEIGHT else x_hat
130
- if HAS_BIAS:
131
- y = y + b
132
-
133
- # Aply quantization to the output
134
- scale = 127.0 / tl.maximum(tl.max(tl.abs(y), 0), 1e-5)
135
- # Quantize and then de-quantize the tensor
136
- y = tl.extra.cuda.libdevice.round(y * scale)
137
- y = tl.maximum(tl.minimum(y, 127), -128) / scale
138
-
139
- # Write output
140
- tl.store(Y + cols, y, mask=mask)
141
-
142
-
143
- def layer_norm_fwd_quant(
144
- x: torch.Tensor,
145
- weight: torch.Tensor,
146
- bias: torch.Tensor,
147
- eps: float,
148
- residual: torch.Tensor = None,
149
- out_dtype: torch.dtype = None,
150
- residual_dtype: torch.dtype = None,
151
- is_rms_norm: bool = False,
152
- ):
153
- if residual is not None:
154
- residual_dtype = residual.dtype
155
- M, N = x.shape
156
- # allocate output
157
- y = torch.empty_like(x, dtype=x.dtype if out_dtype is None else out_dtype)
158
- if residual is not None or (residual_dtype is not None and residual_dtype != x.dtype):
159
- residual_out = torch.empty(M, N, device=x.device, dtype=residual_dtype)
160
- else:
161
- residual_out = None
162
- mean = torch.empty((M,), dtype=torch.float32, device=x.device) if not is_rms_norm else None
163
- rstd = torch.empty((M,), dtype=torch.float32, device=x.device)
164
- # Less than 64KB per feature: enqueue fused kernel
165
- MAX_FUSED_SIZE = 65536 // x.element_size()
166
- BLOCK_N = min(MAX_FUSED_SIZE, triton.next_power_of_2(N))
167
- if N > BLOCK_N:
168
- raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
169
- # heuristics for number of warps
170
- layer_norm_fwd_kernel_quant[(M,)](
171
- x,
172
- y,
173
- weight,
174
- bias,
175
- residual,
176
- residual_out,
177
- mean,
178
- rstd,
179
- x.stride(0),
180
- y.stride(0),
181
- residual.stride(0) if residual is not None else 0,
182
- residual_out.stride(0) if residual_out is not None else 0,
183
- N,
184
- eps,
185
- is_rms_norm,
186
- BLOCK_N,
187
- residual is not None,
188
- residual_out is not None,
189
- weight is not None,
190
- bias is not None,
191
- )
192
- # residual_out is None if residual is None and residual_dtype == input_dtype
193
- return y, mean, rstd, residual_out if residual_out is not None else x
194
-
195
-
196
- @triton.heuristics({
197
- "RECOMPUTE_OUTPUT": lambda args: args["Y"] is not None,
198
- })
199
- @triton.autotune(
200
- configs=[
201
- triton.Config({}, num_warps=num_warps)
202
- for num_warps in NUM_WARPS_AUTOTUNE
203
- ],
204
- key=["N", "HAS_DRESIDUAL", "STORE_DRESIDUAL", "IS_RMS_NORM", "HAS_BIAS"],
205
- **autotune_cache_kwargs,
206
- )
207
- @triton.jit
208
- def layer_norm_bwd_kernel(
209
- X, # pointer to the input
210
- W, # pointer to the weights
211
- B, # pointer to the biases
212
- Y, # pointer to the output to be recomputed
213
- DY, # pointer to the output gradient
214
- DX, # pointer to the input gradient
215
- DW, # pointer to the partial sum of weights gradient
216
- DB, # pointer to the partial sum of biases gradient
217
- DRESIDUAL,
218
- DRESIDUAL_IN,
219
- Mean, # pointer to the mean
220
- Rstd, # pointer to the 1/std
221
- stride_x_row, # how much to increase the pointer when moving by 1 row
222
- stride_y_row,
223
- stride_dy_row,
224
- stride_dx_row,
225
- stride_dres_row,
226
- stride_dres_in_row,
227
- M, # number of rows in X
228
- N, # number of columns in X
229
- eps, # epsilon to avoid division by zero
230
- rows_per_program,
231
- IS_RMS_NORM: tl.constexpr,
232
- BLOCK_N: tl.constexpr,
233
- HAS_DRESIDUAL: tl.constexpr,
234
- STORE_DRESIDUAL: tl.constexpr,
235
- HAS_WEIGHT: tl.constexpr,
236
- HAS_BIAS: tl.constexpr,
237
- RECOMPUTE_OUTPUT: tl.constexpr,
238
- ):
239
- # Map the program id to the elements of X, DX, and DY it should compute.
240
- row_block_id = tl.program_id(0)
241
- row_start = row_block_id * rows_per_program
242
- cols = tl.arange(0, BLOCK_N)
243
- mask = cols < N
244
- X += row_start * stride_x_row
245
- if HAS_DRESIDUAL:
246
- DRESIDUAL += row_start * stride_dres_row
247
- if STORE_DRESIDUAL:
248
- DRESIDUAL_IN += row_start * stride_dres_in_row
249
- DY += row_start * stride_dy_row
250
- DX += row_start * stride_dx_row
251
- if RECOMPUTE_OUTPUT:
252
- Y += row_start * stride_y_row
253
- if HAS_WEIGHT:
254
- w = tl.load(W + cols, mask=mask).to(tl.float32)
255
- dw = tl.zeros((BLOCK_N,), dtype=tl.float32)
256
- if RECOMPUTE_OUTPUT and HAS_BIAS:
257
- b = tl.load(B + cols, mask=mask, other=0.0).to(tl.float32)
258
- if HAS_BIAS:
259
- db = tl.zeros((BLOCK_N,), dtype=tl.float32)
260
- row_end = min((row_block_id + 1) * rows_per_program, M)
261
- for row in range(row_start, row_end):
262
- # Load data to SRAM
263
- x = tl.load(X + cols, mask=mask, other=0).to(tl.float32)
264
- dy = tl.load(DY + cols, mask=mask, other=0).to(tl.float32)
265
- if not IS_RMS_NORM:
266
- mean = tl.load(Mean + row)
267
- rstd = tl.load(Rstd + row)
268
- # Compute dx
269
- xhat = (x - mean) * rstd if not IS_RMS_NORM else x * rstd
270
- xhat = tl.where(mask, xhat, 0.0)
271
- if RECOMPUTE_OUTPUT:
272
- y = xhat * w if HAS_WEIGHT else xhat
273
- if HAS_BIAS:
274
- y = y + b
275
-
276
- # Aply quantization to the output
277
- scale = 127.0 / tl.maximum(tl.max(tl.abs(y), 0), 1e-5)
278
- # Quantize and then de-quantize the tensor
279
- y = tl.extra.cuda.libdevice.round(y * scale)
280
- y = tl.maximum(tl.minimum(y, 127), -128) / scale
281
-
282
- tl.store(Y + cols, y, mask=mask)
283
- wdy = dy
284
- if HAS_WEIGHT:
285
- wdy = dy * w
286
- dw += dy * xhat
287
- if HAS_BIAS:
288
- db += dy
289
- if not IS_RMS_NORM:
290
- c1 = tl.sum(xhat * wdy, axis=0) / N
291
- c2 = tl.sum(wdy, axis=0) / N
292
- dx = (wdy - (xhat * c1 + c2)) * rstd
293
- else:
294
- c1 = tl.sum(xhat * wdy, axis=0) / N
295
- dx = (wdy - xhat * c1) * rstd
296
- if HAS_DRESIDUAL:
297
- dres = tl.load(DRESIDUAL + cols, mask=mask, other=0).to(tl.float32)
298
- dx += dres
299
- # Write dx
300
- if STORE_DRESIDUAL:
301
- tl.store(DRESIDUAL_IN + cols, dx, mask=mask)
302
- tl.store(DX + cols, dx, mask=mask)
303
-
304
- X += stride_x_row
305
- if HAS_DRESIDUAL:
306
- DRESIDUAL += stride_dres_row
307
- if STORE_DRESIDUAL:
308
- DRESIDUAL_IN += stride_dres_in_row
309
- if RECOMPUTE_OUTPUT:
310
- Y += stride_y_row
311
- DY += stride_dy_row
312
- DX += stride_dx_row
313
- if HAS_WEIGHT:
314
- tl.store(DW + row_block_id * N + cols, dw, mask=mask)
315
- if HAS_BIAS:
316
- tl.store(DB + row_block_id * N + cols, db, mask=mask)
317
-
318
-
319
- def layer_norm_bwd(
320
- dy: torch.Tensor,
321
  x: torch.Tensor,
322
- weight: torch.Tensor,
323
- bias: torch.Tensor,
324
- eps: float,
325
- mean: torch.Tensor,
326
- rstd: torch.Tensor,
327
- dresidual: torch.Tensor = None,
328
- has_residual: bool = False,
 
329
  is_rms_norm: bool = False,
330
- x_dtype: torch.dtype = None,
331
- recompute_output: bool = False,
332
- ):
333
- M, N = x.shape
334
- # allocate output
335
- dx = torch.empty_like(x) if x_dtype is None else torch.empty(M, N, dtype=x_dtype, device=x.device)
336
- dresidual_in = torch.empty_like(x) if has_residual and dx.dtype != x.dtype else None
337
- y = torch.empty(M, N, dtype=dy.dtype, device=dy.device) if recompute_output else None
338
-
339
- # Less than 64KB per feature: enqueue fused kernel
340
- MAX_FUSED_SIZE = 65536 // x.element_size()
341
- BLOCK_N = min(MAX_FUSED_SIZE, triton.next_power_of_2(N))
342
- if N > BLOCK_N:
343
- raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
344
- sm_count = get_multiprocessor_count(x.device.index)
345
- _dw = torch.empty((sm_count, N), dtype=torch.float32, device=weight.device) if weight is not None else None
346
- _db = torch.empty((sm_count, N), dtype=torch.float32, device=bias.device) if bias is not None else None
347
- rows_per_program = math.ceil(M / sm_count)
348
- grid = (sm_count,)
349
- layer_norm_bwd_kernel[grid](
350
- x,
351
- weight,
352
- bias,
353
- y,
354
- dy,
355
- dx,
356
- _dw,
357
- _db,
358
- dresidual,
359
- dresidual_in,
360
- mean,
361
- rstd,
362
- x.stride(0),
363
- 0 if not recompute_output else y.stride(0),
364
- dy.stride(0),
365
- dx.stride(0),
366
- dresidual.stride(0) if dresidual is not None else 0,
367
- dresidual_in.stride(0) if dresidual_in is not None else 0,
368
- M,
369
- N,
370
- eps,
371
- rows_per_program,
372
- is_rms_norm,
373
- BLOCK_N,
374
- dresidual is not None,
375
- dresidual_in is not None,
376
- weight is not None,
377
- bias is not None,
378
- )
379
- dw = _dw.sum(0).to(weight.dtype) if weight is not None else None
380
- db = _db.sum(0).to(bias.dtype) if bias is not None else None
381
- # Don't need to compute dresidual_in separately in this case
382
- if has_residual and dx.dtype == x.dtype:
383
- dresidual_in = dx
384
- return (dx, dw, db, dresidual_in) if not recompute_output else (dx, dw, db, dresidual_in, y)
385
-
386
-
387
- class LayerNormLinearQuantFn(torch.autograd.Function):
388
-
389
- @staticmethod
390
- @input_guard
391
- def forward(
392
- ctx,
393
- x,
394
- norm_weight,
395
- norm_bias,
396
- linear_weight,
397
- linear_bias,
398
- residual=None,
399
- eps=1e-6,
400
- prenorm=False,
401
- residual_in_fp32=False,
402
- is_rms_norm=False,
403
- ):
404
- x_shape_og = x.shape
405
- # reshape input data into 2D tensor
406
- x = x.reshape(-1, x.shape[-1])
407
- if residual is not None:
408
- assert residual.shape == x_shape_og
409
- residual = residual.reshape(-1, residual.shape[-1])
410
- residual_dtype = residual.dtype if residual is not None else (torch.float32 if residual_in_fp32 else None)
411
- y, mean, rstd, residual_out = layer_norm_fwd_quant(
412
- x,
413
- norm_weight,
414
- norm_bias,
415
- eps,
416
- residual,
417
- out_dtype=None if not torch.is_autocast_enabled() else torch.get_autocast_gpu_dtype(),
418
- residual_dtype=residual_dtype,
419
- is_rms_norm=is_rms_norm,
420
- )
421
- y = y.reshape(x_shape_og)
422
- dtype = torch.get_autocast_gpu_dtype() if torch.is_autocast_enabled() else y.dtype
423
- linear_weight = weight_quant(linear_weight).to(dtype)
424
- linear_bias = linear_bias.to(dtype) if linear_bias is not None else None
425
- out = F.linear(y.to(linear_weight.dtype), linear_weight, linear_bias)
426
- # We don't store y, will be recomputed in the backward pass to save memory
427
- ctx.save_for_backward(residual_out, norm_weight, norm_bias, linear_weight, mean, rstd)
428
- ctx.x_shape_og = x_shape_og
429
- ctx.eps = eps
430
- ctx.is_rms_norm = is_rms_norm
431
- ctx.has_residual = residual is not None
432
- ctx.prenorm = prenorm
433
- ctx.x_dtype = x.dtype
434
- ctx.linear_bias_is_none = linear_bias is None
435
- return out if not prenorm else (out, residual_out.reshape(x_shape_og))
436
-
437
- @staticmethod
438
- @input_guard
439
- def backward(ctx, dout, *args):
440
- x, norm_weight, norm_bias, linear_weight, mean, rstd = ctx.saved_tensors
441
- dout = dout.reshape(-1, dout.shape[-1])
442
- dy = F.linear(dout, linear_weight.t())
443
- dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
444
- assert dy.shape == x.shape
445
- if ctx.prenorm:
446
- dresidual = args[0]
447
- dresidual = dresidual.reshape(-1, dresidual.shape[-1])
448
- assert dresidual.shape == x.shape
449
- else:
450
- dresidual = None
451
- dx, dnorm_weight, dnorm_bias, dresidual_in, y = layer_norm_bwd(
452
- dy,
453
- x,
454
- norm_weight,
455
- norm_bias,
456
- ctx.eps,
457
- mean,
458
- rstd,
459
- dresidual,
460
- ctx.has_residual,
461
- ctx.is_rms_norm,
462
- x_dtype=ctx.x_dtype,
463
- recompute_output=True,
464
- )
465
- dlinear_weight = torch.einsum("bo,bi->oi", dout, y)
466
- return (
467
- dx.reshape(ctx.x_shape_og),
468
- dnorm_weight,
469
- dnorm_bias,
470
- dlinear_weight,
471
- dlinear_bias,
472
- dresidual_in.reshape(ctx.x_shape_og) if ctx.has_residual else None,
473
- None,
474
- None,
475
- None,
476
- None,
477
- )
478
-
479
-
480
- def layer_norm_linear_quant_fn(
481
- x,
482
- norm_weight,
483
- norm_bias,
484
- linear_weight,
485
- linear_bias,
486
- residual=None,
487
- eps=1e-6,
488
- prenorm=False,
489
- residual_in_fp32=False,
490
- is_rms_norm=False,
491
- ):
492
- return LayerNormLinearQuantFn.apply(
493
  x,
494
  norm_weight,
495
  norm_bias,
@@ -505,16 +56,16 @@ def layer_norm_linear_quant_fn(
505
 
506
  def rms_norm_linear_quant(
507
  x: torch.Tensor,
508
- norm_weight: torch.Tensor,
509
- norm_bias: torch.Tensor,
510
  linear_weight: torch.Tensor,
511
- linear_bias: torch.Tensor,
512
- residual: torch.Tensor = None,
513
  eps: float = 1e-5,
514
  prenorm: bool = False,
515
  residual_in_fp32: bool = False,
516
- ):
517
- return layer_norm_linear_quant_fn(
518
  x=x,
519
  norm_weight=norm_weight,
520
  norm_bias=norm_bias,
@@ -528,111 +79,77 @@ def rms_norm_linear_quant(
528
  )
529
 
530
 
531
- @require_version("triton>=3.0", "Triton >= 3.0 is required to do online quantization.")
532
- def bit_linear(x, weight, bias=None, norm_weight=None, norm_bias=None, eps=1e-8):
533
- """
534
- A functional version of BitLinear that applies quantization to activations and weights.
535
-
536
- Args:
537
- x: Input tensor with shape [n, d].
538
- weight: Weight tensor with shape [out_features, in_features].
539
- bias: Bias tensor with shape [out_features] (optional).
540
- norm_weight: Weight tensor for RMS normalization with shape [in_features].
541
- norm_bias: Bias tensor for RMS normalization with shape [in_features].
542
- eps: A small constant for numerical stability in normalization.
543
-
544
- Returns:
545
- Output tensor with shape [n, out_features].
546
- """
547
- return layer_norm_linear_quant_fn(
548
- x,
549
- norm_weight,
550
- norm_bias,
551
- weight,
552
- bias,
553
  is_rms_norm=True,
554
  )
555
 
556
 
557
  class BitLinear(nn.Linear):
558
  """
559
- A custom linear layer that applies quantization on both activations and weights.
560
- This is primarily for training; kernel optimization is needed for efficiency in deployment.
561
- """
 
562
 
563
- def __init__(
564
- self,
565
- in_features: int,
566
- out_features: int,
567
- bias: bool = False,
568
- norm_eps: float = 1e-8,
569
- ):
570
- """
571
- Initializes the BitLinear layer.
 
572
 
573
- Args:
574
- in_features: Size of each input sample.
575
- out_features: Size of each output sample.
576
- bias: If set to False, the layer will not learn an additive bias. Default: True.
577
- """
578
- # Initialize the superclass nn.Linear with the given parameters
579
- super().__init__(in_features, out_features, bias=bias)
580
 
581
- self.norm = RMSNorm(in_features, eps=norm_eps, dtype=torch.float32)
582
 
583
  def __repr__(self) -> str:
584
  return f"{self.__class__.__name__}({super().extra_repr()}, norm_eps={self.norm.eps})"
585
 
586
- def forward(self, x):
587
- """
588
- Overrides the forward pass to include quantization.
589
-
590
- Args:
591
- x: An input tensor with shape [n, d].
592
-
593
- Returns:
594
- An output tensor with shape [n, d].
595
- """
596
- # Weight tensor
597
  w = self.weight
 
598
 
599
- # Apply RMS normalization to the input
600
- x_norm = self.norm(x)
601
-
602
- # Apply quantization to both activations and weights
603
- # Uses Straight-Through Estimator (STE) trick with .detach() for gradient flow
604
- x_quant = x_norm + (activation_quant(x_norm) - x_norm).detach()
605
- w_quant = w + (weight_quant(w) - w).detach()
606
- # Perform linear operation with quantized values
607
- y = F.linear(x_quant, w_quant)
608
 
609
  return y
610
 
611
 
612
  class FusedBitLinear(BitLinear):
613
- """
614
- A custom linear layer that applies quantization on both activations and weights.
615
- This is primarily for training; kernel optimization is needed for efficiency in deployment.
616
- """
617
-
618
- def __init__(self, in_features, out_features, bias=False):
619
- """
620
- Initializes the BitLinear layer.
621
-
622
- Args:
623
- in_features: Size of each input sample.
624
- out_features: Size of each output sample.
625
- bias: If set to False, the layer will not learn an additive bias. Default: True.
626
- """
627
- # Initialize the superclass nn.Linear with the given parameters
628
- super().__init__(in_features, out_features, bias=bias)
629
-
630
- def forward(self, x):
631
- return layer_norm_linear_quant_fn(
632
- x,
633
- self.norm.weight,
634
- self.norm.bias,
635
- self.weight,
636
- self.bias,
637
  is_rms_norm=True,
638
  )
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ # BitLinear with fused normalization and quantized linear projection.
9
  # [The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits](https://arxiv.org/abs/2402.17764)
10
  # [Scalable MatMul-free Language Modeling](https://arxiv.org/abs/2406.02528)
11
  #
12
+ # adapted from https://github.com/ridgerchu/matmulfreellm/
13
 
14
  from __future__ import annotations
15
 
 
 
16
  import torch
17
  import torch.nn as nn
18
  import torch.nn.functional as F
 
 
 
 
 
 
 
19
 
20
+ from ..modules.norm import RMSNorm
21
+ from ..modules.norm.layernorm_quant import LayerNormLinearQuantFunction, weight_quant
22
 
 
 
 
 
 
 
23
 
24
+ def activation_quant(x: torch.Tensor) -> torch.Tensor:
25
+ """Quantize activations to 8 bits per token and return dequantized values."""
 
 
26
  scale = 127.0 / x.abs().max(dim=-1, keepdim=True).values.clamp_(min=1e-5)
27
+ y = (x * scale).round().clamp_(min=-128, max=127) / scale
 
28
  return y
29
 
30
 
31
+ def layer_norm_linear_quant(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  x: torch.Tensor,
33
+ norm_weight: torch.Tensor | None,
34
+ norm_bias: torch.Tensor | None,
35
+ linear_weight: torch.Tensor,
36
+ linear_bias: torch.Tensor | None,
37
+ residual: torch.Tensor | None = None,
38
+ eps: float = 1e-6,
39
+ prenorm: bool = False,
40
+ residual_in_fp32: bool = False,
41
  is_rms_norm: bool = False,
42
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
43
+ return LayerNormLinearQuantFunction.apply(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  x,
45
  norm_weight,
46
  norm_bias,
 
56
 
57
  def rms_norm_linear_quant(
58
  x: torch.Tensor,
59
+ norm_weight: torch.Tensor | None,
60
+ norm_bias: torch.Tensor | None,
61
  linear_weight: torch.Tensor,
62
+ linear_bias: torch.Tensor | None,
63
+ residual: torch.Tensor | None = None,
64
  eps: float = 1e-5,
65
  prenorm: bool = False,
66
  residual_in_fp32: bool = False,
67
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
68
+ return layer_norm_linear_quant(
69
  x=x,
70
  norm_weight=norm_weight,
71
  norm_bias=norm_bias,
 
79
  )
80
 
81
 
82
+ def bit_linear(
83
+ x: torch.Tensor,
84
+ weight: torch.Tensor,
85
+ bias: torch.Tensor | None = None,
86
+ norm_weight: torch.Tensor | None = None,
87
+ norm_bias: torch.Tensor | None = None,
88
+ eps: float = 1e-8,
89
+ ) -> torch.Tensor:
90
+ """Apply RMS normalization and a linear projection with quantized activations and weights."""
91
+ return layer_norm_linear_quant(
92
+ x=x,
93
+ norm_weight=norm_weight,
94
+ norm_bias=norm_bias,
95
+ linear_weight=weight,
96
+ linear_bias=bias,
97
+ eps=eps,
 
 
 
 
 
 
98
  is_rms_norm=True,
99
  )
100
 
101
 
102
  class BitLinear(nn.Linear):
103
  """
104
+ RMS-normalized linear layer with 8-bit activations and ternary weights.
105
+
106
+ Quantization uses a straight-through estimator during training.
107
+ Efficient deployment requires specialized kernels.
108
 
109
+ Args:
110
+ in_features (int):
111
+ Size of each input sample.
112
+ out_features (int):
113
+ Size of each output sample.
114
+ bias (bool, Optional):
115
+ Whether to allocate an additive bias. Default: `False`.
116
+ norm_eps (float, Optional):
117
+ Epsilon for RMS normalization. Default: 1e-8.
118
+ """
119
 
120
+ def __init__(self, in_features: int, out_features: int, bias: bool = False, norm_eps: float = 1e-8) -> None:
121
+ super().__init__(in_features=in_features, out_features=out_features, bias=bias)
 
 
 
 
 
122
 
123
+ self.norm = RMSNorm(hidden_size=in_features, eps=norm_eps, dtype=torch.float32)
124
 
125
  def __repr__(self) -> str:
126
  return f"{self.__class__.__name__}({super().extra_repr()}, norm_eps={self.norm.eps})"
127
 
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
 
 
 
 
 
 
 
 
 
 
129
  w = self.weight
130
+ x_norm = self.norm(x=x)
131
 
132
+ # straight-through gradients bypass quantization
133
+ x_quant = x_norm + (activation_quant(x=x_norm) - x_norm).detach()
134
+ w_quant = w + (weight_quant(w=w) - w).detach()
135
+ y = F.linear(input=x_quant, weight=w_quant)
 
 
 
 
 
136
 
137
  return y
138
 
139
 
140
  class FusedBitLinear(BitLinear):
141
+ """BitLinear with fused RMS normalization and activation quantization."""
142
+
143
+ def __init__(self, in_features: int, out_features: int, bias: bool = False) -> None:
144
+ super().__init__(in_features=in_features, out_features=out_features, bias=bias)
145
+
146
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
147
+ return layer_norm_linear_quant(
148
+ x=x,
149
+ norm_weight=self.norm.weight,
150
+ norm_bias=self.norm.bias,
151
+ linear_weight=self.weight,
152
+ linear_bias=self.bias,
153
+ eps=self.norm.eps,
 
 
 
 
 
 
 
 
 
 
 
154
  is_rms_norm=True,
155
  )
build/torch-cuda/modules/fused_cross_entropy.py DELETED
@@ -1,459 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- from typing import Any
9
-
10
- import torch
11
- import torch.nn as nn
12
- import triton
13
- import triton.language as tl
14
-
15
- from ..modules.backends import dispatch
16
- from ..ops.utils.op import exp, log, tanh
17
- from ..utils import input_guard
18
-
19
- # `all_gather_into_tensor` and `reduce_scatter_tensor` are new placeholders for
20
- # `_all_gather_base` and `_reduce_scatter_base`. They require the most recent
21
- # version of PyTorch. The following 2 lines are for backward compatibility with
22
- # older PyTorch.
23
- if "all_gather_into_tensor" not in dir(torch.distributed):
24
- torch.distributed.all_gather_into_tensor = torch.distributed._all_gather_base
25
-
26
-
27
- @triton.heuristics({
28
- "HAS_SMOOTHING": lambda args: args["label_smoothing"] > 0.0,
29
- "HAS_SOFTCAPPING": lambda args: args["logit_softcapping"] is not None,
30
- })
31
- @triton.jit
32
- def cross_entropy_fwd_kernel(
33
- loss_ptr, # data ptrs
34
- lse_ptr,
35
- z_loss_ptr,
36
- logits_ptr,
37
- labels_ptr,
38
- label_smoothing,
39
- logit_scale,
40
- lse_square_scale,
41
- logit_softcapping,
42
- ignore_index,
43
- total_classes,
44
- class_start_idx, # Useful for tensor parallel when each rank only has a subset of classes
45
- n_cols, # shapes
46
- n_rows,
47
- logits_row_stride, # strides
48
- BLOCK_SIZE: tl.constexpr,
49
- HAS_SMOOTHING: tl.constexpr,
50
- HAS_SOFTCAPPING: tl.constexpr,
51
- # if SPLIT (e.g. tensor parallel), don't include the LSE in the loss since it's not the final LSE
52
- SPLIT: tl.constexpr,
53
- ):
54
- row_idx = tl.program_id(0)
55
- col_block_idx = tl.program_id(1)
56
- logits_ptr = logits_ptr + row_idx * logits_row_stride.to(tl.int64)
57
- col_offsets = col_block_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
58
- label_idx = tl.load(labels_ptr + row_idx)
59
- logits = tl.load(logits_ptr + col_offsets, mask=col_offsets < n_cols, other=-float("inf"))
60
- logits = logits.to(tl.float32) * logit_scale
61
- if HAS_SOFTCAPPING:
62
- logits = logit_softcapping * tanh(logits / logit_softcapping)
63
- max_logits = tl.max(logits, 0)
64
- if HAS_SMOOTHING:
65
- sum_logits = tl.sum(tl.where(col_offsets < n_cols, logits, 0.0), 0)
66
- lse = log(tl.sum(exp(logits - max_logits), 0)) + max_logits
67
- tl.store(lse_ptr + col_block_idx * n_rows + row_idx, lse)
68
- if label_idx == ignore_index:
69
- loss = 0.0
70
- z_loss = 0.0
71
- else:
72
- label_idx -= class_start_idx
73
- if label_idx >= col_block_idx * BLOCK_SIZE and label_idx < min(
74
- n_cols, (col_block_idx + 1) * BLOCK_SIZE,
75
- ):
76
- logits_label = tl.load(logits_ptr + label_idx).to(tl.float32) * logit_scale
77
- if HAS_SOFTCAPPING:
78
- logits_label = logit_softcapping * tanh(logits_label / logit_softcapping)
79
- if HAS_SMOOTHING:
80
- loss = (
81
- (lse if not SPLIT else 0.0)
82
- - label_smoothing * sum_logits / total_classes
83
- - (1 - label_smoothing) * logits_label
84
- )
85
- else:
86
- loss = (lse if not SPLIT else 0.0) - logits_label
87
- else:
88
- # If label is out of bounds, we set the CE loss to 0.0. But we still want the label_smoothing loss
89
- if HAS_SMOOTHING:
90
- loss = label_smoothing * ((lse if not SPLIT else 0.0) - sum_logits / total_classes)
91
- else:
92
- loss = 0.0
93
- if not SPLIT:
94
- z_loss = lse_square_scale * lse * lse
95
- loss += z_loss
96
- else:
97
- z_loss = 0.0
98
- tl.store(loss_ptr + col_block_idx * n_rows + row_idx, loss)
99
- if not SPLIT:
100
- tl.store(z_loss_ptr + col_block_idx * n_rows + row_idx, z_loss)
101
-
102
-
103
- @triton.heuristics({
104
- "HAS_SMOOTHING": lambda args: args["label_smoothing"] > 0.0,
105
- "HAS_SOFTCAPPING": lambda args: args["logit_softcapping"] is not None,
106
- })
107
- @triton.jit
108
- def cross_entropy_bwd_kernel(
109
- dlogits_ptr, # data ptrs
110
- dloss_ptr,
111
- logits_ptr,
112
- lse_ptr,
113
- labels_ptr,
114
- label_smoothing,
115
- logit_scale,
116
- lse_square_scale,
117
- logit_softcapping,
118
- ignore_index,
119
- total_classes,
120
- class_start_idx, # Useful for tensor parallel when each rank only has a subset of classes
121
- n_cols, # shapes
122
- logits_row_stride, # strides
123
- dlogits_row_stride,
124
- dloss_row_stride,
125
- BLOCK_SIZE: tl.constexpr,
126
- HAS_SMOOTHING: tl.constexpr,
127
- HAS_SOFTCAPPING: tl.constexpr,
128
- ):
129
- row_idx = tl.program_id(0)
130
- col_block_idx = tl.program_id(1)
131
- logits_ptr = logits_ptr + row_idx * logits_row_stride.to(tl.int64)
132
- dlogits_ptr = dlogits_ptr + row_idx * dlogits_row_stride.to(tl.int64)
133
- col_offsets = col_block_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
134
- label_idx = tl.load(labels_ptr + row_idx)
135
- if label_idx != ignore_index:
136
- dloss = tl.load(dloss_ptr + row_idx * dloss_row_stride)
137
- else:
138
- dloss = 0.0
139
- logits = tl.load(logits_ptr + col_offsets, mask=col_offsets < n_cols, other=-float("inf")).to(
140
- tl.float32,
141
- ) * logit_scale
142
- if HAS_SOFTCAPPING:
143
- t = tanh(logits / logit_softcapping)
144
- logits = logit_softcapping * t
145
- lse = tl.load(lse_ptr + row_idx)
146
- probs = exp(logits - lse)
147
- probs += 2.0 * lse_square_scale * lse * probs
148
- label_idx -= class_start_idx
149
- if HAS_SMOOTHING:
150
- smooth_negative = label_smoothing / total_classes
151
- probs = tl.where(col_offsets == label_idx, probs - (1 - label_smoothing), probs) - smooth_negative
152
- else:
153
- probs = tl.where(col_offsets == label_idx, probs - 1.0, probs)
154
- # d(softcap * tanh(x/softcap))/dx = 1 - tanh(x/softcap)^2
155
- if HAS_SOFTCAPPING:
156
- probs = probs * (1.0 - t * t)
157
- tl.store(dlogits_ptr + col_offsets, (dloss * logit_scale) * probs, mask=col_offsets < n_cols)
158
-
159
-
160
- def fused_cross_entropy_forward(
161
- logits: torch.Tensor,
162
- target: torch.Tensor,
163
- label_smoothing: float = 0.0,
164
- logit_scale: float = 1.0,
165
- lse_square_scale: float = 0.0,
166
- logit_softcapping: float = None,
167
- ignore_index: int = -100,
168
- process_group=None,
169
- ):
170
- n_rows, n_cols = logits.shape
171
- assert target.shape == (n_rows,)
172
- world_size = 1 if process_group is None else torch.distributed.get_world_size(process_group)
173
- total_classes = world_size * n_cols
174
- rank = 0 if process_group is None else torch.distributed.get_rank(process_group)
175
- class_start_idx = rank * n_cols
176
-
177
- if logits.stride(-1) != 1:
178
- logits = logits.contiguous()
179
- # Set these similar to https://github.com/triton-lang/triton/blob/main/python/tutorials/02-fused-softmax.py
180
- MAX_BLOCK_SIZE = 64 * 1024
181
- BLOCK_SIZE = min(triton.next_power_of_2(n_cols), MAX_BLOCK_SIZE)
182
- num_warps = (
183
- 4
184
- if BLOCK_SIZE < 2048
185
- else (8 if BLOCK_SIZE < 8192 else (16 if BLOCK_SIZE < 128 * 1024 else 32))
186
- )
187
- # We may split the lse computation across multiple blocks, then do a reduction
188
- # lse(local_lse) to get the final LSE. This is faster for large n_cols (e.g., > 64k)
189
- # where having just one thread block processing more than 64k elements is slow.
190
- split = world_size > 1 or n_cols > MAX_BLOCK_SIZE
191
- n_splits = (n_cols + BLOCK_SIZE - 1) // BLOCK_SIZE
192
- loss_shape = (n_splits, n_rows) if n_splits > 1 else (n_rows,)
193
- losses = torch.empty(*loss_shape, dtype=torch.float, device=logits.device)
194
- lse = torch.empty(*loss_shape, dtype=torch.float, device=logits.device)
195
- z_losses = torch.empty(*loss_shape, dtype=torch.float, device=logits.device)
196
-
197
- cross_entropy_fwd_kernel[(n_rows, n_splits)](
198
- losses, # data ptrs
199
- lse,
200
- z_losses,
201
- logits,
202
- target,
203
- label_smoothing,
204
- logit_scale,
205
- lse_square_scale,
206
- logit_softcapping,
207
- ignore_index,
208
- total_classes,
209
- class_start_idx,
210
- n_cols, # shapes
211
- n_rows,
212
- logits.stride(0), # strides
213
- BLOCK_SIZE=BLOCK_SIZE, # constants
214
- num_warps=num_warps,
215
- SPLIT=split,
216
- )
217
-
218
- if split:
219
- # If there's no label_smoothing, if target are in the vocab of this partition, losses contains
220
- # - predicted logit, and 0 otherwise.
221
- # If there's label_smoothing=0.1, for target in the vocab of this partition, losses contains
222
- # -0.9 * predicted logit - 0.1 * sum logit / total_classes.
223
- # For target not in the vocab of this partition, losses contains
224
- # -0.1 * sum logit / total_classes.
225
- if n_splits > 1:
226
- lse = torch.logsumexp(lse, dim=0)
227
- losses = losses.sum(dim=0)
228
- if world_size > 1:
229
- lse_allgather = torch.empty(world_size, n_rows, dtype=lse.dtype, device=lse.device)
230
- torch.distributed.all_gather_into_tensor(lse_allgather, lse, group=process_group)
231
- handle_losses = torch.distributed.all_reduce(
232
- losses, op=torch.distributed.ReduceOp.SUM, group=process_group, async_op=True,
233
- )
234
- lse = torch.logsumexp(lse_allgather, dim=0)
235
- handle_losses.wait()
236
- # After the allreduce, if there's no label_smoothing, the total losses are - predicted_logit,
237
- # we just have to add the (global) lse.
238
- # If there's label_smoothing=0.1, the total losses are
239
- # -0.9 * predicted_logit - 0.1 * sum logit / total_classes.
240
- # Again, we just have to add the (global) lse.
241
- losses += lse
242
- if lse_square_scale != 0.0:
243
- z_losses = lse_square_scale * lse.square()
244
- z_losses.masked_fill_(target == ignore_index, 0.0)
245
- losses += z_losses
246
- else:
247
- z_losses = torch.zeros_like(losses)
248
- losses.masked_fill_(target == ignore_index, 0.0)
249
-
250
- return losses, z_losses, lse, total_classes, class_start_idx
251
-
252
-
253
- class CrossEntropyLossFunction(torch.autograd.Function):
254
-
255
- @staticmethod
256
- @input_guard
257
- def forward(
258
- ctx,
259
- logits,
260
- target,
261
- label_smoothing=0.0,
262
- logit_scale=1.0,
263
- lse_square_scale=0.0,
264
- logit_softcapping=None,
265
- ignore_index=-100,
266
- inplace_backward=False,
267
- process_group=None,
268
- ):
269
- losses, z_losses, lse, total_classes, class_start_idx = fused_cross_entropy_forward(
270
- logits,
271
- target,
272
- label_smoothing,
273
- logit_scale,
274
- lse_square_scale,
275
- logit_softcapping,
276
- ignore_index,
277
- process_group,
278
- )
279
- ctx.save_for_backward(logits, lse, target)
280
- ctx.mark_non_differentiable(z_losses)
281
- ctx.label_smoothing = label_smoothing
282
- ctx.logit_scale = logit_scale
283
- ctx.lse_square_scale = lse_square_scale
284
- ctx.logit_softcapping = logit_softcapping
285
- ctx.ignore_index = ignore_index
286
- ctx.total_classes = total_classes
287
- ctx.class_start_idx = class_start_idx
288
- ctx.inplace_backward = inplace_backward
289
-
290
- return losses, z_losses
291
-
292
- @staticmethod
293
- @input_guard
294
- def backward(ctx, grad_losses, grad_z_losses):
295
- del grad_z_losses # z_losses are only for logging.
296
-
297
- logits, lse, target = ctx.saved_tensors
298
- dlogits = logits if ctx.inplace_backward else torch.empty_like(logits)
299
- n_rows, n_cols = logits.shape
300
- BLOCK_SIZE = min(triton.next_power_of_2(n_cols), 4 * 1024)
301
- num_warps = 4 if BLOCK_SIZE < 2048 else (8 if BLOCK_SIZE < 8192 else 16)
302
- def grid(META): return (n_rows, triton.cdiv(n_cols, META["BLOCK_SIZE"])) # noqa
303
- cross_entropy_bwd_kernel[grid](
304
- dlogits, # data ptrs
305
- grad_losses,
306
- logits,
307
- lse,
308
- target,
309
- ctx.label_smoothing,
310
- ctx.logit_scale,
311
- ctx.lse_square_scale,
312
- ctx.logit_softcapping,
313
- ctx.ignore_index,
314
- ctx.total_classes,
315
- ctx.class_start_idx,
316
- n_cols, # shapes
317
- logits.stride(0), # strides
318
- dlogits.stride(0),
319
- grad_losses.stride(0),
320
- BLOCK_SIZE=BLOCK_SIZE, # constants
321
- num_warps=num_warps,
322
- )
323
- return dlogits, None, None, None, None, None, None, None, None, None
324
-
325
-
326
- @dispatch('modules')
327
- def cross_entropy_loss(
328
- logits: torch.Tensor,
329
- target: torch.Tensor,
330
- label_smoothing: float = 0.0,
331
- logit_scale: float = 1.0,
332
- lse_square_scale: float = 0.0,
333
- logit_softcapping: float = None,
334
- ignore_index=-100,
335
- inplace_backward: bool = False,
336
- process_group=None,
337
- ) -> tuple[torch.Tensor, torch.Tensor]:
338
- """
339
- Arguments:
340
- logits: [batch, vocab_size]
341
- target: [batch,]
342
- label_smoothing: float
343
- logit_scale: float.
344
- Multiply logits by this scale before calculating the loss.
345
- lse_square_scale: float.
346
- If > 0, we add lse_square_scale * lse(logits) ^ 2 to the loss.
347
- This is also referred to as "z-loss".
348
- logit_softcapping: float.
349
- If > 0, apply logit softcapping: logits = softcap * tanh(logits / softcap).
350
- This prevents logit magnitudes from growing unboundedly.
351
- ignore_index: int.
352
- If target == ignore_index, the loss is set to 0.0.
353
- inplace_backward: bool.
354
- If True, we do the backward pass in-place by modifying the logits.
355
- This saves memory.
356
- process_group:
357
- if not None, we're doing Tensor Parallel: each process is responsible for
358
- one part of the vocab. The loss will be aggregated across processes.
359
- Returns:
360
- losses: [batch,], float
361
- z_losses: [batch,], float
362
- """
363
- return CrossEntropyLossFunction.apply(
364
- logits,
365
- target,
366
- label_smoothing,
367
- logit_scale,
368
- lse_square_scale,
369
- logit_softcapping,
370
- ignore_index,
371
- inplace_backward,
372
- process_group,
373
- )
374
-
375
-
376
- class FusedCrossEntropyLoss(nn.Module):
377
- def __init__(
378
- self,
379
- ignore_index: int = -100,
380
- reduction: str = "mean",
381
- label_smoothing: float = 0.0,
382
- logit_scale: float = 1.0,
383
- lse_square_scale: float = 0.0,
384
- logit_softcapping: float = None,
385
- inplace_backward: bool = False,
386
- process_group: Any = None,
387
- return_z_loss: bool = False,
388
- ):
389
- """
390
- Arguments:
391
- ignore_index: int. If target == ignore_index, the loss is set to 0.0.
392
- label_smoothing: float
393
- lse_square_scale: float. If > 0, we add lse_square_scale * lse(logits) ^ 2 to the loss.
394
- This is also referred to as "z-loss".
395
- logit_softcapping: float. If > 0, apply logit softcapping:
396
- logits = softcap * tanh(logits / softcap).
397
- This prevents logit magnitudes from growing unboundedly.
398
- inplace_backward: bool. If True, we do the backward pass in-place by modifying the logits.
399
- This saves memory.
400
- process_group: if not None, we're doing Tensor Parallel: each process is responsible for
401
- one part of the vocab. The loss will be aggregated across processes.
402
- return_z_loss: bool. If True, we return the component of the loss contributed by
403
- the lse_square_scale value. This value is only for logging and does not support
404
- backprop.
405
- """
406
- super().__init__()
407
- if reduction not in ["mean", "none", "sum"]:
408
- raise NotImplementedError("Only support reduction = 'mean' or 'none' or 'sum'")
409
- self.ignore_index = ignore_index
410
- self.reduction = reduction
411
- self.label_smoothing = label_smoothing
412
- self.logit_scale = logit_scale
413
- self.lse_square_scale = lse_square_scale
414
- self.logit_softcapping = logit_softcapping
415
- self.inplace_backward = inplace_backward
416
- self.process_group = process_group
417
- self.return_z_loss = return_z_loss
418
-
419
- def forward(self, input, target):
420
- """
421
- Arguments:
422
- input: (batch, vocab_size)
423
- target: (batch,)
424
- Returns:
425
- losses: (batch,) if reduction is 'none', else (1,), dtype float
426
- z_loss: (batch,) if reduction is 'none', else (1,), dtype float (if self.return_z_loss)
427
- """
428
- assert input.device.type in ('cuda', 'npu') and target.device.type in ('cuda', 'npu'), (
429
- "Only support CUDA/NPU tensors"
430
- )
431
- loss, z_loss = cross_entropy_loss(
432
- input,
433
- target,
434
- label_smoothing=self.label_smoothing,
435
- logit_scale=self.logit_scale,
436
- lse_square_scale=self.lse_square_scale,
437
- logit_softcapping=self.logit_softcapping,
438
- ignore_index=self.ignore_index,
439
- inplace_backward=self.inplace_backward,
440
- process_group=self.process_group,
441
- )
442
- if self.reduction == "mean":
443
- loss = loss.sum() / (target != self.ignore_index).sum()
444
- elif self.reduction == "sum":
445
- loss = loss.sum()
446
- else:
447
- loss = loss
448
-
449
- if not self.return_z_loss:
450
- return loss
451
-
452
- if self.reduction == "mean":
453
- z_loss = z_loss.sum() / (target != self.ignore_index).sum()
454
- elif self.reduction == "sum":
455
- z_loss = z_loss.sum()
456
- else:
457
- z_loss = z_loss
458
-
459
- return loss, z_loss
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch-cuda/modules/fused_cross_entropy/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ from ...backends import GluonBackend
9
+ from ...modules.fused_cross_entropy.ops import FusedCrossEntropyLoss, cross_entropy_loss
10
+
11
+ __all__ = ['FusedCrossEntropyLoss', 'cross_entropy_loss']
12
+
13
+
14
+ if GluonBackend.is_available():
15
+ from ...modules.fused_cross_entropy import gluon # noqa: F401
build/torch-cuda/modules/fused_cross_entropy/gluon.py ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from typing import TYPE_CHECKING
12
+
13
+ import torch
14
+ import triton
15
+ from triton.experimental import gluon
16
+ from triton.experimental.gluon import language as gl
17
+ from triton.experimental.gluon.language import extra
18
+
19
+ from ...backends import GluonBackend, register
20
+ from ...modules.fused_cross_entropy.ops import cross_entropy_bwd, cross_entropy_fwd
21
+
22
+ if TYPE_CHECKING:
23
+ from torch.distributed import ProcessGroup
24
+
25
+
26
+ if os.environ.get('FLA_USE_FAST_OPS', '0') == '1':
27
+ exp = extra.libdevice.fast_expf
28
+ log = extra.libdevice.fast_logf
29
+ else:
30
+ exp = gl.exp
31
+ log = gl.log
32
+ tanh = extra.libdevice.tanh
33
+
34
+
35
+ @gluon.jit(do_not_specialize=['N'])
36
+ def cross_entropy_fwd_kernel_gluon(
37
+ logits, # [N, V]
38
+ target, # [N]
39
+ loss, # [NV, N] or [N] when NV == 1
40
+ z_loss, # [N]
41
+ lse, # [NV, N] or [N] when NV == 1
42
+ s_logits,
43
+ label_smoothing: gl.constexpr,
44
+ logit_scale: gl.constexpr,
45
+ lse_square_scale: gl.constexpr,
46
+ logit_softcapping: gl.constexpr,
47
+ ignore_index: gl.constexpr,
48
+ N,
49
+ V: gl.constexpr,
50
+ V_TOTAL: gl.constexpr,
51
+ V_START: gl.constexpr,
52
+ BV: gl.constexpr,
53
+ SPLIT: gl.constexpr,
54
+ ):
55
+ NW: gl.constexpr = 4
56
+ R_MAX: gl.constexpr = 128 // logits.dtype.element_ty.primitive_bitwidth
57
+ R: gl.constexpr = (V & -V) if (V & -V) < R_MAX else R_MAX
58
+ layout: gl.constexpr = gl.BlockedLayout([R], [32], [NW], [0])
59
+ i_n = gl.program_id(0).to(gl.int64)
60
+ i_v = gl.program_id(1).to(gl.int64)
61
+ o_v = i_v * BV + gl.arange(0, BV, layout=layout).to(gl.int64)
62
+ m_v = o_v < V
63
+ p_logits = logits + i_n * s_logits
64
+
65
+ # [BV]
66
+ b_logits = gl.load(p_logits + o_v, mask=m_v, other=-float('inf')).to(gl.float32) * logit_scale
67
+ if logit_softcapping is not None:
68
+ b_logits = logit_softcapping * tanh(b_logits / logit_softcapping)
69
+ # these transforms do not preserve -inf in padded lanes
70
+ if logit_softcapping is not None or logit_scale <= 0:
71
+ b_logits = gl.where(m_v, b_logits, -float('inf'))
72
+ if label_smoothing > 0:
73
+ b_sum = gl.sum(gl.where(m_v, b_logits, 0.0), 0)
74
+ b_max = gl.max(b_logits, 0)
75
+ b_lse = log(gl.sum(exp(b_logits - b_max), 0)) + b_max
76
+
77
+ b_target = gl.load(target + i_n).to(gl.int64)
78
+ b_loss = 0.0
79
+ b_z_loss = 0.0
80
+ if b_target != ignore_index:
81
+ b_target -= V_START
82
+ m_target = (b_target >= i_v * BV) & (b_target < gl.minimum(V, (i_v + 1) * BV))
83
+ b_target_logit = gl.load(p_logits + b_target, mask=m_target, other=0).to(gl.float32) * logit_scale
84
+ if logit_softcapping is not None:
85
+ b_target_logit = logit_softcapping * tanh(b_target_logit / logit_softcapping)
86
+ b_loss = -b_target_logit
87
+ if label_smoothing > 0:
88
+ b_loss = (1 - label_smoothing) * b_loss - label_smoothing * b_sum / V_TOTAL
89
+ if not SPLIT:
90
+ b_z_loss = lse_square_scale * b_lse * b_lse
91
+ b_loss += b_lse + b_z_loss
92
+
93
+ gl.store(loss + i_v * N + i_n, b_loss)
94
+ gl.store(lse + i_v * N + i_n, b_lse)
95
+ if not SPLIT:
96
+ gl.store(z_loss + i_n, b_z_loss)
97
+
98
+
99
+ @gluon.jit
100
+ def cross_entropy_bwd_kernel_gluon(
101
+ logits, # [N, V]
102
+ target, # [N]
103
+ lse, # [N]
104
+ dloss, # [N]
105
+ dlogits, # [N, V]
106
+ s_logits,
107
+ s_dloss,
108
+ s_dlogits,
109
+ label_smoothing: gl.constexpr,
110
+ logit_scale: gl.constexpr,
111
+ lse_square_scale: gl.constexpr,
112
+ logit_softcapping: gl.constexpr,
113
+ ignore_index: gl.constexpr,
114
+ V: gl.constexpr,
115
+ V_TOTAL: gl.constexpr,
116
+ V_START: gl.constexpr,
117
+ BV: gl.constexpr,
118
+ ):
119
+ NW: gl.constexpr = 4
120
+ R_MAX: gl.constexpr = 128 // logits.dtype.element_ty.primitive_bitwidth
121
+ R: gl.constexpr = (V & -V) if (V & -V) < R_MAX else R_MAX
122
+ layout: gl.constexpr = gl.BlockedLayout([R], [32], [NW], [0])
123
+ i_n = gl.program_id(0).to(gl.int64)
124
+ i_v = gl.program_id(1).to(gl.int64)
125
+ o_local = gl.arange(0, BV, layout=layout)
126
+ o_v = i_v * BV + o_local.to(gl.int64)
127
+ if R == R_MAX and V > BV:
128
+ m_v = o_local < gl.minimum(V - i_v * BV, BV).to(gl.int32)
129
+ else:
130
+ m_v = o_v < V
131
+ p_logits = logits + i_n * s_logits + o_v
132
+ p_dlogits = dlogits + i_n * s_dlogits + o_v
133
+
134
+ b_target = gl.load(target + i_n).to(gl.int64)
135
+ b_dloss = gl.load(dloss + i_n * s_dloss, mask=b_target != ignore_index, other=0).to(gl.float32)
136
+ # [BV]
137
+ b_logits = gl.load(p_logits, mask=m_v, other=0).to(gl.float32) * logit_scale
138
+ if logit_softcapping is not None:
139
+ b_tanh = tanh(b_logits / logit_softcapping)
140
+ b_logits = logit_softcapping * b_tanh
141
+ b_lse = gl.load(lse + i_n)
142
+ # [BV]
143
+ b_probs = exp(b_logits - b_lse)
144
+ b_dlogits = b_probs + 2.0 * lse_square_scale * b_lse * b_probs
145
+ TARGET_WEIGHT: gl.constexpr = 1 - label_smoothing if label_smoothing > 0 else 1.0
146
+ b_target -= V_START
147
+ if R == R_MAX and V > BV:
148
+ b_target -= i_v * BV
149
+ if (b_target >= 0) & (b_target < BV):
150
+ b_dlogits -= gl.where(o_local == b_target.to(gl.int32), TARGET_WEIGHT, 0.0)
151
+ else:
152
+ b_dlogits -= gl.where(o_v == b_target, TARGET_WEIGHT, 0.0)
153
+ if label_smoothing > 0:
154
+ b_dlogits -= label_smoothing / V_TOTAL
155
+ if logit_softcapping is not None:
156
+ b_dlogits *= 1.0 - b_tanh * b_tanh
157
+ b_dlogits *= b_dloss * logit_scale
158
+ gl.store(p_dlogits, b_dlogits, mask=m_v)
159
+
160
+
161
+ @gluon.jit(do_not_specialize=['N'])
162
+ def cross_entropy_fwd_kernel_reduce_gluon(
163
+ partial_loss,
164
+ partial_lse,
165
+ target,
166
+ loss,
167
+ z_loss,
168
+ lse,
169
+ lse_square_scale: gl.constexpr,
170
+ ignore_index: gl.constexpr,
171
+ N,
172
+ NV: gl.constexpr,
173
+ BN: gl.constexpr,
174
+ BS: gl.constexpr,
175
+ SPLIT: gl.constexpr,
176
+ REDUCE_LOSS: gl.constexpr,
177
+ ):
178
+ # keep partition reductions within a warp to avoid shared memory
179
+ P: gl.constexpr = 128 // BN
180
+ layout: gl.constexpr = gl.BlockedLayout([1, 1], [P, 32 // P], [1, 4], [1, 0])
181
+ o_n = gl.program_id(0).to(gl.int64) * BN + gl.arange(0, BN, layout=gl.SliceLayout(0, layout))
182
+ o_s = gl.arange(0, BS, layout=gl.SliceLayout(1, layout)).to(gl.int64)
183
+ offsets = o_s[:, None] * N + o_n[None, :]
184
+ mask = (o_s[:, None] < NV) & (o_n[None, :] < N)
185
+ b_lse = gl.load(partial_lse + offsets, mask=mask, other=-float('inf')).to(gl.float32)
186
+ b_max = gl.max(b_lse, 0)
187
+ b_lse = b_max + log(gl.sum(exp(b_lse - b_max[None, :]), 0))
188
+ if REDUCE_LOSS:
189
+ b_loss = gl.sum(gl.load(partial_loss + offsets, mask=mask, other=0).to(gl.float32), 0)
190
+ else:
191
+ b_loss = gl.load(partial_loss + o_n, mask=o_n < N, other=0).to(gl.float32)
192
+ if not SPLIT:
193
+ valid = gl.load(target + o_n, mask=o_n < N, other=ignore_index) != ignore_index
194
+ b_z_loss = gl.where(valid, lse_square_scale * b_lse * b_lse, 0.)
195
+ b_loss = gl.where(valid, b_loss + b_lse + b_z_loss, 0.)
196
+ gl.store(z_loss + o_n, b_z_loss, mask=o_n < N)
197
+ gl.store(loss + o_n, b_loss, mask=o_n < N)
198
+ gl.store(lse + o_n, b_lse, mask=o_n < N)
199
+
200
+
201
+ def cross_entropy_reduce_gluon(
202
+ loss: torch.Tensor,
203
+ z_loss: torch.Tensor,
204
+ lse: torch.Tensor,
205
+ target: torch.Tensor,
206
+ lse_square_scale: float,
207
+ ignore_index: int,
208
+ process_group: ProcessGroup | None,
209
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
210
+ N = target.shape[0]
211
+ world_size = 1 if process_group is None else torch.distributed.get_world_size(group=process_group)
212
+ if lse.ndim == 2:
213
+ NV = lse.shape[0]
214
+ BS = triton.next_power_of_2(NV)
215
+ BN = 128 // max(1, min(8, BS // 4, 16384 // triton.next_power_of_2(N)))
216
+ reduced_loss = loss.new_empty(N)
217
+ reduced_lse = torch.empty_like(reduced_loss)
218
+ cross_entropy_fwd_kernel_reduce_gluon[(triton.cdiv(N, BN),)](
219
+ partial_loss=loss,
220
+ partial_lse=lse,
221
+ target=target,
222
+ loss=reduced_loss,
223
+ z_loss=z_loss,
224
+ lse=reduced_lse,
225
+ lse_square_scale=lse_square_scale,
226
+ ignore_index=ignore_index,
227
+ N=N,
228
+ NV=NV,
229
+ BN=BN,
230
+ BS=BS,
231
+ SPLIT=world_size > 1,
232
+ REDUCE_LOSS=True,
233
+ num_warps=4,
234
+ )
235
+ loss, lse = reduced_loss, reduced_lse
236
+ if world_size > 1:
237
+ BS = triton.next_power_of_2(world_size)
238
+ BN = 128 // max(1, min(8, BS // 4, 16384 // triton.next_power_of_2(N)))
239
+ gathered_lse = lse.new_empty(world_size, N)
240
+ torch.distributed.all_gather_into_tensor(output_tensor=gathered_lse, input_tensor=lse, group=process_group)
241
+ torch.distributed.all_reduce(tensor=loss, group=process_group)
242
+ cross_entropy_fwd_kernel_reduce_gluon[(triton.cdiv(N, BN),)](
243
+ partial_loss=loss,
244
+ partial_lse=gathered_lse,
245
+ target=target,
246
+ loss=loss,
247
+ z_loss=z_loss,
248
+ lse=lse,
249
+ lse_square_scale=lse_square_scale,
250
+ ignore_index=ignore_index,
251
+ N=N,
252
+ NV=world_size,
253
+ BN=BN,
254
+ BS=BS,
255
+ SPLIT=False,
256
+ REDUCE_LOSS=False,
257
+ num_warps=4,
258
+ )
259
+ return loss, z_loss, lse
260
+
261
+
262
+ def cross_entropy_fwd_verifier(
263
+ logits: torch.Tensor,
264
+ target: torch.Tensor,
265
+ label_smoothing: float = 0.0,
266
+ logit_scale: float = 1.0,
267
+ lse_square_scale: float = 0.0,
268
+ logit_softcapping: float | None = None,
269
+ ignore_index: int = -100,
270
+ process_group: ProcessGroup | None = None,
271
+ ) -> tuple[bool, str | None]:
272
+ if logits.device.type != 'cuda' or target.device != logits.device:
273
+ return False, "Logits and targets must be on the same CUDA device"
274
+ if logits.dtype not in (torch.float16, torch.bfloat16, torch.float32):
275
+ return False, "Gluon cross entropy supports fp16, bf16, and fp32 logits"
276
+ return True, None
277
+
278
+
279
+ @register(cross_entropy_fwd, backend=GluonBackend, verifier=cross_entropy_fwd_verifier, env_var='FLA_CE_GLUON')
280
+ def cross_entropy_fwd_gluon(
281
+ logits: torch.Tensor,
282
+ target: torch.Tensor,
283
+ label_smoothing: float = 0.0,
284
+ logit_scale: float = 1.0,
285
+ lse_square_scale: float = 0.0,
286
+ logit_softcapping: float | None = None,
287
+ ignore_index: int = -100,
288
+ process_group: ProcessGroup | None = None,
289
+ ):
290
+ N, V = logits.shape
291
+ assert target.shape == (N,)
292
+ world_size = 1 if process_group is None else torch.distributed.get_world_size(group=process_group)
293
+ total_classes = world_size * V
294
+ rank = 0 if process_group is None else torch.distributed.get_rank(group=process_group)
295
+ class_start_idx = rank * V
296
+
297
+ if logits.stride(-1) != 1:
298
+ logits = logits.contiguous()
299
+ BV = triton.next_power_of_2(V)
300
+ if logit_softcapping is not None:
301
+ BV = min(BV, 2048)
302
+ elif V > 16384:
303
+ BV = 8192
304
+ # vocab partitions contribute partial loss until the global LSE is available
305
+ NV = triton.cdiv(V, BV)
306
+ split = world_size > 1 or NV > 1
307
+ shape = (NV, N) if NV > 1 else (N,)
308
+ loss = logits.new_empty(shape, dtype=torch.float)
309
+ lse = torch.empty_like(loss)
310
+ z_loss = logits.new_empty(N, dtype=torch.float)
311
+
312
+ cross_entropy_fwd_kernel_gluon[(N, NV)](
313
+ logits=logits,
314
+ target=target,
315
+ loss=loss,
316
+ z_loss=z_loss,
317
+ lse=lse,
318
+ s_logits=logits.stride(0),
319
+ label_smoothing=label_smoothing,
320
+ logit_scale=logit_scale,
321
+ lse_square_scale=lse_square_scale,
322
+ logit_softcapping=logit_softcapping,
323
+ ignore_index=ignore_index,
324
+ N=N,
325
+ V=V,
326
+ V_TOTAL=total_classes,
327
+ V_START=class_start_idx,
328
+ BV=BV,
329
+ SPLIT=split,
330
+ num_warps=4,
331
+ )
332
+
333
+ if split:
334
+ loss, z_loss, lse = cross_entropy_reduce_gluon(
335
+ loss=loss,
336
+ z_loss=z_loss,
337
+ lse=lse,
338
+ target=target,
339
+ lse_square_scale=lse_square_scale,
340
+ ignore_index=ignore_index,
341
+ process_group=process_group,
342
+ )
343
+
344
+ return loss, z_loss, lse, total_classes, class_start_idx
345
+
346
+
347
+ def cross_entropy_bwd_verifier(
348
+ logits: torch.Tensor,
349
+ target: torch.Tensor,
350
+ lse: torch.Tensor,
351
+ dloss: torch.Tensor,
352
+ label_smoothing: float = 0.0,
353
+ logit_scale: float = 1.0,
354
+ lse_square_scale: float = 0.0,
355
+ logit_softcapping: float | None = None,
356
+ ignore_index: int = -100,
357
+ total_classes: int | None = None,
358
+ class_start_idx: int = 0,
359
+ inplace_backward: bool = False,
360
+ ) -> tuple[bool, str | None]:
361
+ return cross_entropy_fwd_verifier(logits=logits, target=target)
362
+
363
+
364
+ @register(cross_entropy_bwd, backend=GluonBackend, verifier=cross_entropy_bwd_verifier, env_var='FLA_CE_GLUON')
365
+ def cross_entropy_bwd_gluon(
366
+ logits: torch.Tensor,
367
+ target: torch.Tensor,
368
+ lse: torch.Tensor,
369
+ dloss: torch.Tensor,
370
+ label_smoothing: float = 0.0,
371
+ logit_scale: float = 1.0,
372
+ lse_square_scale: float = 0.0,
373
+ logit_softcapping: float | None = None,
374
+ ignore_index: int = -100,
375
+ total_classes: int | None = None,
376
+ class_start_idx: int = 0,
377
+ inplace_backward: bool = False,
378
+ ) -> torch.Tensor:
379
+ N, V = logits.shape
380
+ BV = min(triton.next_power_of_2(V), 1024 if logit_softcapping is not None else 4096)
381
+ dlogits = logits if inplace_backward else torch.empty_like(logits)
382
+ cross_entropy_bwd_kernel_gluon[(N, triton.cdiv(V, BV))](
383
+ logits=logits,
384
+ target=target,
385
+ lse=lse,
386
+ dloss=dloss,
387
+ dlogits=dlogits,
388
+ s_logits=logits.stride(0),
389
+ s_dloss=dloss.stride(0),
390
+ s_dlogits=dlogits.stride(0),
391
+ label_smoothing=label_smoothing,
392
+ logit_scale=logit_scale,
393
+ lse_square_scale=lse_square_scale,
394
+ logit_softcapping=logit_softcapping,
395
+ ignore_index=ignore_index,
396
+ V=V,
397
+ V_TOTAL=V if total_classes is None else total_classes,
398
+ V_START=class_start_idx,
399
+ BV=BV,
400
+ num_warps=4,
401
+ )
402
+ return dlogits
build/torch-cuda/modules/fused_cross_entropy/ops.py ADDED
@@ -0,0 +1,538 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING
11
+
12
+ import torch
13
+ import torch.nn as nn
14
+ import triton
15
+ import triton.language as tl
16
+ from triton.language.extra.libdevice import tanh
17
+
18
+ from ...backends import dispatch
19
+ from ...ops.utils.op import exp, log
20
+ from ...utils import autocast_custom_bwd, autocast_custom_fwd, input_guard
21
+
22
+ if TYPE_CHECKING:
23
+ from torch.distributed import ProcessGroup
24
+
25
+
26
+ @triton.jit(do_not_specialize=['N'])
27
+ def cross_entropy_fwd_kernel(
28
+ logits, # [N, V]
29
+ target, # [N]
30
+ loss, # [NV, N] or [N] when NV == 1
31
+ z_loss, # [N]
32
+ lse, # [NV, N] or [N] when NV == 1
33
+ s_logits,
34
+ label_smoothing: tl.constexpr,
35
+ logit_scale: tl.constexpr,
36
+ lse_square_scale: tl.constexpr,
37
+ logit_softcapping: tl.constexpr,
38
+ ignore_index: tl.constexpr,
39
+ N,
40
+ V: tl.constexpr,
41
+ V_TOTAL: tl.constexpr,
42
+ V_START: tl.constexpr,
43
+ BV: tl.constexpr,
44
+ SPLIT: tl.constexpr,
45
+ ):
46
+ i_n = tl.program_id(0).to(tl.int64)
47
+ i_v = tl.program_id(1)
48
+ o_v = i_v * BV + tl.arange(0, BV)
49
+ m_v = o_v < V
50
+ p_logits = logits + i_n * s_logits
51
+
52
+ # [BV]
53
+ b_logits = tl.load(p_logits + o_v, mask=m_v, other=-float('inf')).to(tl.float32) * logit_scale
54
+ if logit_softcapping is not None:
55
+ b_logits = logit_softcapping * tanh(b_logits / logit_softcapping)
56
+ # these transforms do not preserve -inf in padded lanes
57
+ if logit_softcapping is not None or logit_scale <= 0:
58
+ b_logits = tl.where(m_v, b_logits, -float('inf'))
59
+ if label_smoothing > 0:
60
+ b_sum = tl.sum(tl.where(m_v, b_logits, 0.0), 0)
61
+ b_max = tl.max(b_logits, 0)
62
+ b_lse = log(tl.sum(exp(b_logits - b_max), 0)) + b_max
63
+
64
+ b_target = tl.load(target + i_n).to(tl.int64)
65
+ b_loss = 0.0
66
+ b_z_loss = 0.0
67
+ if b_target != ignore_index:
68
+ b_target -= V_START
69
+ m_target = (b_target >= i_v * BV) & (b_target < tl.minimum(V, (i_v + 1) * BV))
70
+ b_target_logit = tl.load(p_logits + b_target, mask=m_target, other=0).to(tl.float32) * logit_scale
71
+ if logit_softcapping is not None:
72
+ b_target_logit = logit_softcapping * tanh(b_target_logit / logit_softcapping)
73
+ b_loss = -b_target_logit
74
+ if label_smoothing > 0:
75
+ b_loss = (1 - label_smoothing) * b_loss - label_smoothing * b_sum / V_TOTAL
76
+ if not SPLIT:
77
+ b_z_loss = lse_square_scale * b_lse * b_lse
78
+ b_loss += b_lse + b_z_loss
79
+
80
+ tl.store(loss + i_v * N + i_n, b_loss)
81
+ tl.store(lse + i_v * N + i_n, b_lse)
82
+ if not SPLIT:
83
+ tl.store(z_loss + i_n, b_z_loss)
84
+
85
+
86
+ @triton.jit
87
+ def cross_entropy_bwd_kernel(
88
+ logits, # [N, V]
89
+ target, # [N]
90
+ lse, # [N]
91
+ dloss, # [N]
92
+ dlogits, # [N, V]
93
+ s_logits,
94
+ s_dloss,
95
+ s_dlogits,
96
+ label_smoothing: tl.constexpr,
97
+ logit_scale: tl.constexpr,
98
+ lse_square_scale: tl.constexpr,
99
+ logit_softcapping: tl.constexpr,
100
+ ignore_index: tl.constexpr,
101
+ V: tl.constexpr,
102
+ V_TOTAL: tl.constexpr,
103
+ V_START: tl.constexpr,
104
+ BV: tl.constexpr,
105
+ ):
106
+ i_n = tl.program_id(0).to(tl.int64)
107
+ i_v = tl.program_id(1).to(tl.int64)
108
+ o_v = i_v * BV + tl.arange(0, BV).to(tl.int64)
109
+ m_v = o_v < V
110
+ p_logits = logits + i_n * s_logits + o_v
111
+ p_dlogits = dlogits + i_n * s_dlogits + o_v
112
+
113
+ b_target = tl.load(target + i_n).to(tl.int64)
114
+ b_dloss = tl.load(dloss + i_n * s_dloss, mask=b_target != ignore_index, other=0).to(tl.float32)
115
+ # [BV]
116
+ b_logits = tl.load(p_logits, mask=m_v, other=0).to(tl.float32) * logit_scale
117
+ if logit_softcapping is not None:
118
+ b_tanh = tanh(b_logits / logit_softcapping)
119
+ b_logits = logit_softcapping * b_tanh
120
+ b_lse = tl.load(lse + i_n)
121
+ # [BV]
122
+ b_probs = exp(b_logits - b_lse)
123
+ b_dlogits = b_probs + 2.0 * lse_square_scale * b_lse * b_probs
124
+ b_target -= V_START
125
+ if label_smoothing > 0:
126
+ b_dlogits -= tl.where(o_v == b_target, 1 - label_smoothing, 0.0)
127
+ b_dlogits -= label_smoothing / V_TOTAL
128
+ else:
129
+ b_dlogits -= tl.where(o_v == b_target, 1.0, 0.0)
130
+ if logit_softcapping is not None:
131
+ b_dlogits *= 1.0 - b_tanh * b_tanh
132
+ b_dlogits *= b_dloss * logit_scale
133
+ tl.store(p_dlogits, b_dlogits, mask=m_v)
134
+
135
+
136
+ @triton.jit(do_not_specialize=['N'])
137
+ def cross_entropy_fwd_kernel_reduce(
138
+ partial_loss,
139
+ partial_lse,
140
+ target,
141
+ loss,
142
+ z_loss,
143
+ lse,
144
+ lse_square_scale: tl.constexpr,
145
+ ignore_index: tl.constexpr,
146
+ N,
147
+ NV: tl.constexpr,
148
+ BN: tl.constexpr,
149
+ BS: tl.constexpr,
150
+ SPLIT: tl.constexpr,
151
+ REDUCE_LOSS: tl.constexpr,
152
+ ):
153
+ o_n = tl.program_id(0).to(tl.int64) * BN + tl.arange(0, BN)
154
+ o_s = tl.arange(0, BS).to(tl.int64)
155
+ offsets = o_s[:, None] * N + o_n[None, :]
156
+ mask = (o_s[:, None] < NV) & (o_n[None, :] < N)
157
+ b_lse = tl.load(partial_lse + offsets, mask=mask, other=-float('inf')).to(tl.float32)
158
+ b_max = tl.max(b_lse, 0)
159
+ b_lse = b_max + log(tl.sum(exp(b_lse - b_max[None, :]), 0))
160
+ if REDUCE_LOSS:
161
+ b_loss = tl.sum(tl.load(partial_loss + offsets, mask=mask, other=0).to(tl.float32), 0)
162
+ else:
163
+ b_loss = tl.load(partial_loss + o_n, mask=o_n < N, other=0).to(tl.float32)
164
+ if not SPLIT:
165
+ valid = tl.load(target + o_n, mask=o_n < N, other=ignore_index) != ignore_index
166
+ b_z_loss = tl.where(valid, lse_square_scale * b_lse * b_lse, 0.)
167
+ b_loss = tl.where(valid, b_loss + b_lse + b_z_loss, 0.)
168
+ tl.store(z_loss + o_n, b_z_loss, mask=o_n < N)
169
+ tl.store(loss + o_n, b_loss, mask=o_n < N)
170
+ tl.store(lse + o_n, b_lse, mask=o_n < N)
171
+
172
+
173
+ def cross_entropy_reduce(
174
+ loss: torch.Tensor,
175
+ z_loss: torch.Tensor,
176
+ lse: torch.Tensor,
177
+ target: torch.Tensor,
178
+ lse_square_scale: float,
179
+ ignore_index: int,
180
+ process_group: ProcessGroup | None,
181
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
182
+ N = target.shape[0]
183
+ world_size = 1 if process_group is None else torch.distributed.get_world_size(group=process_group)
184
+ if lse.ndim == 2:
185
+ NV = lse.shape[0]
186
+ reduced_loss = loss.new_empty(N)
187
+ reduced_lse = torch.empty_like(reduced_loss)
188
+ cross_entropy_fwd_kernel_reduce[(triton.cdiv(N, 128),)](
189
+ partial_loss=loss,
190
+ partial_lse=lse,
191
+ target=target,
192
+ loss=reduced_loss,
193
+ z_loss=z_loss,
194
+ lse=reduced_lse,
195
+ lse_square_scale=lse_square_scale,
196
+ ignore_index=ignore_index,
197
+ N=N,
198
+ NV=NV,
199
+ BN=128,
200
+ BS=triton.next_power_of_2(NV),
201
+ SPLIT=world_size > 1,
202
+ REDUCE_LOSS=True,
203
+ num_warps=4,
204
+ )
205
+ loss, lse = reduced_loss, reduced_lse
206
+ if world_size > 1:
207
+ gathered_lse = lse.new_empty(world_size, N)
208
+ torch.distributed.all_gather_into_tensor(output_tensor=gathered_lse, input_tensor=lse, group=process_group)
209
+ torch.distributed.all_reduce(tensor=loss, group=process_group)
210
+ cross_entropy_fwd_kernel_reduce[(triton.cdiv(N, 128),)](
211
+ partial_loss=loss,
212
+ partial_lse=gathered_lse,
213
+ target=target,
214
+ loss=loss,
215
+ z_loss=z_loss,
216
+ lse=lse,
217
+ lse_square_scale=lse_square_scale,
218
+ ignore_index=ignore_index,
219
+ N=N,
220
+ NV=world_size,
221
+ BN=128,
222
+ BS=triton.next_power_of_2(world_size),
223
+ SPLIT=False,
224
+ REDUCE_LOSS=False,
225
+ num_warps=4,
226
+ )
227
+ return loss, z_loss, lse
228
+
229
+
230
+ @dispatch
231
+ def cross_entropy_fwd(
232
+ logits: torch.Tensor,
233
+ target: torch.Tensor,
234
+ label_smoothing: float = 0.0,
235
+ logit_scale: float = 1.0,
236
+ lse_square_scale: float = 0.0,
237
+ logit_softcapping: float | None = None,
238
+ ignore_index: int = -100,
239
+ process_group: ProcessGroup | None = None,
240
+ ):
241
+ N, V = logits.shape
242
+ assert target.shape == (N,)
243
+ world_size = 1 if process_group is None else torch.distributed.get_world_size(group=process_group)
244
+ total_classes = world_size * V
245
+ rank = 0 if process_group is None else torch.distributed.get_rank(group=process_group)
246
+ class_start_idx = rank * V
247
+
248
+ if logits.stride(-1) != 1:
249
+ logits = logits.contiguous()
250
+ BV = min(triton.next_power_of_2(V), 64 * 1024)
251
+ # reduce softcap register pressure without introducing an additional split reduction
252
+ if logit_softcapping is not None and V > BV:
253
+ BV = 8 * 1024
254
+ num_warps = 4 if BV < 2048 else (8 if BV < 8192 else 16)
255
+ # vocab partitions contribute partial loss until the global LSE is available
256
+ NV = triton.cdiv(V, BV)
257
+ split = world_size > 1 or NV > 1
258
+ shape = (NV, N) if NV > 1 else (N,)
259
+ loss = logits.new_empty(shape, dtype=torch.float)
260
+ lse = torch.empty_like(loss)
261
+ z_loss = logits.new_empty(N, dtype=torch.float)
262
+
263
+ cross_entropy_fwd_kernel[(N, NV)](
264
+ logits=logits,
265
+ target=target,
266
+ loss=loss,
267
+ z_loss=z_loss,
268
+ lse=lse,
269
+ s_logits=logits.stride(0),
270
+ label_smoothing=label_smoothing,
271
+ logit_scale=logit_scale,
272
+ lse_square_scale=lse_square_scale,
273
+ logit_softcapping=logit_softcapping,
274
+ ignore_index=ignore_index,
275
+ N=N,
276
+ V=V,
277
+ V_TOTAL=total_classes,
278
+ V_START=class_start_idx,
279
+ BV=BV,
280
+ SPLIT=split,
281
+ num_warps=num_warps,
282
+ )
283
+
284
+ if split:
285
+ loss, z_loss, lse = cross_entropy_reduce(
286
+ loss=loss,
287
+ z_loss=z_loss,
288
+ lse=lse,
289
+ target=target,
290
+ lse_square_scale=lse_square_scale,
291
+ ignore_index=ignore_index,
292
+ process_group=process_group,
293
+ )
294
+
295
+ return loss, z_loss, lse, total_classes, class_start_idx
296
+
297
+
298
+ @dispatch
299
+ def cross_entropy_bwd(
300
+ logits: torch.Tensor,
301
+ target: torch.Tensor,
302
+ lse: torch.Tensor,
303
+ dloss: torch.Tensor,
304
+ label_smoothing: float = 0.0,
305
+ logit_scale: float = 1.0,
306
+ lse_square_scale: float = 0.0,
307
+ logit_softcapping: float | None = None,
308
+ ignore_index: int = -100,
309
+ total_classes: int | None = None,
310
+ class_start_idx: int = 0,
311
+ inplace_backward: bool = False,
312
+ ):
313
+ N, V = logits.shape
314
+ BV = min(triton.next_power_of_2(V), 4 * 1024)
315
+ dlogits = logits if inplace_backward else torch.empty_like(logits)
316
+ cross_entropy_bwd_kernel[(N, triton.cdiv(V, BV))](
317
+ logits=logits,
318
+ target=target,
319
+ lse=lse,
320
+ dloss=dloss,
321
+ dlogits=dlogits,
322
+ s_logits=logits.stride(0),
323
+ s_dloss=dloss.stride(0),
324
+ s_dlogits=dlogits.stride(0),
325
+ label_smoothing=label_smoothing,
326
+ logit_scale=logit_scale,
327
+ lse_square_scale=lse_square_scale,
328
+ logit_softcapping=logit_softcapping,
329
+ ignore_index=ignore_index,
330
+ V=V,
331
+ V_TOTAL=V if total_classes is None else total_classes,
332
+ V_START=class_start_idx,
333
+ BV=BV,
334
+ num_warps=4 if BV < 2048 else 8,
335
+ )
336
+ return dlogits
337
+
338
+
339
+ class FusedCrossEntropyFunction(torch.autograd.Function):
340
+
341
+ @staticmethod
342
+ @input_guard
343
+ @autocast_custom_fwd
344
+ def forward(
345
+ ctx,
346
+ logits,
347
+ target,
348
+ label_smoothing=0.0,
349
+ logit_scale=1.0,
350
+ lse_square_scale=0.0,
351
+ logit_softcapping=None,
352
+ ignore_index=-100,
353
+ inplace_backward=False,
354
+ process_group: ProcessGroup | None = None,
355
+ ):
356
+ loss, z_loss, lse, total_classes, class_start_idx = cross_entropy_fwd(
357
+ logits=logits,
358
+ target=target,
359
+ label_smoothing=label_smoothing,
360
+ logit_scale=logit_scale,
361
+ lse_square_scale=lse_square_scale,
362
+ logit_softcapping=logit_softcapping,
363
+ ignore_index=ignore_index,
364
+ process_group=process_group,
365
+ )
366
+ ctx.save_for_backward(logits, target, lse)
367
+ ctx.mark_non_differentiable(z_loss)
368
+ ctx.label_smoothing = label_smoothing
369
+ ctx.logit_scale = logit_scale
370
+ ctx.lse_square_scale = lse_square_scale
371
+ ctx.logit_softcapping = logit_softcapping
372
+ ctx.ignore_index = ignore_index
373
+ ctx.total_classes = total_classes
374
+ ctx.class_start_idx = class_start_idx
375
+ ctx.inplace_backward = inplace_backward
376
+
377
+ return loss, z_loss
378
+
379
+ @staticmethod
380
+ @input_guard
381
+ @autocast_custom_bwd
382
+ def backward(ctx, dloss, dz_loss):
383
+ # z-loss is returned for logging and is already included in the differentiable loss
384
+ del dz_loss
385
+
386
+ logits, target, lse = ctx.saved_tensors
387
+ dlogits = cross_entropy_bwd(
388
+ logits=logits,
389
+ target=target,
390
+ lse=lse,
391
+ dloss=dloss,
392
+ label_smoothing=ctx.label_smoothing,
393
+ logit_scale=ctx.logit_scale,
394
+ lse_square_scale=ctx.lse_square_scale,
395
+ logit_softcapping=ctx.logit_softcapping,
396
+ ignore_index=ctx.ignore_index,
397
+ total_classes=ctx.total_classes,
398
+ class_start_idx=ctx.class_start_idx,
399
+ inplace_backward=ctx.inplace_backward,
400
+ )
401
+ return dlogits, None, None, None, None, None, None, None, None
402
+
403
+
404
+ fused_cross_entropy_forward = cross_entropy_fwd
405
+
406
+
407
+ CrossEntropyLossFunction = FusedCrossEntropyFunction
408
+
409
+
410
+ @dispatch
411
+ def cross_entropy_loss(
412
+ logits: torch.Tensor,
413
+ target: torch.Tensor,
414
+ label_smoothing: float = 0.0,
415
+ logit_scale: float = 1.0,
416
+ lse_square_scale: float = 0.0,
417
+ logit_softcapping: float | None = None,
418
+ ignore_index: int = -100,
419
+ inplace_backward: bool = False,
420
+ process_group: ProcessGroup | None = None,
421
+ ) -> tuple[torch.Tensor, torch.Tensor]:
422
+ """Compute unreduced cross entropy and its non-differentiable z-loss component.
423
+
424
+ Args:
425
+ logits (torch.Tensor):
426
+ Input logits of shape `[N, V]`.
427
+ target (torch.Tensor):
428
+ Global target indices of shape `[N]`.
429
+ label_smoothing (float, Optional):
430
+ Uniform label smoothing coefficient. Default: 0.0.
431
+ logit_scale (float, Optional):
432
+ Scale applied before softcapping. Default: 1.0.
433
+ lse_square_scale (float, Optional):
434
+ Coefficient of the squared logsumexp regularizer. Default: 0.0.
435
+ logit_softcapping (float, Optional):
436
+ Softcap applied as `softcap * tanh(logits / softcap)`. Default: `None`.
437
+ ignore_index (int, Optional):
438
+ Target index excluded from loss and gradients. Default: -100.
439
+ inplace_backward (bool, Optional):
440
+ Whether to overwrite logits with their gradients. Default: `False`.
441
+ process_group (ProcessGroup, Optional):
442
+ Group sharding the vocabulary into equal contiguous partitions. Default: `None`.
443
+
444
+ Returns:
445
+ tuple[torch.Tensor, torch.Tensor]:
446
+ FP32 loss and z-loss tensors of shape `[N]`. The loss includes z-loss.
447
+ """
448
+ return FusedCrossEntropyFunction.apply(
449
+ logits,
450
+ target,
451
+ label_smoothing,
452
+ logit_scale,
453
+ lse_square_scale,
454
+ logit_softcapping,
455
+ ignore_index,
456
+ inplace_backward,
457
+ process_group,
458
+ )
459
+
460
+
461
+ class FusedCrossEntropyLoss(nn.Module):
462
+ def __init__(
463
+ self,
464
+ ignore_index: int = -100,
465
+ reduction: str = "mean",
466
+ label_smoothing: float = 0.0,
467
+ logit_scale: float = 1.0,
468
+ lse_square_scale: float = 0.0,
469
+ logit_softcapping: float | None = None,
470
+ inplace_backward: bool = False,
471
+ process_group: ProcessGroup | None = None,
472
+ return_z_loss: bool = False,
473
+ ):
474
+ """Cross entropy with optional logit transforms and vocabulary parallelism.
475
+
476
+ Args:
477
+ ignore_index (int, Optional):
478
+ Target index excluded from loss and gradients. Default: -100.
479
+ reduction (str, Optional):
480
+ Reduction over non-ignored targets: `mean`, `sum`, or `none`. Default: `mean`.
481
+ label_smoothing (float, Optional):
482
+ Uniform label smoothing coefficient. Default: 0.0.
483
+ logit_scale (float, Optional):
484
+ Scale applied before softcapping. Default: 1.0.
485
+ lse_square_scale (float, Optional):
486
+ Coefficient of the squared logsumexp regularizer. Default: 0.0.
487
+ logit_softcapping (float, Optional):
488
+ Softcap applied as `softcap * tanh(logits / softcap)`. Default: `None`.
489
+ inplace_backward (bool, Optional):
490
+ Whether to overwrite logits with their gradients. Default: `False`.
491
+ process_group (ProcessGroup, Optional):
492
+ Group sharding the vocabulary into equal contiguous partitions. Default: `None`.
493
+ return_z_loss (bool, Optional):
494
+ Whether to also return z-loss for logging, without its own backward. Default: `False`.
495
+ """
496
+ super().__init__()
497
+ if reduction not in ('mean', 'sum', 'none'):
498
+ raise NotImplementedError(f"Unsupported reduction: {reduction}")
499
+ self.ignore_index = ignore_index
500
+ self.reduction = reduction
501
+ self.label_smoothing = label_smoothing
502
+ self.logit_scale = logit_scale
503
+ self.lse_square_scale = lse_square_scale
504
+ self.logit_softcapping = logit_softcapping
505
+ self.inplace_backward = inplace_backward
506
+ self.process_group = process_group
507
+ self.return_z_loss = return_z_loss
508
+
509
+ def forward(self, input: torch.Tensor, target: torch.Tensor):
510
+ """Compute loss from `[N, V]` logits and `[N]` targets.
511
+
512
+ Return FP32 scalars for `mean` or `sum`, and `[N]` tensors for `none`.
513
+ When `return_z_loss=True`, also return the reduced z-loss component.
514
+ """
515
+ assert input.device.type in ('cuda', 'npu', 'xpu') and target.device.type in ('cuda', 'npu', 'xpu'), (
516
+ "Only support CUDA/NPU/XPU tensors"
517
+ )
518
+ loss, z_loss = cross_entropy_loss(
519
+ logits=input,
520
+ target=target,
521
+ label_smoothing=self.label_smoothing,
522
+ logit_scale=self.logit_scale,
523
+ lse_square_scale=self.lse_square_scale,
524
+ logit_softcapping=self.logit_softcapping,
525
+ ignore_index=self.ignore_index,
526
+ inplace_backward=self.inplace_backward,
527
+ process_group=self.process_group,
528
+ )
529
+ if self.reduction != 'none':
530
+ loss = loss.sum()
531
+ if self.return_z_loss:
532
+ z_loss = z_loss.sum()
533
+ if self.reduction == 'mean':
534
+ total = (target != self.ignore_index).sum()
535
+ loss = loss / total
536
+ if self.return_z_loss:
537
+ z_loss = z_loss / total
538
+ return (loss, z_loss) if self.return_z_loss else loss
build/torch-cuda/modules/fused_kl_div/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ from ...modules.fused_kl_div.ops import FusedKLDivLoss, fused_kl_div_loss
9
+
10
+ __all__ = ['FusedKLDivLoss', 'fused_kl_div_loss']
11
+
build/torch-cuda/modules/{fused_kl_div.py → fused_kl_div/ops.py} RENAMED
@@ -11,14 +11,14 @@ import torch.nn.functional as F
11
  import triton
12
  import triton.language as tl
13
 
14
- from ..modules.backends import dispatch
15
- from ..ops.utils.op import exp, log
16
- from ..utils import IS_AMD, input_guard
17
 
18
- # The hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576
19
  # https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
20
- # However, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
21
- # The optimal maximum block size depends on your hardware, your kernel, and your dtype
22
  MAX_FUSED_SIZE = 65536 // 2
23
  STATIC_WARPS = 32 if not IS_AMD else 16
24
 
@@ -42,22 +42,19 @@ def kl_div_kernel(
42
  logits += i_n * s_logits
43
  target_logits += i_n * s_logits
44
 
45
- # m is the max value. use the notation from the paper
46
  sm = float('-inf')
47
  tm = float('-inf')
48
- # d is the sum. use the notation from the paper
49
  sd, td = 0.0, 0.0
50
 
51
  NV = tl.cdiv(V, BV)
52
  for iv in range(0, NV):
53
  o_x = iv * BV + tl.arange(0, BV)
54
- # for student
55
  b_sl = tl.load(logits + o_x, mask=o_x < V, other=float('-inf'))
56
  b_sm = tl.max(b_sl)
57
  m_new = tl.maximum(sm, b_sm)
58
  sd = sd * exp(sm - m_new) + tl.sum(exp(b_sl - m_new))
59
  sm = m_new
60
- # for teacher
61
  b_tl = tl.load(target_logits + o_x, mask=o_x < V, other=float('-inf'))
62
  b_tm = tl.max(b_tl)
63
  m_new = tl.maximum(tm, b_tm)
@@ -89,45 +86,28 @@ def kl_div_kernel(
89
 
90
 
91
  @triton.jit
92
- def elementwise_mul_kernel(
93
- x,
94
- g,
95
- N: tl.constexpr,
96
- B: tl.constexpr,
97
- ):
98
- """
99
- This function multiplies each element of the tensor pointed by x with the value pointed by g.
100
- The multiplication is performed in-place on the tensor pointed by x.
101
-
102
- Parameters:
103
- x:
104
- Pointer to the input tensor.
105
- g:
106
- Pointer to the gradient output value.
107
- N (int):
108
- The number of columns in the input tensor.
109
- B (int):
110
- The block size for Triton operations.
111
- """
112
 
113
- # Get the program ID and convert it to int64 to avoid overflow
114
  i_x = tl.program_id(0).to(tl.int64)
115
  o_x = i_x * B + tl.arange(0, B)
116
 
117
- # Load the gradient output value
118
  b_g = tl.load(g)
 
 
119
  b_x = tl.load(x + o_x, mask=o_x < N)
120
  tl.store(x + o_x, b_x * b_g, mask=o_x < N)
121
 
122
 
123
- @dispatch('modules')
124
- def fused_kl_div_forward(
125
  x: torch.Tensor,
126
  target_x: torch.Tensor,
127
  weight: torch.Tensor,
128
  target_weight: torch.Tensor,
129
  reduction: str = 'batchmean',
130
  accumulate_grad_in_fp32: bool = True,
 
 
131
  ):
132
  device = x.device
133
 
@@ -146,9 +126,8 @@ def fused_kl_div_forward(
146
 
147
  grad_dtype = torch.float32 if accumulate_grad_in_fp32 else weight.dtype
148
 
149
- dx = torch.zeros_like(x, device=device)
150
- dw = torch.zeros_like(weight, device=device, dtype=grad_dtype) if weight is not None else None
151
- # we use fp32 for loss accumulator
152
  loss = torch.zeros(N, dtype=torch.float32, device=device)
153
 
154
  for ic in range(NC):
@@ -156,14 +135,12 @@ def fused_kl_div_forward(
156
  # [C, N]
157
  c_sx = x[start:end]
158
  c_tx = target_x[start:end]
159
- # when doing matmul, use the original precision
160
  # [C, V]
161
  c_sl = F.linear(c_sx, weight)
162
  c_tl = F.linear(c_tx, target_weight)
163
- if weight is not None and c_sx.dtype != grad_dtype:
164
  c_sx = c_sx.to(dtype=grad_dtype)
165
 
166
- # unreduced loss
167
  c_loss = loss[start:end]
168
 
169
  # Here we calculate the gradient of c_sx in place so we can save memory.
@@ -187,15 +164,11 @@ def fused_kl_div_forward(
187
  # Thus, we need an additional scaling factor of (n_non_ignore/total) to scale the gradients.
188
  # [C, H]
189
 
190
- dx[start:end] = torch.mm(c_sl, weight)
 
191
 
192
- if weight is not None:
193
- torch.addmm(
194
- input=dw,
195
- mat1=c_sl.t().to(dtype=grad_dtype),
196
- mat2=c_sx,
197
- out=dw,
198
- )
199
 
200
  loss = loss.sum()
201
  if dw is not None:
@@ -203,37 +176,15 @@ def fused_kl_div_forward(
203
  return loss, dx, dw
204
 
205
 
206
- @dispatch('modules')
207
- def fused_kl_div_backward(
208
- do: torch.Tensor,
209
- dx: torch.Tensor,
210
- dw: torch.Tensor,
211
- ):
212
- # If cross entropy is the last layer, do is 1.0. Skip the mul to save time
213
- if torch.ne(do, torch.tensor(1.0, device=do.device)):
214
- # We use a Triton kernel instead of a PyTorch operation because modifying inputs in-place
215
- # for gradient storage and backward multiple times causes anomalies with PyTorch but not with Triton.
216
- N, H = dx.shape
217
  B = min(MAX_FUSED_SIZE, triton.next_power_of_2(H))
218
-
219
- elementwise_mul_kernel[(triton.cdiv(N * H, B),)](
220
- x=dx,
221
- g=do,
222
- N=N*H,
223
- B=B,
224
- num_warps=STATIC_WARPS,
225
- )
226
-
227
- # handle dw
228
- if dw is not None:
229
- V, H = dw.shape
230
- elementwise_mul_kernel[(triton.cdiv(V * H, B),)](
231
- x=dw,
232
- g=do,
233
- N=V*H,
234
- B=B,
235
- num_warps=STATIC_WARPS,
236
- )
237
 
238
  return dx, dw
239
 
@@ -250,14 +201,17 @@ class FusedKLDivLossFunction(torch.autograd.Function):
250
  target_weight: torch.Tensor,
251
  reduction: str,
252
  accumulate_grad_in_fp32: bool,
 
253
  ):
254
- loss, dx, dw = fused_kl_div_forward(
255
  x=x,
256
  target_x=target_x,
257
  weight=weight,
258
  target_weight=target_weight,
259
  reduction=reduction,
260
  accumulate_grad_in_fp32=accumulate_grad_in_fp32,
 
 
261
  )
262
  ctx.save_for_backward(dx, dw)
263
  return loss
@@ -266,8 +220,8 @@ class FusedKLDivLossFunction(torch.autograd.Function):
266
  @input_guard
267
  def backward(ctx, do):
268
  dx, dw = ctx.saved_tensors
269
- dx, dw = fused_kl_div_backward(do, dx, dw)
270
- return dx, None, dw, None, None, None
271
 
272
 
273
  def fused_kl_div_loss(
@@ -280,23 +234,25 @@ def fused_kl_div_loss(
280
  ) -> tuple[torch.Tensor, torch.Tensor]:
281
  """
282
  Args:
283
- x (`torch.Tensor`):
284
  Tensor of shape `[batch_size * seq_len, hidden_size]`.
285
- target_x (`torch.Tensor`):
286
  Frozen teacher input tensor of shape `[batch_size * seq_len, hidden_size]`.
287
  Must not require gradients.
288
- weight (`torch.Tensor`):
289
  Tensor of shape `[vocab_size, hidden_size]`.
290
- target_weight (`torch.Tensor`):
291
  Frozen teacher weight tensor of shape `[vocab_size, hidden_size]`.
292
  Must not require gradients.
293
- reduction (`str`):
294
  Specifies the reduction to apply to the output: 'batchmean'. Default: 'batchmean'.
295
- accumulate_grad_in_fp32 (`bool`):
296
- Whether to accumulate the student weight gradient in fp32 before casting it back
297
- to `weight.dtype`. Default: `True`.
 
298
  Returns:
299
- loss
 
300
  """
301
  if target_x.requires_grad or target_weight.requires_grad:
302
  raise RuntimeError(
@@ -311,26 +267,24 @@ def fused_kl_div_loss(
311
  target_weight,
312
  reduction,
313
  accumulate_grad_in_fp32,
 
314
  )
315
 
316
 
317
  class FusedKLDivLoss(nn.Module):
318
 
319
- def __init__(
320
- self,
321
- reduction: str = 'batchmean',
322
- accumulate_grad_in_fp32: bool = True,
323
- ):
324
  """
325
  Args:
326
- reduction (`str`):
327
  Specifies the reduction to apply to the output: 'batchmean'. Default: 'batchmean'.
328
- accumulate_grad_in_fp32 (`bool`):
329
- Whether to accumulate the student weight gradient in fp32 before casting it back
330
- to `weight.dtype`. Default: `True`.
 
331
  Note:
332
- FusedKLDivLoss only computes gradients for `x` and `weight`; `target_x` and
333
- `target_weight` are treated as frozen teacher tensors and must not require gradients.
334
  """
335
  super().__init__()
336
 
@@ -339,27 +293,23 @@ class FusedKLDivLoss(nn.Module):
339
  self.reduction = reduction
340
  self.accumulate_grad_in_fp32 = accumulate_grad_in_fp32
341
 
342
- def forward(
343
- self,
344
- x: torch.Tensor,
345
- target_x: torch.Tensor,
346
- weight: torch.Tensor,
347
- target_weight: torch.Tensor,
348
- ):
349
  """
350
  Args:
351
- x (`torch.Tensor`):
352
  Tensor of shape `[batch_size * seq_len, hidden_size]`.
353
- target_x (`torch.Tensor`):
354
  Frozen teacher input tensor of shape `[batch_size * seq_len, hidden_size]`.
355
  Must not require gradients.
356
- weight (`torch.Tensor`):
357
  Tensor of shape `[vocab_size, hidden_size]`.
358
- target_weight (`torch.Tensor`):
359
  Frozen teacher weight tensor of shape `[vocab_size, hidden_size]`.
360
  Must not require gradients.
 
361
  Returns:
362
- loss
 
363
  """
364
  loss = fused_kl_div_loss(
365
  x=x,
 
11
  import triton
12
  import triton.language as tl
13
 
14
+ from ...backends import dispatch
15
+ from ...ops.utils.op import exp, log
16
+ from ...utils import IS_AMD, input_guard
17
 
18
+ # the hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576
19
  # https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
20
+ # however, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
21
+ # the optimal maximum block size depends on your hardware, your kernel, and your dtype
22
  MAX_FUSED_SIZE = 65536 // 2
23
  STATIC_WARPS = 32 if not IS_AMD else 16
24
 
 
42
  logits += i_n * s_logits
43
  target_logits += i_n * s_logits
44
 
45
+ # running maxima and exponential sums for student and teacher logits.
46
  sm = float('-inf')
47
  tm = float('-inf')
 
48
  sd, td = 0.0, 0.0
49
 
50
  NV = tl.cdiv(V, BV)
51
  for iv in range(0, NV):
52
  o_x = iv * BV + tl.arange(0, BV)
 
53
  b_sl = tl.load(logits + o_x, mask=o_x < V, other=float('-inf'))
54
  b_sm = tl.max(b_sl)
55
  m_new = tl.maximum(sm, b_sm)
56
  sd = sd * exp(sm - m_new) + tl.sum(exp(b_sl - m_new))
57
  sm = m_new
 
58
  b_tl = tl.load(target_logits + o_x, mask=o_x < V, other=float('-inf'))
59
  b_tm = tl.max(b_tl)
60
  m_new = tl.maximum(tm, b_tm)
 
86
 
87
 
88
  @triton.jit
89
+ def elementwise_mul_kernel(x, g, N: tl.constexpr, B: tl.constexpr):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
 
91
  i_x = tl.program_id(0).to(tl.int64)
92
  o_x = i_x * B + tl.arange(0, B)
93
 
 
94
  b_g = tl.load(g)
95
+ if b_g == 1.0:
96
+ return
97
  b_x = tl.load(x + o_x, mask=o_x < N)
98
  tl.store(x + o_x, b_x * b_g, mask=o_x < N)
99
 
100
 
101
+ @dispatch
102
+ def fused_kl_div_fwd(
103
  x: torch.Tensor,
104
  target_x: torch.Tensor,
105
  weight: torch.Tensor,
106
  target_weight: torch.Tensor,
107
  reduction: str = 'batchmean',
108
  accumulate_grad_in_fp32: bool = True,
109
+ use_dx: bool = True,
110
+ use_dw: bool = True,
111
  ):
112
  device = x.device
113
 
 
126
 
127
  grad_dtype = torch.float32 if accumulate_grad_in_fp32 else weight.dtype
128
 
129
+ dx = torch.zeros_like(x, device=device) if use_dx else None
130
+ dw = torch.zeros_like(weight, device=device, dtype=grad_dtype) if use_dw else None
 
131
  loss = torch.zeros(N, dtype=torch.float32, device=device)
132
 
133
  for ic in range(NC):
 
135
  # [C, N]
136
  c_sx = x[start:end]
137
  c_tx = target_x[start:end]
 
138
  # [C, V]
139
  c_sl = F.linear(c_sx, weight)
140
  c_tl = F.linear(c_tx, target_weight)
141
+ if dw is not None and c_sx.dtype != grad_dtype:
142
  c_sx = c_sx.to(dtype=grad_dtype)
143
 
 
144
  c_loss = loss[start:end]
145
 
146
  # Here we calculate the gradient of c_sx in place so we can save memory.
 
164
  # Thus, we need an additional scaling factor of (n_non_ignore/total) to scale the gradients.
165
  # [C, H]
166
 
167
+ if dx is not None:
168
+ dx[start:end] = torch.mm(c_sl, weight)
169
 
170
+ if dw is not None:
171
+ torch.addmm(input=dw, mat1=c_sl.t().to(dtype=grad_dtype), mat2=c_sx, out=dw)
 
 
 
 
 
172
 
173
  loss = loss.sum()
174
  if dw is not None:
 
176
  return loss, dx, dw
177
 
178
 
179
+ @dispatch
180
+ def fused_kl_div_bwd(do: torch.Tensor, dx: torch.Tensor | None, dw: torch.Tensor | None):
181
+ # in-place gradient storage across repeated backward calls causes PyTorch anomalies; use Triton.
182
+ for grad in (dx, dw):
183
+ if grad is None:
184
+ continue
185
+ N, H = grad.shape
 
 
 
 
186
  B = min(MAX_FUSED_SIZE, triton.next_power_of_2(H))
187
+ elementwise_mul_kernel[(triton.cdiv(N * H, B),)](x=grad, g=do, N=N*H, B=B, num_warps=STATIC_WARPS,)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  return dx, dw
190
 
 
201
  target_weight: torch.Tensor,
202
  reduction: str,
203
  accumulate_grad_in_fp32: bool,
204
+ grad_enabled: bool,
205
  ):
206
+ loss, dx, dw = fused_kl_div_fwd(
207
  x=x,
208
  target_x=target_x,
209
  weight=weight,
210
  target_weight=target_weight,
211
  reduction=reduction,
212
  accumulate_grad_in_fp32=accumulate_grad_in_fp32,
213
+ use_dx=grad_enabled and ctx.needs_input_grad[0],
214
+ use_dw=grad_enabled and ctx.needs_input_grad[2],
215
  )
216
  ctx.save_for_backward(dx, dw)
217
  return loss
 
220
  @input_guard
221
  def backward(ctx, do):
222
  dx, dw = ctx.saved_tensors
223
+ dx, dw = fused_kl_div_bwd(do=do, dx=dx, dw=dw)
224
+ return dx, None, dw, None, None, None, None
225
 
226
 
227
  def fused_kl_div_loss(
 
234
  ) -> tuple[torch.Tensor, torch.Tensor]:
235
  """
236
  Args:
237
+ x (torch.Tensor):
238
  Tensor of shape `[batch_size * seq_len, hidden_size]`.
239
+ target_x (torch.Tensor):
240
  Frozen teacher input tensor of shape `[batch_size * seq_len, hidden_size]`.
241
  Must not require gradients.
242
+ weight (torch.Tensor):
243
  Tensor of shape `[vocab_size, hidden_size]`.
244
+ target_weight (torch.Tensor):
245
  Frozen teacher weight tensor of shape `[vocab_size, hidden_size]`.
246
  Must not require gradients.
247
+ reduction (str, Optional):
248
  Specifies the reduction to apply to the output: 'batchmean'. Default: 'batchmean'.
249
+ accumulate_grad_in_fp32 (bool, Optional):
250
+ Whether to accumulate the student weight gradient in fp32 before casting it back to `weight.dtype`.
251
+ Default: `True`.
252
+
253
  Returns:
254
+ loss (torch.Tensor):
255
+ Scalar KL divergence loss.
256
  """
257
  if target_x.requires_grad or target_weight.requires_grad:
258
  raise RuntimeError(
 
267
  target_weight,
268
  reduction,
269
  accumulate_grad_in_fp32,
270
+ torch.is_grad_enabled(),
271
  )
272
 
273
 
274
  class FusedKLDivLoss(nn.Module):
275
 
276
+ def __init__(self, reduction: str = 'batchmean', accumulate_grad_in_fp32: bool = True):
 
 
 
 
277
  """
278
  Args:
279
+ reduction (str, Optional):
280
  Specifies the reduction to apply to the output: 'batchmean'. Default: 'batchmean'.
281
+ accumulate_grad_in_fp32 (bool, Optional):
282
+ Whether to accumulate the student weight gradient in fp32 before casting it back to `weight.dtype`.
283
+ Default: `True`.
284
+
285
  Note:
286
+ FusedKLDivLoss only computes gradients for `x` and `weight`;
287
+ `target_x` and `target_weight` are treated as frozen teacher tensors and must not require gradients.
288
  """
289
  super().__init__()
290
 
 
293
  self.reduction = reduction
294
  self.accumulate_grad_in_fp32 = accumulate_grad_in_fp32
295
 
296
+ def forward(self, x: torch.Tensor, target_x: torch.Tensor, weight: torch.Tensor, target_weight: torch.Tensor):
 
 
 
 
 
 
297
  """
298
  Args:
299
+ x (torch.Tensor):
300
  Tensor of shape `[batch_size * seq_len, hidden_size]`.
301
+ target_x (torch.Tensor):
302
  Frozen teacher input tensor of shape `[batch_size * seq_len, hidden_size]`.
303
  Must not require gradients.
304
+ weight (torch.Tensor):
305
  Tensor of shape `[vocab_size, hidden_size]`.
306
+ target_weight (torch.Tensor):
307
  Frozen teacher weight tensor of shape `[vocab_size, hidden_size]`.
308
  Must not require gradients.
309
+
310
  Returns:
311
+ loss (torch.Tensor):
312
+ Scalar KL divergence loss.
313
  """
314
  loss = fused_kl_div_loss(
315
  x=x,
build/torch-cuda/modules/fused_linear_cross_entropy/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ # For a list of all contributors, visit:
6
+ # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
+
8
+ from ...modules.fused_linear_cross_entropy.ops import (
9
+ FusedLinearCrossEntropyLoss,
10
+ LinearLossParallel,
11
+ fused_linear_cross_entropy_loss,
12
+ )
13
+
14
+ __all__ = ['FusedLinearCrossEntropyLoss', 'LinearLossParallel', 'fused_linear_cross_entropy_loss']
15
+
build/torch-cuda/modules/{fused_linear_cross_entropy.py → fused_linear_cross_entropy/ops.py} RENAMED
@@ -5,10 +5,12 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- # Code adapted from
9
- # https://github.com/linkedin/Liger-Kernel/blob/main/src/liger_kernel/ops/fused_linear_cross_entropy.py
 
10
 
11
  from functools import partial
 
12
 
13
  import torch
14
  import torch.nn as nn
@@ -19,19 +21,23 @@ from torch.distributed import DeviceMesh
19
  from torch.distributed.tensor import Replicate, Shard, distribute_module
20
  from torch.distributed.tensor.parallel import ParallelStyle
21
 
22
- from ..modules.backends import dispatch
23
- from ..ops.utils.op import exp, log, tanh
24
- from ..utils import IS_AMD, input_guard
 
 
 
 
25
 
26
  try:
27
  from torch.distributed.tensor import DTensor
28
  except (ImportError, AttributeError):
29
  DTensor = None
30
 
31
- # The hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576
32
  # https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
33
- # However, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
34
- # The optimal maximum block size depends on your hardware, your kernel, and your dtype
35
  MAX_FUSED_SIZE = 65536 // 2
36
  STATIC_WARPS = 32 if not IS_AMD else 16
37
 
@@ -42,268 +48,86 @@ STATIC_WARPS = 32 if not IS_AMD else 16
42
  })
43
  @triton.jit
44
  def logsumexp_fwd_kernel(
45
- x,
46
- z,
47
  scale,
48
  softcapping,
49
  D: tl.constexpr,
50
- B: tl.constexpr,
51
  HAS_SCALE: tl.constexpr,
52
  HAS_SOFTCAPPING: tl.constexpr,
53
  ):
54
  i_n, i_d = tl.program_id(0).to(tl.int64), tl.program_id(1).to(tl.int64)
55
- o_d = i_d * B + tl.arange(0, B)
56
  m_d = o_d < D
57
 
 
58
  b_x = tl.load(x + i_n * D + o_d, mask=m_d, other=-float('inf'))
59
  if HAS_SCALE:
60
  b_x = b_x * scale
61
  if HAS_SOFTCAPPING:
62
  b_x = softcapping * tanh(b_x / softcapping)
 
63
  b_m = tl.max(b_x, 0)
64
  b_z = log(tl.sum(exp(b_x - b_m), 0)) + b_m
65
- tl.store(z + i_n * tl.cdiv(D, B) + i_d, b_z)
66
 
67
 
68
- @dispatch('modules')
69
- def logsumexp_fwd(
70
- x,
71
- scale: float | None = None,
72
- softcapping: float | None = None,
73
- dtype: torch.dtype | None = None,
74
  ):
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  shape = x.shape
76
  x = x.view(-1, shape[-1])
77
  N, D = x.shape
78
- B = min(triton.next_power_of_2(D), 64 * 1024)
79
- ND = triton.cdiv(D, B)
80
 
81
  z = x.new_empty(N, ND, dtype=torch.float)
82
- logsumexp_fwd_kernel[(N, ND)](
83
- x=x,
84
- z=z,
85
- scale=scale,
86
- softcapping=softcapping,
87
- D=D,
88
- B=B,
89
- )
90
  z = z.logsumexp(-1).view(*shape[:-1])
91
  if dtype is not None and dtype != torch.float:
92
  z = z.to(dtype)
93
  return z
94
 
95
 
96
- @triton.jit
97
- def cross_entropy_kernel(
98
- logits,
99
- lse,
100
- target,
101
- loss,
102
- total,
103
- ignore_index,
104
- label_smoothing: tl.constexpr,
105
- logit_scale: tl.constexpr,
106
- logit_softcapping: tl.constexpr,
107
- reduction: tl.constexpr,
108
- V: tl.constexpr,
109
- BV: tl.constexpr,
110
- ):
111
- """
112
- This kernel computes both cross entropy loss and the gradient of the input.
113
- We only consider hard label + mean reduction for now.
114
- Please refer to https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html for the math.
115
-
116
- Args:
117
- logits:
118
- Pointer to logits tensor.
119
- lse:
120
- Pointer to logsumexp tensor.
121
- target: Pointer to target tensor.
122
- loss:
123
- Pointer to tensor to store the loss.
124
- V (int):
125
- The number of columns in the input tensor.
126
- total (int):
127
- The number of non-ignored classes.
128
- ignore_index (int):
129
- The index to ignore in the target.
130
- label_smoothing (float):
131
- The amount of smoothing when computing the loss, where 0.0 means no smoothing.
132
- reduction (str):
133
- The string for the reduction to apply
134
- BV (int):
135
- The block size for vocab.
136
- """
137
-
138
- # https://github.com/triton-lang/triton/issues/1058
139
- # If B*T*V is too large, i_n * stride will overflow out of int32, so we convert to int64
140
- i_n = tl.program_id(0).to(tl.int64)
141
- NV = tl.cdiv(V, BV)
142
-
143
- # 1. Load target first because if the target is ignore_index, we can return right away
144
- b_y = tl.load(target + i_n)
145
-
146
- # 2. locate the start index
147
- logits += i_n * V
148
-
149
- if b_y == ignore_index:
150
- # set all x as 0
151
- for i in range(0, V, BV):
152
- o_v = i + tl.arange(0, BV)
153
- tl.store(logits + o_v, 0.0, mask=o_v < V)
154
- return
155
-
156
- # Online softmax: 2 loads + 1 store (compared with 3 loads + 1 store for the safe softmax)
157
- # Refer to Algorithm 3 in the paper: https://arxiv.org/pdf/1805.02867
158
-
159
- # 3. [Online softmax] first pass: compute logsumexp
160
- # we did this in another kernel
161
- b_l = tl.load(logits + b_y).to(tl.float32) * logit_scale
162
- if logit_softcapping is not None:
163
- b_t_y = tanh(b_l / logit_softcapping)
164
- b_l = logit_softcapping * b_t_y
165
- # Save the softcap derivative for the target position for use in step 6
166
- b_softcap_deriv_y = 1.0 - b_t_y * b_t_y
167
- b_lse = tl.load(lse + i_n)
168
-
169
- # 4. Calculate the loss
170
- # loss = lse - logits_l
171
- b_loss = b_lse - b_l
172
-
173
- # Label smoothing is a general case of normal cross entropy
174
- # See the full derivation at https://github.com/linkedin/Liger-Kernel/pull/198#issue-2503665310
175
- b_z = 0.0
176
- eps = label_smoothing / V
177
-
178
- # We need tl.debug_barrier() as mentioned in
179
- # https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/ops/cross_entropy.py#L34
180
- tl.debug_barrier()
181
-
182
- # 5. [Online Softmax] Second pass: compute gradients
183
- # For 'mean' reduction, gradients are normalized by number of non-ignored elements
184
- # dx_y = (softmax(x_y) - 1) / N
185
- # dx_i = softmax(x_i) / N, i != y
186
- # For label smoothing:
187
- # dx_i = (softmax(x_y) - label_smoothing / V) / N, i != y
188
- # dx_y = (softmax(x_y) - label_smoothing / V - (1 - label_smoothing)) / N
189
- # = dx_i - (1 - label_smoothing) / N
190
- for iv in range(0, NV):
191
- o_v = iv * BV + tl.arange(0, BV)
192
- b_logits = tl.load(logits + o_v, mask=o_v < V, other=float('-inf')).to(tl.float32) * logit_scale
193
- if logit_softcapping is not None:
194
- b_t = tanh(b_logits / logit_softcapping)
195
- b_capped = logit_softcapping * b_t
196
- else:
197
- b_capped = b_logits
198
- if label_smoothing > 0:
199
- # scale X beforehand to avoid overflow
200
- b_z += tl.sum(tl.where(o_v < V, -eps * b_capped, 0.0))
201
- b_p = (exp(b_capped - b_lse) - eps) * logit_scale
202
- # d(softcap * tanh(x/softcap))/dx = 1 - tanh(x/softcap)^2
203
- if logit_softcapping is not None:
204
- b_p = b_p * (1.0 - b_t * b_t)
205
- if reduction == "mean":
206
- b_p = b_p / total
207
- tl.store(logits + o_v, b_p, mask=o_v < V)
208
-
209
- tl.debug_barrier()
210
-
211
- # Original loss = H(q, p), with label smoothing regularization = H(q', p) and (label_smoothing / V) = eps
212
- # H(q', p) = (1 - label_smoothing) * H(q, p) + label_smoothing * H(u, p)
213
- # = (1 - label_smoothing) * H(q, p) + eps * sum(logsoftmax(x_i))
214
- # By using m (global max of xi) and d (sum of e^(xi-m)), we can simplify as:
215
- # = (1 - label_smoothing) * H(q, p) + (-sum(x_i * eps) + label_smoothing * (m + logd))
216
- # Refer to H(q', p) in section 7 of the paper:
217
- # https://arxiv.org/pdf/1512.00567
218
- # pytorch:
219
- # https://github.com/pytorch/pytorch/blob/2981534f54d49fa3a9755c9b0855e7929c2527f0/aten/src/ATen/native/LossNLL.cpp#L516
220
- # See full derivation at https://github.com/linkedin/Liger-Kernel/pull/198#issuecomment-2333753087
221
- if label_smoothing > 0:
222
- b_loss = b_loss * (1 - label_smoothing) + (b_z + label_smoothing * b_lse)
223
-
224
- # 6. Specially handle the i==y case where `dx_y = (softmax(x_y) - (1 - label_smoothing) / N`
225
- b_l = tl.load(logits + b_y)
226
-
227
- # The correction term also needs the softcap chain rule factor
228
- if logit_softcapping is not None:
229
- b_sc_factor = b_softcap_deriv_y
230
- else:
231
- b_sc_factor = 1.0
232
-
233
- # Normalize the loss by the number of non-ignored elements if reduction is "mean"
234
- if reduction == 'mean':
235
- b_loss = b_loss / total
236
- b_l += (label_smoothing - 1) / total * logit_scale * b_sc_factor
237
- else:
238
- b_l += (label_smoothing - 1) * logit_scale * b_sc_factor
239
-
240
- tl.store(loss + i_n, b_loss)
241
- tl.store(logits + b_y, b_l)
242
-
243
-
244
- @triton.jit
245
- def elementwise_mul_kernel(
246
- x,
247
- g,
248
- N: tl.constexpr,
249
- B: tl.constexpr,
250
- ):
251
- """
252
- This function multiplies each element of the tensor pointed by x with the value pointed by g.
253
- The multiplication is performed in-place on the tensor pointed by x.
254
-
255
- Parameters:
256
- x:
257
- Pointer to the input tensor.
258
- g:
259
- Pointer to the gradient output value.
260
- N (int):
261
- The number of columns in the input tensor.
262
- B (int):
263
- The block size for Triton operations.
264
- """
265
-
266
- # Get the program ID and convert it to int64 to avoid overflow
267
- i_x = tl.program_id(0).to(tl.int64)
268
- o_x = i_x * B + tl.arange(0, B)
269
-
270
- # Load the gradient output value
271
- b_g = tl.load(g)
272
- b_x = tl.load(x + o_x, mask=o_x < N)
273
- tl.store(x + o_x, b_x * b_g, mask=o_x < N)
274
-
275
-
276
- @dispatch('modules')
277
- def fused_linear_cross_entropy_forward(
278
  x: torch.Tensor,
279
  target: torch.LongTensor,
280
  weight: torch.Tensor,
281
- bias: torch.Tensor = None,
282
  ignore_index: int = -100,
283
  label_smoothing: float = 0.0,
284
  logit_scale: float = 1.0,
285
- logit_softcapping: float = None,
286
  num_chunks: int = 8,
287
  reduction: str = "mean",
288
  use_l2warp: bool = False,
289
  l2_penalty_factor: float = 1e-4,
290
  accumulate_grad_in_fp32: bool = True,
 
291
  ):
292
  device = x.device
293
- # inputs have shape: [N, H]
294
- # materialized activations will have shape: [N, V]
295
- # the increase in memory = [N, V]
296
- # reduction can be achieved by partitioning the number of tokens N into smaller chunks.
297
-
298
- # ideally, we would like to achieve the same memory consumption as [N, H],
299
- # so the expected chunk size should be:
300
- # NC = ceil(V / H)
301
- # C = ceil(N / NC)
302
- # for ex: N = 4096*4, V = 32000, H = 4096 ==> NC = 8, C = ceil(N / NC) = 2048
303
  N, H, V = *x.shape, weight.shape[0]
304
- BV = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
305
- # TODO: in real cases, we may need to limit the number of chunks NC to
306
- # ensure the precisions of accumulated gradients
307
  NC = min(num_chunks, triton.cdiv(V, H))
308
  C = triton.next_power_of_2(triton.cdiv(N, NC))
309
  NC = triton.cdiv(N, C)
@@ -322,87 +146,83 @@ def fused_linear_cross_entropy_forward(
322
  # [N]
323
  loss = torch.zeros(N, device=device, dtype=torch.float)
324
 
325
- total = target.ne(ignore_index).sum().item()
 
326
 
327
  for ic in range(NC):
328
  start, end = ic * C, min((ic + 1) * C, N)
329
- # [C, N]
330
  c_x = x[start:end]
331
- # when doing matmul, use the original precision
332
  # [C, V]
333
  c_logits = F.linear(c_x, weight, bias)
334
  if weight is not None and c_x.dtype != grad_dtype:
335
  c_x = c_x.to(dtype=grad_dtype)
336
  c_target = target[start:end]
337
- # [C]
338
- # keep lse in fp32 to maintain precision
339
- c_lse = logsumexp_fwd(c_logits, scale=logit_scale, softcapping=logit_softcapping, dtype=torch.float)
340
-
341
- # unreduced loss
342
- c_loss = loss[start:end]
 
 
 
 
343
  if use_l2warp:
344
  c_maxx, c_ids = torch.max(c_logits, -1, keepdim=True)
345
-
346
- # Here we calculate the gradient of c_logits in place so we can save memory.
347
- cross_entropy_kernel[(c_logits.shape[0],)](
 
 
 
 
 
 
 
 
348
  logits=c_logits,
349
- lse=c_lse,
350
  target=c_target,
351
- loss=c_loss,
352
- total=total,
353
- ignore_index=ignore_index,
354
  label_smoothing=label_smoothing,
355
  logit_scale=logit_scale,
356
  logit_softcapping=logit_softcapping,
357
- reduction=reduction,
358
- V=V,
359
- BV=BV,
360
- num_warps=STATIC_WARPS,
361
  )
362
  if use_l2warp:
363
- # a. Calculate the L2 gradient w.r.t logits (g_logits_l2)
364
  g_logits_l2 = torch.zeros_like(c_logits)
365
 
366
- # Match L2Wrap: normalize by the full number of input tokens, not by non-ignored labels.
367
  l2_factor = l2_penalty_factor / N
368
  penalty_grad = c_maxx * l2_factor
369
  g_logits_l2.scatter_(-1, c_ids, penalty_grad)
370
 
371
- # b. Backpropagate g_logits_l2 to get its effect on dx, dw, db
372
- # and add it to the main gradients.
373
- # Total_dx = CE_dx + L2_dx
374
- # Total_dw = CE_dw + L2_dw
375
- # Total_db = CE_db + L2_db
376
  if weight is not None:
377
- torch.addmm(
378
- input=dw,
379
- mat1=g_logits_l2.t().to(dtype=grad_dtype),
380
- mat2=c_x,
381
- out=dw,
382
- )
383
  if bias is not None:
384
  torch.add(input=db, other=g_logits_l2.sum(0, dtype=bias_grad_dtype), out=db)
385
- # The dx contribution must be added to the final dx calculation
386
  dx_l2_contribution = torch.mm(g_logits_l2, weight)
387
  else:
388
  dx_l2_contribution = 0.0
389
 
390
- # gradient of logits is computed in-place by the above triton kernel and is of shape: C x V
391
- # thus dx should be of shape: C x H
392
  dx[start:end] = torch.mm(c_logits, weight) + dx_l2_contribution
393
 
394
  if weight is not None:
395
- torch.addmm(
396
- input=dw,
397
- mat1=c_logits.t().to(dtype=grad_dtype),
398
- mat2=c_x,
399
- out=dw,
400
- )
401
 
402
  if bias is not None:
403
  torch.add(input=db, other=c_logits.sum(0, dtype=bias_grad_dtype), out=db)
404
 
 
 
405
  loss = loss.sum()
 
 
406
  if dw is not None:
407
  dw = dw.to(weight)
408
  if db is not None:
@@ -410,51 +230,30 @@ def fused_linear_cross_entropy_forward(
410
  return loss, dx, dw, db
411
 
412
 
413
- @dispatch('modules')
414
- def fused_linear_cross_entropy_backward(
415
- do: torch.Tensor,
416
- dx: torch.Tensor,
417
- dw: torch.Tensor,
418
- db: torch.Tensor,
419
- ):
420
- # If cross entropy is the last layer, do is 1.0. Skip the mul to save time
421
- if torch.ne(do, torch.tensor(1.0, device=do.device)):
422
- # We use a Triton kernel instead of a PyTorch operation because modifying inputs in-place
423
- # for gradient storage and backward multiple times causes anomalies with PyTorch but not with Triton.
424
- N, H = dx.shape
425
- B = min(MAX_FUSED_SIZE, triton.next_power_of_2(H))
426
-
427
- elementwise_mul_kernel[(triton.cdiv(N * H, B),)](
428
- x=dx,
429
- g=do,
430
- N=N*H,
431
- B=B,
432
- num_warps=STATIC_WARPS,
433
- )
434
 
435
- # handle dw
436
- if dw is not None:
437
- V, H = dw.shape
438
- elementwise_mul_kernel[(triton.cdiv(V * H, B),)](
439
- x=dw,
440
- g=do,
441
- N=V*H,
442
- B=B,
443
- num_warps=STATIC_WARPS,
444
- )
445
-
446
- if db is not None:
447
- V = db.shape[0]
448
- elementwise_mul_kernel[(triton.cdiv(V, B),)](
449
- x=db,
450
- g=do,
451
- N=V,
452
- B=B,
453
- num_warps=STATIC_WARPS,
454
- )
455
  return dx, dw, db
456
 
457
 
 
 
 
 
 
 
458
  class FusedLinearCrossEntropyFunction(torch.autograd.Function):
459
 
460
  @staticmethod
@@ -464,76 +263,35 @@ class FusedLinearCrossEntropyFunction(torch.autograd.Function):
464
  x: torch.Tensor,
465
  target: torch.LongTensor,
466
  weight: torch.Tensor,
467
- bias: torch.Tensor = None,
468
  ignore_index: int = -100,
469
  label_smoothing: float = 0.0,
470
  logit_scale: float = 1.0,
471
- logit_softcapping: float = None,
472
  num_chunks: int = 8,
473
  reduction: str = "mean",
474
  use_l2warp: bool = False,
475
  l2_penalty_factor: float = 1e-4,
476
  accumulate_grad_in_fp32: bool = True,
 
477
  ):
478
- """
479
- Fusing the last linear layer with cross-entropy loss
480
- Reference: https://github.com/mgmalek/efficient_cross_entropy
481
-
482
- Handle the forward and backward pass of the final linear layer via cross-entropy loss by avoiding
483
- the materialization of the large logits tensor. Since Cross Entropy Loss is the last layer, we can
484
- compute the gradient at the forward pass. By doing so, we don't have to store the x and target
485
- for the backward pass.
486
-
487
- x (torch.Tensor): [batch_size * seq_len, hidden_size]
488
- target (torch.LongTensor): [batch_size * seq_len]
489
- where each value is in [0, vocab_size).
490
- weight (torch.Tensor): [vocab_size, hidden_size]
491
- where `vocab_size` is the number of classes.
492
- bias (Optional[torch.Tensor]): [vocab_size]
493
- where `vocab_size` is the number of classes.
494
- ignore_index:
495
- the index to ignore in the target.
496
- label_smoothing:
497
- the amount of smoothing when computing the loss, where 0.0 means no smoothing.
498
- logit_scale: float = 1.0,
499
- A scaling factor applied to the logits. Default: 1.0
500
- logit_softcapping: float = None,
501
- If > 0, apply logit softcapping: logits = softcap * tanh(logits / softcap).
502
- Default: 0.0
503
- num_chunks: int
504
- The number of chunks to split the input tensor into for processing.
505
- This can help optimize memory usage and computation speed.
506
- Default: 8
507
- reduction:
508
- Specifies the reduction to apply to the output: 'mean' | 'sum'.
509
- 'mean': the weighted mean of the output is taken,
510
- 'sum': the output will be summed.
511
- Default: 'mean'.
512
- use_l2warp: bool = False,
513
- Whether to use L2 regularization on the logits to prevent overconfidence.
514
- Default: False
515
- l2_penalty_factor: float = 1e-4,
516
- The L2Warp penalty factor. Default: 1e-4
517
- accumulate_grad_in_fp32: bool = True,
518
- Whether to accumulate weight and bias gradients in fp32 before casting them
519
- back to the parameter dtype. Default: True
520
- """
521
- loss, dx, dw, db = fused_linear_cross_entropy_forward(
522
- x,
523
- target,
524
- weight,
525
- bias,
526
- ignore_index,
527
- label_smoothing,
528
- logit_scale,
529
- logit_softcapping,
530
- num_chunks,
531
- reduction,
532
- use_l2warp,
533
- l2_penalty_factor,
534
- accumulate_grad_in_fp32,
535
  )
536
- # downcast to dtype and store for backward
537
  ctx.save_for_backward(
538
  dx.detach(),
539
  dw.detach() if weight is not None else None,
@@ -545,62 +303,71 @@ class FusedLinearCrossEntropyFunction(torch.autograd.Function):
545
  @input_guard
546
  def backward(ctx, do):
547
  dx, dw, db = ctx.saved_tensors
548
- dx, dw, db = fused_linear_cross_entropy_backward(do, dx, dw, db)
549
- return dx, None, dw, db, None, None, None, None, None, None, None, None, None
550
 
551
 
552
  def fused_linear_cross_entropy_loss(
553
  x: torch.Tensor,
554
  target: torch.LongTensor,
555
  weight: torch.Tensor,
556
- bias: torch.Tensor = None,
557
  ignore_index: int = -100,
558
  label_smoothing: float = 0.0,
559
  logit_scale: float = 1.0,
560
- logit_softcapping: float = None,
561
  num_chunks: int = 8,
562
  reduction: str = "mean",
563
  use_l2warp: bool = False,
564
  l2_penalty_factor: float = 1e-4,
565
  accumulate_grad_in_fp32: bool = True,
566
- ) -> tuple[torch.Tensor, torch.Tensor]:
 
567
  """
568
  Args:
569
- x (torch.Tensor): [batch_size * seq_len, hidden_size]
570
- target (torch.LongTensor): [batch_size * seq_len]
571
- where each value is in [0, vocab_size).
572
- weight (torch.Tensor): [vocab_size, hidden_size]
573
- where `vocab_size` is the number of classes.
574
- bias (Optional[torch.Tensor]): [vocab_size]
575
- where `vocab_size` is the number of classes.
576
- ignore_index: int.
577
- If target == ignore_index, the loss is set to 0.0.
578
- label_smoothing: float
579
- logit_scale: float
580
- A scaling factor applied to the logits. Default: 1.0
581
- logit_softcapping: float
582
- If > 0, apply logit softcapping: logits = softcap * tanh(logits / softcap).
583
- Default: 0.0
584
- num_chunks: int
 
 
585
  The number of chunks to split the input tensor into for processing.
586
  This can help optimize memory usage and computation speed.
587
- Default: 8
588
- reduction:
589
  Specifies the reduction to apply to the output: 'mean' | 'sum'.
590
  'mean': the weighted mean of the output is taken,
591
  'sum': the output will be summed.
592
  Default: 'mean'.
593
- use_l2warp:
594
- Whether to add the L2Warp logit regularization gradient. The penalty is normalized by
595
- the full number of input tokens, matching `fla.modules.l2warp.l2_warp`.
596
  Default: `False`.
597
- l2_penalty_factor:
598
  The L2Warp penalty factor. Default: `1e-4`.
599
- accumulate_grad_in_fp32:
600
- Whether to accumulate weight and bias gradients in fp32 before casting them
601
- back to the parameter dtype. Default: `True`.
 
 
 
 
 
602
  Returns:
603
- losses: [batch,], float
 
604
  """
605
  return FusedLinearCrossEntropyFunction.apply(
606
  x,
@@ -616,6 +383,7 @@ def fused_linear_cross_entropy_loss(
616
  use_l2warp,
617
  l2_penalty_factor,
618
  accumulate_grad_in_fp32,
 
619
  )
620
 
621
 
@@ -626,41 +394,47 @@ class FusedLinearCrossEntropyLoss(nn.Module):
626
  ignore_index: int = -100,
627
  label_smoothing: float = 0.0,
628
  logit_scale: float = 1.0,
629
- logit_softcapping: float = None,
630
  num_chunks: int = 8,
631
  reduction: str = "mean",
632
  use_l2warp: bool = False,
633
  l2_penalty_factor: float = 1e-4,
634
  accumulate_grad_in_fp32: bool = True,
 
635
  ):
636
  """
637
  Args:
638
- ignore_index: int.
639
- If target == ignore_index, the loss is set to 0.0.
640
- label_smoothing: float
641
- logit_scale: float
642
- A scaling factor applied to the logits. Default: 1.0
643
- logit_softcapping: float
644
- If > 0, apply logit softcapping: logits = softcap * tanh(logits / softcap).
645
- Default: 0.0
646
- num_chunks: int
 
647
  The number of chunks to split the input tensor into for processing.
648
  This can help optimize memory usage and computation speed.
649
- Default: 8
650
- reduction:
651
  Specifies the reduction to apply to the output: 'mean' | 'sum'.
652
  'mean': the weighted mean of the output is taken,
653
  'sum': the output will be summed.
654
  Default: 'mean'.
655
- use_l2warp:
656
- Whether to add the L2Warp logit regularization gradient. The penalty is normalized by
657
- the full number of input tokens, matching `fla.modules.l2warp.l2_warp`.
658
  Default: `False`.
659
- l2_penalty_factor:
660
  The L2Warp penalty factor. Default: `1e-4`.
661
- accumulate_grad_in_fp32:
662
- Whether to accumulate weight and bias gradients in fp32 before casting them
663
- back to the parameter dtype. Default: `True`.
 
 
 
 
664
  """
665
  super().__init__()
666
 
@@ -675,30 +449,28 @@ class FusedLinearCrossEntropyLoss(nn.Module):
675
  self.use_l2warp = use_l2warp
676
  self.l2_penalty_factor = l2_penalty_factor
677
  self.accumulate_grad_in_fp32 = accumulate_grad_in_fp32
 
678
 
679
  @torch.compiler.disable
680
- def forward(
681
- self,
682
- x: torch.Tensor,
683
- target: torch.LongTensor,
684
- weight: torch.Tensor,
685
- bias: torch.Tensor | None = None,
686
- ):
687
  """
688
  Args:
689
- x (torch.Tensor): [batch_size, seq_len, hidden_size]
690
- target (torch.LongTensor): [batch_size, seq_len]
691
- where each value is in [0, V).
692
- weight (torch.Tensor): [vocab_size, hidden_size]
693
- where `vocab_size` is the number of classes.
694
- bias (Optional[torch.Tensor]): [vocab_size]
695
- where `vocab_size` is the number of classes.
 
 
696
  Returns:
697
- loss
 
698
  """
699
  loss = fused_linear_cross_entropy_loss(
700
- x.view(-1, x.shape[-1]),
701
- target.view(-1),
702
  weight=weight,
703
  bias=bias,
704
  ignore_index=self.ignore_index,
@@ -710,17 +482,13 @@ class FusedLinearCrossEntropyLoss(nn.Module):
710
  use_l2warp=self.use_l2warp,
711
  l2_penalty_factor=self.l2_penalty_factor,
712
  accumulate_grad_in_fp32=self.accumulate_grad_in_fp32,
 
713
  )
714
  return loss
715
 
716
 
717
  class LinearLossParallel(ParallelStyle):
718
- def __init__(
719
- self,
720
- *,
721
- sequence_dim: int = 1,
722
- use_local_output: bool = False,
723
- ):
724
  super().__init__()
725
 
726
  self.sequence_sharding = (Shard(sequence_dim),)
@@ -755,7 +523,7 @@ class LinearLossParallel(ParallelStyle):
755
 
756
  @staticmethod
757
  def _prepare_output_fn(use_local_output, mod, outputs, device_mesh):
758
- return outputs.to_local() if use_local_output else outputs
759
 
760
  def _apply(self, module: nn.Module, device_mesh: DeviceMesh) -> nn.Module:
761
  return distribute_module(
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ # adapted from https://github.com/linkedin/Liger-Kernel/blob/main/src/liger_kernel/ops/fused_linear_cross_entropy.py
9
+
10
+ from __future__ import annotations
11
 
12
  from functools import partial
13
+ from typing import TYPE_CHECKING
14
 
15
  import torch
16
  import torch.nn as nn
 
21
  from torch.distributed.tensor import Replicate, Shard, distribute_module
22
  from torch.distributed.tensor.parallel import ParallelStyle
23
 
24
+ from ...backends import dispatch
25
+ from ...modules.fused_cross_entropy.ops import cross_entropy_bwd, cross_entropy_fwd
26
+ from ...ops.utils.op import exp, log, tanh
27
+ from ...utils import IS_AMD, input_guard
28
+
29
+ if TYPE_CHECKING:
30
+ from torch.distributed import ProcessGroup
31
 
32
  try:
33
  from torch.distributed.tensor import DTensor
34
  except (ImportError, AttributeError):
35
  DTensor = None
36
 
37
+ # the hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576
38
  # https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
39
+ # however, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
40
+ # the optimal maximum block size depends on your hardware, your kernel, and your dtype
41
  MAX_FUSED_SIZE = 65536 // 2
42
  STATIC_WARPS = 32 if not IS_AMD else 16
43
 
 
48
  })
49
  @triton.jit
50
  def logsumexp_fwd_kernel(
51
+ x, # [N, D]
52
+ z, # [N, ND]
53
  scale,
54
  softcapping,
55
  D: tl.constexpr,
56
+ BD: tl.constexpr,
57
  HAS_SCALE: tl.constexpr,
58
  HAS_SOFTCAPPING: tl.constexpr,
59
  ):
60
  i_n, i_d = tl.program_id(0).to(tl.int64), tl.program_id(1).to(tl.int64)
61
+ o_d = i_d * BD + tl.arange(0, BD)
62
  m_d = o_d < D
63
 
64
+ # [BD]
65
  b_x = tl.load(x + i_n * D + o_d, mask=m_d, other=-float('inf'))
66
  if HAS_SCALE:
67
  b_x = b_x * scale
68
  if HAS_SOFTCAPPING:
69
  b_x = softcapping * tanh(b_x / softcapping)
70
+ b_x = tl.where(m_d, b_x, -float('inf'))
71
  b_m = tl.max(b_x, 0)
72
  b_z = log(tl.sum(exp(b_x - b_m), 0)) + b_m
73
+ tl.store(z + i_n * tl.cdiv(D, BD) + i_d, b_z)
74
 
75
 
76
+ @triton.jit
77
+ def elementwise_mul_kernel(
78
+ x, # [N]
79
+ g, # scalar
80
+ N: tl.constexpr,
81
+ BN: tl.constexpr,
82
  ):
83
+ i_x = tl.program_id(0).to(tl.int64)
84
+ o_x = i_x * BN + tl.arange(0, BN)
85
+
86
+ b_g = tl.load(g)
87
+ if b_g == 1.0:
88
+ return
89
+ # [BN]
90
+ b_x = tl.load(x + o_x, mask=o_x < N)
91
+ tl.store(x + o_x, b_x * b_g, mask=o_x < N)
92
+
93
+
94
+ @dispatch
95
+ def logsumexp_fwd(x, scale: float | None = None, softcapping: float | None = None, dtype: torch.dtype | None = None):
96
  shape = x.shape
97
  x = x.view(-1, shape[-1])
98
  N, D = x.shape
99
+ BD = min(triton.next_power_of_2(D), 64 * 1024)
100
+ ND = triton.cdiv(D, BD)
101
 
102
  z = x.new_empty(N, ND, dtype=torch.float)
103
+ logsumexp_fwd_kernel[(N, ND)](x=x, z=z, scale=scale, softcapping=softcapping, D=D, BD=BD)
 
 
 
 
 
 
 
104
  z = z.logsumexp(-1).view(*shape[:-1])
105
  if dtype is not None and dtype != torch.float:
106
  z = z.to(dtype)
107
  return z
108
 
109
 
110
+ @dispatch
111
+ def fused_linear_cross_entropy_fwd(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  x: torch.Tensor,
113
  target: torch.LongTensor,
114
  weight: torch.Tensor,
115
+ bias: torch.Tensor | None = None,
116
  ignore_index: int = -100,
117
  label_smoothing: float = 0.0,
118
  logit_scale: float = 1.0,
119
+ logit_softcapping: float | None = None,
120
  num_chunks: int = 8,
121
  reduction: str = "mean",
122
  use_l2warp: bool = False,
123
  l2_penalty_factor: float = 1e-4,
124
  accumulate_grad_in_fp32: bool = True,
125
+ process_group: ProcessGroup | None = None,
126
  ):
127
  device = x.device
128
+ world_size = 1 if process_group is None else torch.distributed.get_world_size(process_group)
129
+ # chunking keeps temporary logits storage close to the input tensor size.
 
 
 
 
 
 
 
 
130
  N, H, V = *x.shape, weight.shape[0]
 
 
 
131
  NC = min(num_chunks, triton.cdiv(V, H))
132
  C = triton.next_power_of_2(triton.cdiv(N, NC))
133
  NC = triton.cdiv(N, C)
 
146
  # [N]
147
  loss = torch.zeros(N, device=device, dtype=torch.float)
148
 
149
+ total = target.ne(ignore_index).sum().clamp_min(1)
150
+ dloss = total.float().reciprocal() if reduction == "mean" else x.new_ones((), dtype=torch.float)
151
 
152
  for ic in range(NC):
153
  start, end = ic * C, min((ic + 1) * C, N)
154
+ # [C, H]
155
  c_x = x[start:end]
 
156
  # [C, V]
157
  c_logits = F.linear(c_x, weight, bias)
158
  if weight is not None and c_x.dtype != grad_dtype:
159
  c_x = c_x.to(dtype=grad_dtype)
160
  c_target = target[start:end]
161
+ c_loss, _, c_lse, total_classes, class_start_idx = cross_entropy_fwd(
162
+ logits=c_logits,
163
+ target=c_target,
164
+ label_smoothing=label_smoothing,
165
+ logit_scale=logit_scale,
166
+ logit_softcapping=logit_softcapping,
167
+ ignore_index=ignore_index,
168
+ process_group=process_group,
169
+ )
170
+ loss[start:end] = c_loss
171
  if use_l2warp:
172
  c_maxx, c_ids = torch.max(c_logits, -1, keepdim=True)
173
+ if world_size > 1:
174
+ # only the shard owning the first global maximum contributes the L2 gradient
175
+ local_max = c_maxx.clone()
176
+ torch.distributed.all_reduce(c_maxx, op=torch.distributed.ReduceOp.MAX, group=process_group)
177
+ c_ids = torch.where(local_max == c_maxx, c_ids + class_start_idx, total_classes)
178
+ torch.distributed.all_reduce(c_ids, op=torch.distributed.ReduceOp.MIN, group=process_group)
179
+ c_ids -= class_start_idx
180
+ c_maxx = torch.where((c_ids >= 0) & (c_ids < V), c_maxx, 0)
181
+ c_ids = c_ids.clamp(0, V - 1)
182
+
183
+ cross_entropy_bwd(
184
  logits=c_logits,
 
185
  target=c_target,
186
+ lse=c_lse,
187
+ dloss=dloss.expand(end - start),
 
188
  label_smoothing=label_smoothing,
189
  logit_scale=logit_scale,
190
  logit_softcapping=logit_softcapping,
191
+ ignore_index=ignore_index,
192
+ total_classes=total_classes,
193
+ class_start_idx=class_start_idx,
194
+ inplace_backward=True,
195
  )
196
  if use_l2warp:
 
197
  g_logits_l2 = torch.zeros_like(c_logits)
198
 
199
+ # match L2Wrap: normalize by the full number of input tokens, not by non-ignored labels.
200
  l2_factor = l2_penalty_factor / N
201
  penalty_grad = c_maxx * l2_factor
202
  g_logits_l2.scatter_(-1, c_ids, penalty_grad)
203
 
 
 
 
 
 
204
  if weight is not None:
205
+ torch.addmm(input=dw, mat1=g_logits_l2.t().to(dtype=grad_dtype), mat2=c_x, out=dw)
 
 
 
 
 
206
  if bias is not None:
207
  torch.add(input=db, other=g_logits_l2.sum(0, dtype=bias_grad_dtype), out=db)
208
+ # the dx contribution must be added to the final dx calculation
209
  dx_l2_contribution = torch.mm(g_logits_l2, weight)
210
  else:
211
  dx_l2_contribution = 0.0
212
 
 
 
213
  dx[start:end] = torch.mm(c_logits, weight) + dx_l2_contribution
214
 
215
  if weight is not None:
216
+ torch.addmm(input=dw, mat1=c_logits.t().to(dtype=grad_dtype), mat2=c_x, out=dw)
 
 
 
 
 
217
 
218
  if bias is not None:
219
  torch.add(input=db, other=c_logits.sum(0, dtype=bias_grad_dtype), out=db)
220
 
221
+ if world_size > 1:
222
+ torch.distributed.all_reduce(dx, group=process_group)
223
  loss = loss.sum()
224
+ if reduction == "mean":
225
+ loss = loss / total
226
  if dw is not None:
227
  dw = dw.to(weight)
228
  if db is not None:
 
230
  return loss, dx, dw, db
231
 
232
 
233
+ @dispatch
234
+ def fused_linear_cross_entropy_bwd(do: torch.Tensor, dx: torch.Tensor, dw: torch.Tensor, db: torch.Tensor | None):
235
+ # in-place gradient storage across repeated backward calls causes PyTorch anomalies; use Triton.
236
+ N, H = dx.shape
237
+ BN = min(MAX_FUSED_SIZE, triton.next_power_of_2(H))
238
+
239
+ elementwise_mul_kernel[(triton.cdiv(N * H, BN),)](x=dx, g=do, N=N*H, BN=BN, num_warps=STATIC_WARPS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
+ if dw is not None:
242
+ V, H = dw.shape
243
+ elementwise_mul_kernel[(triton.cdiv(V * H, BN),)](x=dw, g=do, N=V*H, BN=BN, num_warps=STATIC_WARPS)
244
+
245
+ if db is not None:
246
+ V = db.shape[0]
247
+ elementwise_mul_kernel[(triton.cdiv(V, BN),)](x=db, g=do, N=V, BN=BN, num_warps=STATIC_WARPS)
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  return dx, dw, db
249
 
250
 
251
+ fused_linear_cross_entropy_forward = fused_linear_cross_entropy_fwd
252
+
253
+
254
+ fused_linear_cross_entropy_backward = fused_linear_cross_entropy_bwd
255
+
256
+
257
  class FusedLinearCrossEntropyFunction(torch.autograd.Function):
258
 
259
  @staticmethod
 
263
  x: torch.Tensor,
264
  target: torch.LongTensor,
265
  weight: torch.Tensor,
266
+ bias: torch.Tensor | None = None,
267
  ignore_index: int = -100,
268
  label_smoothing: float = 0.0,
269
  logit_scale: float = 1.0,
270
+ logit_softcapping: float | None = None,
271
  num_chunks: int = 8,
272
  reduction: str = "mean",
273
  use_l2warp: bool = False,
274
  l2_penalty_factor: float = 1e-4,
275
  accumulate_grad_in_fp32: bool = True,
276
+ process_group: ProcessGroup | None = None,
277
  ):
278
+ """Precompute gradients per token chunk so backward does not retain the logits."""
279
+ loss, dx, dw, db = fused_linear_cross_entropy_fwd(
280
+ x=x,
281
+ target=target,
282
+ weight=weight,
283
+ bias=bias,
284
+ ignore_index=ignore_index,
285
+ label_smoothing=label_smoothing,
286
+ logit_scale=logit_scale,
287
+ logit_softcapping=logit_softcapping,
288
+ num_chunks=num_chunks,
289
+ reduction=reduction,
290
+ use_l2warp=use_l2warp,
291
+ l2_penalty_factor=l2_penalty_factor,
292
+ accumulate_grad_in_fp32=accumulate_grad_in_fp32,
293
+ process_group=process_group,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  )
 
295
  ctx.save_for_backward(
296
  dx.detach(),
297
  dw.detach() if weight is not None else None,
 
303
  @input_guard
304
  def backward(ctx, do):
305
  dx, dw, db = ctx.saved_tensors
306
+ dx, dw, db = fused_linear_cross_entropy_bwd(do=do, dx=dx, dw=dw, db=db)
307
+ return dx, None, dw, db, None, None, None, None, None, None, None, None, None, None
308
 
309
 
310
  def fused_linear_cross_entropy_loss(
311
  x: torch.Tensor,
312
  target: torch.LongTensor,
313
  weight: torch.Tensor,
314
+ bias: torch.Tensor | None = None,
315
  ignore_index: int = -100,
316
  label_smoothing: float = 0.0,
317
  logit_scale: float = 1.0,
318
+ logit_softcapping: float | None = None,
319
  num_chunks: int = 8,
320
  reduction: str = "mean",
321
  use_l2warp: bool = False,
322
  l2_penalty_factor: float = 1e-4,
323
  accumulate_grad_in_fp32: bool = True,
324
+ process_group: ProcessGroup | None = None,
325
+ ) -> torch.Tensor:
326
  """
327
  Args:
328
+ x (torch.Tensor):
329
+ Input of shape `[batch_size * seq_len, hidden_size]`.
330
+ target (torch.LongTensor):
331
+ Target indices of shape `[batch_size * seq_len]` with values in `[0, vocab_size)`.
332
+ weight (torch.Tensor):
333
+ Projection weight of shape `[vocab_size, hidden_size]` where `vocab_size` is the number of classes.
334
+ bias (torch.Tensor, Optional):
335
+ Projection bias of shape `[vocab_size]`. Default: `None`.
336
+ ignore_index (int, Optional):
337
+ Target index excluded from the loss. Default: -100.
338
+ label_smoothing (float, Optional):
339
+ Uniform label smoothing coefficient. Default: 0.0.
340
+ logit_scale (float, Optional):
341
+ A scaling factor applied to the logits. Default: 1.0.
342
+ logit_softcapping (float, Optional):
343
+ If not `None`, apply logit softcapping: logits = softcap * tanh(logits / softcap).
344
+ Default: `None`.
345
+ num_chunks (int, Optional):
346
  The number of chunks to split the input tensor into for processing.
347
  This can help optimize memory usage and computation speed.
348
+ Default: 8.
349
+ reduction (str, Optional):
350
  Specifies the reduction to apply to the output: 'mean' | 'sum'.
351
  'mean': the weighted mean of the output is taken,
352
  'sum': the output will be summed.
353
  Default: 'mean'.
354
+ use_l2warp (bool, Optional):
355
+ Whether to add the L2Warp logit regularization gradient.
356
+ The penalty is normalized by the full number of input tokens, matching `fla.modules.l2warp.l2_warp`.
357
  Default: `False`.
358
+ l2_penalty_factor (float, Optional):
359
  The L2Warp penalty factor. Default: `1e-4`.
360
+ accumulate_grad_in_fp32 (bool, Optional):
361
+ Whether to accumulate weight and bias gradients in fp32 before casting them back to the parameter dtype.
362
+ Default: `True`.
363
+ process_group (ProcessGroup, Optional):
364
+ Group with equal contiguous vocabulary shards of weight and bias, and replicated inputs and targets.
365
+ Loss and input gradients are replicated; weight and bias gradients remain sharded. Default: `None`.
366
+ Multi-rank groups are not supported by the Ascend backend.
367
+
368
  Returns:
369
+ torch.Tensor:
370
+ Scalar loss in fp32.
371
  """
372
  return FusedLinearCrossEntropyFunction.apply(
373
  x,
 
383
  use_l2warp,
384
  l2_penalty_factor,
385
  accumulate_grad_in_fp32,
386
+ process_group,
387
  )
388
 
389
 
 
394
  ignore_index: int = -100,
395
  label_smoothing: float = 0.0,
396
  logit_scale: float = 1.0,
397
+ logit_softcapping: float | None = None,
398
  num_chunks: int = 8,
399
  reduction: str = "mean",
400
  use_l2warp: bool = False,
401
  l2_penalty_factor: float = 1e-4,
402
  accumulate_grad_in_fp32: bool = True,
403
+ process_group: ProcessGroup | None = None,
404
  ):
405
  """
406
  Args:
407
+ ignore_index (int, Optional):
408
+ Target index excluded from the loss. Default: -100.
409
+ label_smoothing (float, Optional):
410
+ Uniform label smoothing coefficient. Default: 0.0.
411
+ logit_scale (float, Optional):
412
+ A scaling factor applied to the logits. Default: 1.0.
413
+ logit_softcapping (float, Optional):
414
+ If not `None`, apply logit softcapping: logits = softcap * tanh(logits / softcap).
415
+ Default: `None`.
416
+ num_chunks (int, Optional):
417
  The number of chunks to split the input tensor into for processing.
418
  This can help optimize memory usage and computation speed.
419
+ Default: 8.
420
+ reduction (str, Optional):
421
  Specifies the reduction to apply to the output: 'mean' | 'sum'.
422
  'mean': the weighted mean of the output is taken,
423
  'sum': the output will be summed.
424
  Default: 'mean'.
425
+ use_l2warp (bool, Optional):
426
+ Whether to add the L2Warp logit regularization gradient.
427
+ The penalty is normalized by the full number of input tokens, matching `fla.modules.l2warp.l2_warp`.
428
  Default: `False`.
429
+ l2_penalty_factor (float, Optional):
430
  The L2Warp penalty factor. Default: `1e-4`.
431
+ accumulate_grad_in_fp32 (bool, Optional):
432
+ Whether to accumulate weight and bias gradients in fp32 before casting them back to the parameter dtype.
433
+ Default: `True`.
434
+ process_group (ProcessGroup, Optional):
435
+ Group with equal contiguous vocabulary shards of weight and bias, and replicated inputs and targets.
436
+ Loss and input gradients are replicated; weight and bias gradients remain sharded. Default: `None`.
437
+ Multi-rank groups are not supported by the Ascend backend.
438
  """
439
  super().__init__()
440
 
 
449
  self.use_l2warp = use_l2warp
450
  self.l2_penalty_factor = l2_penalty_factor
451
  self.accumulate_grad_in_fp32 = accumulate_grad_in_fp32
452
+ self.process_group = process_group
453
 
454
  @torch.compiler.disable
455
+ def forward(self, x: torch.Tensor, target: torch.LongTensor, weight: torch.Tensor, bias: torch.Tensor | None = None):
 
 
 
 
 
 
456
  """
457
  Args:
458
+ x (torch.Tensor):
459
+ Input of shape `[batch_size, seq_len, hidden_size]`.
460
+ target (torch.LongTensor):
461
+ Target indices of shape `[batch_size, seq_len]` with values in `[0, vocab_size)`.
462
+ weight (torch.Tensor):
463
+ Projection weight of shape `[vocab_size, hidden_size]` where `vocab_size` is the number of classes.
464
+ bias (torch.Tensor, Optional):
465
+ Projection bias of shape `[vocab_size]`. Default: `None`.
466
+
467
  Returns:
468
+ torch.Tensor:
469
+ Scalar loss in fp32.
470
  """
471
  loss = fused_linear_cross_entropy_loss(
472
+ x=x.reshape(-1, x.shape[-1]),
473
+ target=target.reshape(-1),
474
  weight=weight,
475
  bias=bias,
476
  ignore_index=self.ignore_index,
 
482
  use_l2warp=self.use_l2warp,
483
  l2_penalty_factor=self.l2_penalty_factor,
484
  accumulate_grad_in_fp32=self.accumulate_grad_in_fp32,
485
+ process_group=self.process_group,
486
  )
487
  return loss
488
 
489
 
490
  class LinearLossParallel(ParallelStyle):
491
+ def __init__(self, *, sequence_dim: int = 1, use_local_output: bool = False):
 
 
 
 
 
492
  super().__init__()
493
 
494
  self.sequence_sharding = (Shard(sequence_dim),)
 
523
 
524
  @staticmethod
525
  def _prepare_output_fn(use_local_output, mod, outputs, device_mesh):
526
+ return outputs.to_local() if use_local_output and isinstance(outputs, DTensor) else outputs
527
 
528
  def _apply(self, module: nn.Module, device_mesh: DeviceMesh) -> nn.Module:
529
  return distribute_module(
build/torch-cuda/modules/fused_norm_gate.py DELETED
@@ -1,1245 +0,0 @@
1
- # Copyright (c) 2023-2026, Songlin Yang, Yu Zhang, Zhiyuan Li
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- # For a list of all contributors, visit:
6
- # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
-
8
- from __future__ import annotations
9
-
10
- import math
11
-
12
- import torch
13
- import torch.nn as nn
14
- import torch.nn.functional as F
15
- import triton
16
- import triton.language as tl
17
-
18
- from ..utils import autotune_cache_kwargs, get_multiprocessor_count, input_guard
19
-
20
-
21
- @triton.heuristics(
22
- {
23
- "STORE_RESIDUAL_OUT": lambda args: args["residual_out"] is not None,
24
- "HAS_RESIDUAL": lambda args: args["residual"] is not None,
25
- "HAS_WEIGHT": lambda args: args["w"] is not None,
26
- "HAS_BIAS": lambda args: args["b"] is not None,
27
- }
28
- )
29
- @triton.autotune(
30
- configs=[triton.Config({"BT": BT}, num_warps=num_warps) for BT in [16, 32, 64] for num_warps in [4, 8, 16]],
31
- key=["D", "NB", "IS_RMS_NORM", "STORE_RESIDUAL_OUT", "HAS_RESIDUAL", "HAS_WEIGHT"],
32
- **autotune_cache_kwargs,
33
- )
34
- @triton.jit
35
- def layer_norm_gated_fwd_kernel(
36
- x, # pointer to the input
37
- g, # pointer to the gate
38
- y, # pointer to the output
39
- w, # pointer to the weights
40
- b, # pointer to the biases
41
- residual, # pointer to the residual
42
- residual_out, # pointer to the residual
43
- mean, # pointer to the mean
44
- rstd, # pointer to the 1/std
45
- eps, # epsilon to avoid division by zero
46
- T, # number of rows in x
47
- D: tl.constexpr, # number of columns in x
48
- BT: tl.constexpr,
49
- BD: tl.constexpr,
50
- NB: tl.constexpr,
51
- ACTIVATION: tl.constexpr,
52
- IS_RMS_NORM: tl.constexpr,
53
- STORE_RESIDUAL_OUT: tl.constexpr,
54
- HAS_RESIDUAL: tl.constexpr,
55
- HAS_WEIGHT: tl.constexpr,
56
- HAS_BIAS: tl.constexpr,
57
- ):
58
- i_t = tl.program_id(0)
59
-
60
- o_d = tl.arange(0, BD)
61
- m_d = o_d < D
62
-
63
- p_x = tl.make_block_ptr(x, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
64
- b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
65
- if HAS_RESIDUAL:
66
- p_res = tl.make_block_ptr(residual, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
67
- b_x += tl.load(p_res, boundary_check=(0, 1)).to(tl.float32)
68
- if STORE_RESIDUAL_OUT:
69
- p_res_out = tl.make_block_ptr(residual_out, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
70
- tl.store(p_res_out, b_x.to(p_res_out.dtype.element_ty), boundary_check=(0, 1))
71
- if not IS_RMS_NORM:
72
- b_mean = tl.sum(b_x, axis=1) / D
73
- p_mean = tl.make_block_ptr(mean, (T,), (1,), (i_t * BT,), (BT,), (0,))
74
- tl.store(p_mean, b_mean.to(p_mean.dtype.element_ty), boundary_check=(0,))
75
- b_xbar = tl.where(m_d[None, :], b_x - b_mean[:, None], 0.0)
76
- b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
77
- else:
78
- b_xbar = tl.where(m_d[None, :], b_x, 0.0)
79
- b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
80
- b_rstd = 1 / tl.sqrt(b_var + eps)
81
-
82
- p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t * BT,), (BT,), (0,))
83
- tl.store(p_rstd, b_rstd.to(p_rstd.dtype.element_ty), boundary_check=(0,))
84
-
85
- if HAS_WEIGHT:
86
- b_w = tl.load(w + o_d, mask=m_d).to(tl.float32)
87
- if HAS_BIAS:
88
- b_b = tl.load(b + o_d, mask=m_d).to(tl.float32)
89
- b_x_hat = (b_x - b_mean[:, None]) * b_rstd[:, None] if not IS_RMS_NORM else b_x * b_rstd[:, None]
90
- b_y = b_x_hat * b_w[None, :] if HAS_WEIGHT else b_x_hat
91
- if HAS_BIAS:
92
- b_y = b_y + b_b[None, :]
93
-
94
- # swish/sigmoid output gate
95
- p_g = tl.make_block_ptr(g, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
96
- b_g = tl.load(p_g, boundary_check=(0, 1)).to(tl.float32)
97
- if ACTIVATION == "swish" or ACTIVATION == "silu":
98
- b_y = b_y * b_g * tl.sigmoid(b_g)
99
- elif ACTIVATION == "sigmoid":
100
- b_y = b_y * tl.sigmoid(b_g)
101
-
102
- # Write output
103
- p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
104
- tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
105
-
106
-
107
- @triton.heuristics(
108
- {
109
- "STORE_RESIDUAL_OUT": lambda args: args["residual_out"] is not None,
110
- "HAS_RESIDUAL": lambda args: args["residual"] is not None,
111
- "HAS_WEIGHT": lambda args: args["w"] is not None,
112
- "HAS_BIAS": lambda args: args["b"] is not None,
113
- }
114
- )
115
- @triton.autotune(
116
- configs=[triton.Config({}, num_warps=num_warps) for num_warps in [2, 4, 8, 16]],
117
- key=["D", "IS_RMS_NORM", "STORE_RESIDUAL_OUT", "HAS_RESIDUAL", "HAS_WEIGHT"],
118
- **autotune_cache_kwargs,
119
- )
120
- @triton.jit
121
- def layer_norm_gated_fwd_kernel1(
122
- x, # pointer to the input
123
- g, # pointer to the gate
124
- y, # pointer to the output
125
- w, # pointer to the weights
126
- b, # pointer to the biases
127
- residual, # pointer to the residual
128
- residual_out, # pointer to the residual
129
- mean, # pointer to the mean
130
- rstd, # pointer to the 1/std
131
- eps, # epsilon to avoid division by zero
132
- D: tl.constexpr, # number of columns in x
133
- BD: tl.constexpr,
134
- ACTIVATION: tl.constexpr,
135
- IS_RMS_NORM: tl.constexpr,
136
- STORE_RESIDUAL_OUT: tl.constexpr,
137
- HAS_RESIDUAL: tl.constexpr,
138
- HAS_WEIGHT: tl.constexpr,
139
- HAS_BIAS: tl.constexpr,
140
- ):
141
- i_t = tl.program_id(0)
142
- x += i_t * D
143
- y += i_t * D
144
- g += i_t * D
145
- if HAS_RESIDUAL:
146
- residual += i_t * D
147
- if STORE_RESIDUAL_OUT:
148
- residual_out += i_t * D
149
-
150
- o_d = tl.arange(0, BD)
151
- m_d = o_d < D
152
- b_x = tl.load(x + o_d, mask=m_d, other=0.0).to(tl.float32)
153
- if HAS_RESIDUAL:
154
- b_x += tl.load(residual + o_d, mask=m_d, other=0.0).to(tl.float32)
155
- if STORE_RESIDUAL_OUT:
156
- tl.store(residual_out + o_d, b_x, mask=m_d)
157
- if not IS_RMS_NORM:
158
- b_mean = tl.sum(b_x, axis=0) / D
159
- tl.store(mean + i_t, b_mean)
160
- b_xbar = tl.where(m_d, b_x - b_mean, 0.0)
161
- b_var = tl.sum(b_xbar * b_xbar, axis=0) / D
162
- else:
163
- b_xbar = tl.where(m_d, b_x, 0.0)
164
- b_var = tl.sum(b_xbar * b_xbar, axis=0) / D
165
- b_rstd = 1 / tl.sqrt(b_var + eps)
166
- tl.store(rstd + i_t, b_rstd)
167
-
168
- if HAS_WEIGHT:
169
- b_w = tl.load(w + o_d, mask=m_d).to(tl.float32)
170
- if HAS_BIAS:
171
- b_b = tl.load(b + o_d, mask=m_d).to(tl.float32)
172
- b_x_hat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
173
- b_y = b_x_hat * b_w if HAS_WEIGHT else b_x_hat
174
- if HAS_BIAS:
175
- b_y = b_y + b_b
176
-
177
- # swish/sigmoid output gate
178
- b_g = tl.load(g + o_d, mask=m_d, other=0.0).to(tl.float32)
179
- if ACTIVATION == "swish" or ACTIVATION == "silu":
180
- b_y = b_y * b_g * tl.sigmoid(b_g)
181
- elif ACTIVATION == "sigmoid":
182
- b_y = b_y * tl.sigmoid(b_g)
183
-
184
- # Write output
185
- tl.store(y + o_d, b_y, mask=m_d)
186
-
187
-
188
- @triton.heuristics(
189
- {
190
- "HAS_DRESIDUAL": lambda args: args["dresidual"] is not None,
191
- "HAS_WEIGHT": lambda args: args["w"] is not None,
192
- "HAS_BIAS": lambda args: args["b"] is not None,
193
- "RECOMPUTE_OUTPUT": lambda args: args["y"] is not None,
194
- }
195
- )
196
- @triton.autotune(
197
- configs=[triton.Config({"BT": BT}, num_warps=num_warps) for BT in [16, 32, 64] for num_warps in [4, 8, 16]],
198
- key=["D", "NB", "IS_RMS_NORM", "HAS_DRESIDUAL", "HAS_WEIGHT"],
199
- **autotune_cache_kwargs,
200
- )
201
- @triton.jit
202
- def layer_norm_gated_bwd_kernel(
203
- x, # pointer to the input
204
- g, # pointer to the gate
205
- w, # pointer to the weights
206
- b, # pointer to the biases
207
- y, # pointer to the output to be recomputed
208
- dy, # pointer to the output gradient
209
- dx, # pointer to the input gradient
210
- dg, # pointer to the gate gradient
211
- dw, # pointer to the partial sum of weights gradient
212
- db, # pointer to the partial sum of biases gradient
213
- dresidual,
214
- dresidual_in,
215
- mean,
216
- rstd,
217
- T,
218
- BS,
219
- D: tl.constexpr,
220
- BT: tl.constexpr,
221
- BD: tl.constexpr,
222
- NB: tl.constexpr,
223
- ACTIVATION: tl.constexpr,
224
- IS_RMS_NORM: tl.constexpr,
225
- STORE_DRESIDUAL: tl.constexpr,
226
- HAS_DRESIDUAL: tl.constexpr,
227
- HAS_WEIGHT: tl.constexpr,
228
- HAS_BIAS: tl.constexpr,
229
- RECOMPUTE_OUTPUT: tl.constexpr,
230
- ):
231
- i_s = tl.program_id(0)
232
- o_d = tl.arange(0, BD)
233
- m_d = o_d < D
234
- if HAS_WEIGHT:
235
- b_w = tl.load(w + o_d, mask=m_d).to(tl.float32)
236
- b_dw = tl.zeros((BT, BD), dtype=tl.float32)
237
- if HAS_BIAS:
238
- b_b = tl.load(b + o_d, mask=m_d, other=0.0).to(tl.float32)
239
- b_db = tl.zeros((BT, BD), dtype=tl.float32)
240
-
241
- # the caller guarantees NS = min(SM, T), so every program has at least one token.
242
- # the last program's range may slightly exceed T (since BS = ceil(T/NS));
243
- # make_block_ptr uses the true tensor shape (T, D), so boundary_check
244
- # handles the partial tail tile by zero-padding loads and skipping stores.
245
- # the m_t mask below further ensures dw/db only accumulate valid rows (< T).
246
- for i_t in range(i_s * BS, i_s * BS + BS, BT):
247
- p_x = tl.make_block_ptr(x, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
248
- p_g = tl.make_block_ptr(g, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
249
- p_dy = tl.make_block_ptr(dy, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
250
- p_dx = tl.make_block_ptr(dx, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
251
- p_dg = tl.make_block_ptr(dg, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
252
- # [BT, BD]
253
- b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
254
- b_g = tl.load(p_g, boundary_check=(0, 1)).to(tl.float32)
255
- b_dy = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
256
-
257
- if not IS_RMS_NORM:
258
- p_mean = tl.make_block_ptr(mean, (T,), (1,), (i_t,), (BT,), (0,))
259
- b_mean = tl.load(p_mean, boundary_check=(0,))
260
- p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t,), (BT,), (0,))
261
- b_rstd = tl.load(p_rstd, boundary_check=(0,))
262
- # Compute dx
263
- b_xhat = (b_x - b_mean[:, None]) * b_rstd[:, None] if not IS_RMS_NORM else b_x * b_rstd[:, None]
264
- b_xhat = tl.where(m_d[None, :], b_xhat, 0.0)
265
-
266
- b_y = b_xhat * b_w[None, :] if HAS_WEIGHT else b_xhat
267
- if HAS_BIAS:
268
- b_y = b_y + b_b[None, :]
269
- if RECOMPUTE_OUTPUT:
270
- p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
271
- tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
272
-
273
- b_sigmoid_g = tl.sigmoid(b_g)
274
- if ACTIVATION == "swish" or ACTIVATION == "silu":
275
- b_dg = b_dy * b_y * (b_sigmoid_g + b_g * b_sigmoid_g * (1 - b_sigmoid_g))
276
- b_dy = b_dy * b_g * b_sigmoid_g
277
- elif ACTIVATION == "sigmoid":
278
- b_dg = b_dy * b_y * b_sigmoid_g * (1 - b_sigmoid_g)
279
- b_dy = b_dy * b_sigmoid_g
280
- b_wdy = b_dy
281
-
282
- if HAS_WEIGHT or HAS_BIAS:
283
- # when BT > BS, a tile may span into the next program's range;
284
- # mask to this program's upper bound to avoid double-counting dw/db.
285
- m_t = (i_t + tl.arange(0, BT)) < min(i_s * BS + BS, T)
286
- if HAS_WEIGHT:
287
- b_wdy = b_dy * b_w
288
- b_dw += tl.where(m_t[:, None], b_dy * b_xhat, 0.0)
289
- if HAS_BIAS:
290
- b_db += tl.where(m_t[:, None], b_dy, 0.0)
291
- if not IS_RMS_NORM:
292
- b_c1 = tl.sum(b_xhat * b_wdy, axis=1) / D
293
- b_c2 = tl.sum(b_wdy, axis=1) / D
294
- b_dx = (b_wdy - (b_xhat * b_c1[:, None] + b_c2[:, None])) * b_rstd[:, None]
295
- else:
296
- b_c1 = tl.sum(b_xhat * b_wdy, axis=1) / D
297
- b_dx = (b_wdy - b_xhat * b_c1[:, None]) * b_rstd[:, None]
298
- if HAS_DRESIDUAL:
299
- p_dres = tl.make_block_ptr(dresidual, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
300
- b_dres = tl.load(p_dres, boundary_check=(0, 1)).to(tl.float32)
301
- b_dx += b_dres
302
- # Write dx
303
- if STORE_DRESIDUAL:
304
- p_dres_in = tl.make_block_ptr(dresidual_in, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
305
- tl.store(p_dres_in, b_dx.to(p_dres_in.dtype.element_ty), boundary_check=(0, 1))
306
-
307
- tl.store(p_dx, b_dx.to(p_dx.dtype.element_ty), boundary_check=(0, 1))
308
- tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), boundary_check=(0, 1))
309
-
310
- if HAS_WEIGHT:
311
- tl.store(dw + i_s * D + o_d, tl.sum(b_dw, axis=0), mask=m_d)
312
- if HAS_BIAS:
313
- tl.store(db + i_s * D + o_d, tl.sum(b_db, axis=0), mask=m_d)
314
-
315
-
316
- @triton.heuristics(
317
- {
318
- "HAS_DRESIDUAL": lambda args: args["dresidual"] is not None,
319
- "HAS_WEIGHT": lambda args: args["w"] is not None,
320
- "HAS_BIAS": lambda args: args["b"] is not None,
321
- "RECOMPUTE_OUTPUT": lambda args: args["y"] is not None,
322
- }
323
- )
324
- @triton.autotune(
325
- configs=[triton.Config({}, num_warps=num_warps) for num_warps in [2, 4, 8, 16]],
326
- key=["D", "IS_RMS_NORM", "STORE_DRESIDUAL", "HAS_DRESIDUAL", "HAS_WEIGHT"],
327
- **autotune_cache_kwargs,
328
- )
329
- @triton.jit
330
- def layer_norm_gated_bwd_kernel1(
331
- x, # pointer to the input
332
- g, # pointer to the gate
333
- w, # pointer to the weights
334
- b, # pointer to the biases
335
- y, # pointer to the output to be recomputed
336
- dy, # pointer to the output gradient
337
- dx, # pointer to the input gradient
338
- dg, # pointer to the gate gradient
339
- dw, # pointer to the partial sum of weights gradient
340
- db, # pointer to the partial sum of biases gradient
341
- dresidual,
342
- dresidual_in,
343
- mean,
344
- rstd,
345
- T,
346
- BS,
347
- D: tl.constexpr,
348
- BD: tl.constexpr,
349
- ACTIVATION: tl.constexpr,
350
- IS_RMS_NORM: tl.constexpr,
351
- STORE_DRESIDUAL: tl.constexpr,
352
- HAS_DRESIDUAL: tl.constexpr,
353
- HAS_WEIGHT: tl.constexpr,
354
- HAS_BIAS: tl.constexpr,
355
- RECOMPUTE_OUTPUT: tl.constexpr,
356
- ):
357
- i_s = tl.program_id(0)
358
- o_d = tl.arange(0, BD)
359
- mask = o_d < D
360
- x += i_s * BS * D
361
- g += i_s * BS * D
362
- if HAS_DRESIDUAL:
363
- dresidual += i_s * BS * D
364
- if STORE_DRESIDUAL:
365
- dresidual_in += i_s * BS * D
366
- dy += i_s * BS * D
367
- dx += i_s * BS * D
368
- dg += i_s * BS * D
369
- if RECOMPUTE_OUTPUT:
370
- y += i_s * BS * D
371
- if HAS_WEIGHT:
372
- b_w = tl.load(w + o_d, mask=mask).to(tl.float32)
373
- b_dw = tl.zeros((BD,), dtype=tl.float32)
374
- if HAS_BIAS:
375
- b_b = tl.load(b + o_d, mask=mask, other=0.0).to(tl.float32)
376
- b_db = tl.zeros((BD,), dtype=tl.float32)
377
-
378
- for i_t in range(i_s * BS, min(i_s * BS + BS, T)):
379
- # Load data to SRAM
380
- b_x = tl.load(x + o_d, mask=mask, other=0).to(tl.float32)
381
- b_g = tl.load(g + o_d, mask=mask, other=0).to(tl.float32)
382
- b_dy = tl.load(dy + o_d, mask=mask, other=0).to(tl.float32)
383
-
384
- if not IS_RMS_NORM:
385
- b_mean = tl.load(mean + i_t)
386
- b_rstd = tl.load(rstd + i_t)
387
- # Compute dx
388
- b_xhat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
389
- b_xhat = tl.where(mask, b_xhat, 0.0)
390
-
391
- b_y = b_xhat * b_w if HAS_WEIGHT else b_xhat
392
- if HAS_BIAS:
393
- b_y = b_y + b_b
394
- if RECOMPUTE_OUTPUT:
395
- tl.store(y + o_d, b_y, mask=mask)
396
-
397
- b_sigmoid_g = tl.sigmoid(b_g)
398
- if ACTIVATION == "swish" or ACTIVATION == "silu":
399
- b_dg = b_dy * b_y * (b_sigmoid_g + b_g * b_sigmoid_g * (1 - b_sigmoid_g))
400
- b_dy = b_dy * b_g * b_sigmoid_g
401
- elif ACTIVATION == "sigmoid":
402
- b_dg = b_dy * b_y * b_sigmoid_g * (1 - b_sigmoid_g)
403
- b_dy = b_dy * b_sigmoid_g
404
- b_wdy = b_dy
405
- if HAS_WEIGHT:
406
- b_wdy = b_dy * b_w
407
- b_dw += b_dy * b_xhat
408
- if HAS_BIAS:
409
- b_db += b_dy
410
- if not IS_RMS_NORM:
411
- b_c1 = tl.sum(b_xhat * b_wdy, axis=0) / D
412
- b_c2 = tl.sum(b_wdy, axis=0) / D
413
- b_dx = (b_wdy - (b_xhat * b_c1 + b_c2)) * b_rstd
414
- else:
415
- b_c1 = tl.sum(b_xhat * b_wdy, axis=0) / D
416
- b_dx = (b_wdy - b_xhat * b_c1) * b_rstd
417
- if HAS_DRESIDUAL:
418
- b_dres = tl.load(dresidual + o_d, mask=mask, other=0).to(tl.float32)
419
- b_dx += b_dres
420
- # Write dx
421
- if STORE_DRESIDUAL:
422
- tl.store(dresidual_in + o_d, b_dx, mask=mask)
423
- tl.store(dx + o_d, b_dx, mask=mask)
424
- tl.store(dg + o_d, b_dg, mask=mask)
425
-
426
- x += D
427
- g += D
428
- if HAS_DRESIDUAL:
429
- dresidual += D
430
- if STORE_DRESIDUAL:
431
- dresidual_in += D
432
- if RECOMPUTE_OUTPUT:
433
- y += D
434
- dy += D
435
- dx += D
436
- dg += D
437
- if HAS_WEIGHT:
438
- tl.store(dw + i_s * D + o_d, b_dw, mask=mask)
439
- if HAS_BIAS:
440
- tl.store(db + i_s * D + o_d, b_db, mask=mask)
441
-
442
-
443
- def layer_norm_gated_fwd(
444
- x: torch.Tensor,
445
- g: torch.Tensor,
446
- weight: torch.Tensor,
447
- bias: torch.Tensor,
448
- activation: str = "swish",
449
- eps: float = 1e-5,
450
- residual: torch.Tensor = None,
451
- out_dtype: torch.dtype = None,
452
- residual_dtype: torch.dtype = None,
453
- is_rms_norm: bool = False,
454
- ):
455
- if residual is not None:
456
- residual_dtype = residual.dtype
457
- T, D = x.shape
458
- if residual is not None:
459
- assert residual.shape == (T, D)
460
- if weight is not None:
461
- assert weight.shape == (D,)
462
- if bias is not None:
463
- assert bias.shape == (D,)
464
- # allocate output
465
- y = torch.empty_like(x, dtype=x.dtype if out_dtype is None else out_dtype)
466
- if residual is not None or (residual_dtype is not None and residual_dtype != x.dtype):
467
- residual_out = torch.empty(T, D, device=x.device, dtype=residual_dtype)
468
- else:
469
- residual_out = None
470
- mean = torch.empty((T,), dtype=torch.float, device=x.device) if not is_rms_norm else None
471
- rstd = torch.empty((T,), dtype=torch.float, device=x.device)
472
- # Less than 64KB per feature: enqueue fused kernel
473
- MAX_FUSED_SIZE = 65536 // x.element_size()
474
- BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
475
- if D > BD:
476
- raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
477
- # heuristics for number of warps
478
-
479
- if D <= 512:
480
- # NOTE(tylerr): Avoid excessive recompilation and autotuning by tolerating a larger range
481
- # of T before recompiling the kernel.
482
- # NB = triton.cdiv(T, 2048)
483
- NB = triton.cdiv(T, 2048 * 32)
484
-
485
- def grid(meta):
486
- return (triton.cdiv(T, meta["BT"]),)
487
-
488
- layer_norm_gated_fwd_kernel[grid](
489
- x=x,
490
- g=g,
491
- y=y,
492
- w=weight,
493
- b=bias,
494
- residual=residual,
495
- residual_out=residual_out,
496
- mean=mean,
497
- rstd=rstd,
498
- eps=eps,
499
- T=T,
500
- D=D,
501
- BD=BD,
502
- NB=NB,
503
- ACTIVATION=activation,
504
- IS_RMS_NORM=is_rms_norm,
505
- )
506
- else:
507
- layer_norm_gated_fwd_kernel1[(T,)](
508
- x=x,
509
- g=g,
510
- y=y,
511
- w=weight,
512
- b=bias,
513
- residual=residual,
514
- residual_out=residual_out,
515
- mean=mean,
516
- rstd=rstd,
517
- eps=eps,
518
- D=D,
519
- BD=BD,
520
- ACTIVATION=activation,
521
- IS_RMS_NORM=is_rms_norm,
522
- )
523
- # residual_out is None if residual is None and residual_dtype == input_dtype
524
- return y, mean, rstd, residual_out if residual_out is not None else x
525
-
526
-
527
- def layer_norm_gated_bwd(
528
- dy: torch.Tensor,
529
- x: torch.Tensor,
530
- g: torch.Tensor,
531
- weight: torch.Tensor,
532
- bias: torch.Tensor,
533
- activation: str = "swish",
534
- eps: float = 1e-5,
535
- mean: torch.Tensor = None,
536
- rstd: torch.Tensor = None,
537
- dresidual: torch.Tensor = None,
538
- has_residual: bool = False,
539
- is_rms_norm: bool = False,
540
- x_dtype: torch.dtype = None,
541
- recompute_output: bool = False,
542
- ):
543
- T, D = x.shape
544
- assert dy.shape == (T, D)
545
- if dresidual is not None:
546
- assert dresidual.shape == (T, D)
547
- if weight is not None:
548
- assert weight.shape == (D,)
549
- if bias is not None:
550
- assert bias.shape == (D,)
551
- # allocate output
552
- dx = torch.empty_like(x) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
553
- dg = torch.empty_like(g) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
554
- dresidual_in = torch.empty_like(x) if has_residual and dx.dtype != x.dtype else None
555
- y = torch.empty(T, D, dtype=dy.dtype, device=dy.device) if recompute_output else None
556
-
557
- # Less than 64KB per feature: enqueue fused kernel
558
- MAX_FUSED_SIZE = 65536 // x.element_size()
559
- BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
560
- if D > BD:
561
- raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
562
- # cap program count to T so no program is completely idle.
563
- # without this, high-SM GPUs (e.g. B200, 160 SMs) with small T would
564
- # launch idle programs whose make_block_ptr offsets exceed the tensor shape.
565
- NS = min(get_multiprocessor_count(x.device.index), T)
566
- BS = math.ceil(T / NS)
567
-
568
- dw = torch.empty((NS, D), dtype=torch.float, device=weight.device) if weight is not None else None
569
- db = torch.empty((NS, D), dtype=torch.float, device=bias.device) if bias is not None else None
570
- grid = (NS,)
571
-
572
- if D <= 512:
573
- # NOTE(tylerr): Avoid excessive recompilation and autotuning by tolerating a larger range
574
- # of T before recompiling the kernel.
575
- # NB = triton.cdiv(T, 2048)
576
- NB = triton.cdiv(T, 2048 * 32)
577
-
578
- layer_norm_gated_bwd_kernel[grid](
579
- x=x,
580
- g=g,
581
- w=weight,
582
- b=bias,
583
- y=y,
584
- dy=dy,
585
- dx=dx,
586
- dg=dg,
587
- dw=dw,
588
- db=db,
589
- dresidual=dresidual,
590
- dresidual_in=dresidual_in,
591
- mean=mean,
592
- rstd=rstd,
593
- T=T,
594
- D=D,
595
- BS=BS,
596
- BD=BD,
597
- NB=NB,
598
- ACTIVATION=activation,
599
- IS_RMS_NORM=is_rms_norm,
600
- STORE_DRESIDUAL=dresidual_in is not None,
601
- )
602
- else:
603
- layer_norm_gated_bwd_kernel1[grid](
604
- x=x,
605
- g=g,
606
- w=weight,
607
- b=bias,
608
- y=y,
609
- dy=dy,
610
- dx=dx,
611
- dg=dg,
612
- dw=dw,
613
- db=db,
614
- dresidual=dresidual,
615
- dresidual_in=dresidual_in,
616
- mean=mean,
617
- rstd=rstd,
618
- T=T,
619
- D=D,
620
- BS=BS,
621
- BD=BD,
622
- ACTIVATION=activation,
623
- IS_RMS_NORM=is_rms_norm,
624
- STORE_DRESIDUAL=dresidual_in is not None,
625
- )
626
- dw = dw.sum(0).to(weight.dtype) if weight is not None else None
627
- db = db.sum(0).to(bias.dtype) if bias is not None else None
628
- # Don't need to compute dresidual_in separately in this case
629
- if has_residual and dx.dtype == x.dtype:
630
- dresidual_in = dx
631
- return (dx, dg, dw, db, dresidual_in) if not recompute_output else (dx, dg, dw, db, dresidual_in, y)
632
-
633
-
634
- class LayerNormGatedFunction(torch.autograd.Function):
635
- @staticmethod
636
- @input_guard
637
- def forward(
638
- ctx,
639
- x: torch.Tensor,
640
- g: torch.Tensor,
641
- weight: torch.Tensor,
642
- bias: torch.Tensor,
643
- activation: str,
644
- residual: torch.Tensor | None = None,
645
- eps: float = 1e-6,
646
- prenorm: bool = False,
647
- residual_in_fp32: bool = False,
648
- is_rms_norm: bool = False,
649
- ):
650
- x_shape_og = x.shape
651
- g_shape_og = g.shape
652
- # reshape input data into 2D tensor
653
- x = x.reshape(-1, x.shape[-1])
654
- g = g.reshape(-1, g.shape[-1])
655
- if residual is not None:
656
- assert residual.shape == x_shape_og
657
- residual = residual.reshape(-1, residual.shape[-1])
658
- residual_dtype = residual.dtype if residual is not None else (torch.float if residual_in_fp32 else None)
659
- y, mean, rstd, residual_out = layer_norm_gated_fwd(
660
- x=x,
661
- g=g,
662
- weight=weight,
663
- bias=bias,
664
- activation=activation,
665
- eps=eps,
666
- residual=residual,
667
- residual_dtype=residual_dtype,
668
- is_rms_norm=is_rms_norm,
669
- )
670
- ctx.save_for_backward(residual_out, g, weight, bias, mean, rstd)
671
- ctx.x_shape_og = x_shape_og
672
- ctx.g_shape_og = g_shape_og
673
- ctx.activation = activation
674
- ctx.eps = eps
675
- ctx.is_rms_norm = is_rms_norm
676
- ctx.has_residual = residual is not None
677
- ctx.prenorm = prenorm
678
- ctx.x_dtype = x.dtype
679
- y = y.reshape(x_shape_og)
680
- return y if not prenorm else (y, residual_out.reshape(x_shape_og))
681
-
682
- @staticmethod
683
- @input_guard
684
- def backward(ctx, dy, *args):
685
- x, g, weight, bias, mean, rstd = ctx.saved_tensors
686
- dy = dy.reshape(-1, dy.shape[-1])
687
- assert dy.shape == x.shape
688
- if ctx.prenorm:
689
- dresidual = args[0]
690
- dresidual = dresidual.reshape(-1, dresidual.shape[-1])
691
- assert dresidual.shape == x.shape
692
- else:
693
- dresidual = None
694
- dx, dg, dw, db, dres_in = layer_norm_gated_bwd(
695
- dy=dy,
696
- x=x,
697
- g=g,
698
- weight=weight,
699
- bias=bias,
700
- activation=ctx.activation,
701
- eps=ctx.eps,
702
- mean=mean,
703
- rstd=rstd,
704
- dresidual=dresidual,
705
- has_residual=ctx.has_residual,
706
- is_rms_norm=ctx.is_rms_norm,
707
- x_dtype=ctx.x_dtype,
708
- )
709
- return (
710
- dx.reshape(ctx.x_shape_og),
711
- dg.reshape(ctx.g_shape_og),
712
- dw,
713
- db,
714
- None,
715
- dres_in.reshape(ctx.x_shape_og) if ctx.has_residual else None,
716
- None,
717
- None,
718
- None,
719
- None,
720
- )
721
-
722
-
723
- class LayerNormGatedLinearFunction(torch.autograd.Function):
724
- @staticmethod
725
- @input_guard
726
- def forward(
727
- ctx,
728
- x: torch.Tensor,
729
- g: torch.Tensor,
730
- norm_weight: torch.Tensor,
731
- norm_bias: torch.Tensor,
732
- linear_weight: torch.Tensor,
733
- linear_bias: torch.Tensor,
734
- residual: torch.Tensor | None = None,
735
- eps: float = 1e-6,
736
- prenorm: bool = False,
737
- residual_in_fp32: bool = False,
738
- is_rms_norm: bool = False,
739
- ):
740
- x_shape_og = x.shape
741
- g_shape_og = g.shape
742
- # reshape input data into 2D tensor
743
- x = x.reshape(-1, x.shape[-1])
744
- g = g.reshape(-1, g.shape[-1])
745
- if residual is not None:
746
- assert residual.shape == x_shape_og
747
- residual = residual.reshape(-1, residual.shape[-1])
748
- residual_dtype = residual.dtype if residual is not None else (torch.float if residual_in_fp32 else None)
749
- y, mean, rstd, residual_out = layer_norm_gated_fwd(
750
- x=x,
751
- g=g,
752
- weight=norm_weight,
753
- bias=norm_bias,
754
- eps=eps,
755
- residual=residual,
756
- residual_dtype=residual_dtype,
757
- is_rms_norm=is_rms_norm,
758
- )
759
- y = y.reshape(x_shape_og)
760
- dtype = torch.get_autocast_gpu_dtype() if torch.is_autocast_enabled() else y.dtype
761
- linear_weight = linear_weight.to(dtype)
762
- linear_bias = linear_bias.to(dtype) if linear_bias is not None else None
763
- out = F.linear(y.to(linear_weight.dtype), linear_weight, linear_bias)
764
- # We don't store y, will be recomputed in the backward pass to save memory
765
- ctx.save_for_backward(residual_out, g, norm_weight, norm_bias, linear_weight, mean, rstd)
766
- ctx.x_shape_og = x_shape_og
767
- ctx.g_shape_og = g_shape_og
768
- ctx.eps = eps
769
- ctx.is_rms_norm = is_rms_norm
770
- ctx.has_residual = residual is not None
771
- ctx.prenorm = prenorm
772
- ctx.x_dtype = x.dtype
773
- ctx.linear_bias_is_none = linear_bias is None
774
- return out if not prenorm else (out, residual_out.reshape(x_shape_og))
775
-
776
- @staticmethod
777
- @input_guard
778
- def backward(ctx, dout, *args):
779
- x, g, norm_weight, norm_bias, linear_weight, mean, rstd = ctx.saved_tensors
780
- dout = dout.reshape(-1, dout.shape[-1])
781
- dy = F.linear(dout, linear_weight.t())
782
- dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
783
- assert dy.shape == x.shape
784
- if ctx.prenorm:
785
- dresidual = args[0]
786
- dresidual = dresidual.reshape(-1, dresidual.shape[-1])
787
- assert dresidual.shape == x.shape
788
- else:
789
- dresidual = None
790
- dx, dg, dnorm_weight, dnorm_bias, dres_in, y = layer_norm_gated_bwd(
791
- dy=dy,
792
- x=x,
793
- g=g,
794
- weight=norm_weight,
795
- bias=norm_bias,
796
- eps=ctx.eps,
797
- mean=mean,
798
- rstd=rstd,
799
- dresidual=dresidual,
800
- has_residual=ctx.has_residual,
801
- is_rms_norm=ctx.is_rms_norm,
802
- x_dtype=ctx.x_dtype,
803
- recompute_output=True,
804
- )
805
- dlinear_weight = torch.einsum("bo,bi->oi", dout, y)
806
- return (
807
- dx.reshape(ctx.x_shape_og),
808
- dg.reshape(ctx.g_shape_og),
809
- dnorm_weight,
810
- dnorm_bias,
811
- dlinear_weight,
812
- dlinear_bias,
813
- dres_in.reshape(ctx.x_shape_og) if ctx.has_residual else None,
814
- None,
815
- None,
816
- None,
817
- None,
818
- )
819
-
820
-
821
- def layer_norm_gated(
822
- x: torch.Tensor,
823
- g: torch.Tensor,
824
- weight: torch.Tensor,
825
- bias: torch.Tensor,
826
- activation: str = "swish",
827
- residual: torch.Tensor | None = None,
828
- prenorm: bool = False,
829
- residual_in_fp32: bool = False,
830
- eps: float = 1e-6,
831
- ):
832
- return LayerNormGatedFunction.apply(
833
- x,
834
- g,
835
- weight,
836
- bias,
837
- activation,
838
- residual,
839
- eps,
840
- prenorm,
841
- residual_in_fp32,
842
- False,
843
- )
844
-
845
-
846
- def rms_norm_gated(
847
- x: torch.Tensor,
848
- g: torch.Tensor,
849
- weight: torch.Tensor,
850
- bias: torch.Tensor,
851
- activation: str = "swish",
852
- residual: torch.Tensor | None = None,
853
- prenorm: bool = False,
854
- residual_in_fp32: bool = False,
855
- eps: float = 1e-6,
856
- ):
857
- return LayerNormGatedFunction.apply(
858
- x,
859
- g,
860
- weight,
861
- bias,
862
- activation,
863
- residual,
864
- eps,
865
- prenorm,
866
- residual_in_fp32,
867
- True,
868
- )
869
-
870
-
871
- def layer_norm_swish_gate_linear(
872
- x: torch.Tensor,
873
- g: torch.Tensor,
874
- norm_weight: torch.Tensor,
875
- norm_bias: torch.Tensor,
876
- linear_weight: torch.Tensor,
877
- linear_bias: torch.Tensor,
878
- residual: torch.Tensor | None = None,
879
- prenorm: bool = False,
880
- residual_in_fp32: bool = False,
881
- eps: float = 1e-6,
882
- ):
883
- return LayerNormGatedLinearFunction.apply(
884
- x,
885
- g,
886
- norm_weight,
887
- norm_bias,
888
- linear_weight,
889
- linear_bias,
890
- residual,
891
- eps,
892
- prenorm,
893
- residual_in_fp32,
894
- False,
895
- )
896
-
897
-
898
- def rms_norm_swish_gate_linear(
899
- x,
900
- g: torch.Tensor,
901
- norm_weight: torch.Tensor,
902
- norm_bias: torch.Tensor,
903
- linear_weight: torch.Tensor,
904
- linear_bias: torch.Tensor,
905
- residual: torch.Tensor | None = None,
906
- prenorm: bool = False,
907
- residual_in_fp32: bool = False,
908
- eps: float = 1e-6,
909
- ):
910
- return LayerNormGatedLinearFunction.apply(
911
- x,
912
- g,
913
- norm_weight,
914
- norm_bias,
915
- linear_weight,
916
- linear_bias,
917
- residual,
918
- eps,
919
- prenorm,
920
- residual_in_fp32,
921
- True,
922
- )
923
-
924
-
925
- class FusedLayerNormGated(nn.Module):
926
- def __init__(
927
- self,
928
- hidden_size: int,
929
- elementwise_affine: bool = True,
930
- bias: bool = False,
931
- activation: str = "swish",
932
- eps: float = 1e-5,
933
- device: torch.device | None = None,
934
- dtype: torch.dtype | None = None,
935
- ) -> FusedLayerNormGated:
936
- factory_kwargs = {"device": device, "dtype": dtype}
937
- super().__init__()
938
-
939
- self.hidden_size = hidden_size
940
- self.elementwise_affine = elementwise_affine
941
- self.eps = eps
942
- self.activation = activation
943
-
944
- if self.activation not in ["swish", "silu", "sigmoid"]:
945
- raise ValueError(f"Unsupported activation: {self.activation}")
946
-
947
- self.register_parameter("weight", None)
948
- self.register_parameter("bias", None)
949
- if elementwise_affine:
950
- self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
951
- if bias:
952
- self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
953
-
954
- self.reset_parameters()
955
-
956
- def reset_parameters(self):
957
- if self.elementwise_affine:
958
- nn.init.ones_(self.weight)
959
- if self.bias is not None:
960
- nn.init.zeros_(self.bias)
961
-
962
- def __repr__(self) -> str:
963
- s = f"{self.__class__.__name__}({self.hidden_size}"
964
- if not self.elementwise_affine:
965
- s += f", elementwise_affine={self.elementwise_affine}"
966
- s += f", eps={self.eps}"
967
- s += f", activation={self.activation}"
968
- s += ")"
969
- return s
970
-
971
- def forward(
972
- self,
973
- x: torch.Tensor,
974
- g: torch.Tensor,
975
- residual: torch.Tensor | None = None,
976
- prenorm: bool = False,
977
- residual_in_fp32: bool = False,
978
- ) -> torch.Tensor:
979
- return layer_norm_gated(
980
- x,
981
- g,
982
- self.weight,
983
- self.bias,
984
- self.activation,
985
- residual=residual,
986
- eps=self.eps,
987
- prenorm=prenorm,
988
- residual_in_fp32=residual_in_fp32,
989
- )
990
-
991
-
992
- class FusedRMSNormGated(nn.Module):
993
- def __init__(
994
- self,
995
- hidden_size: int,
996
- elementwise_affine: bool = True,
997
- eps: float = 1e-5,
998
- activation: str = "swish",
999
- device: torch.device | None = None,
1000
- dtype: torch.dtype | None = None,
1001
- ) -> FusedRMSNormGated:
1002
- factory_kwargs = {"device": device, "dtype": dtype}
1003
- super().__init__()
1004
-
1005
- self.hidden_size = hidden_size
1006
- self.elementwise_affine = elementwise_affine
1007
- self.eps = eps
1008
- self.activation = activation
1009
-
1010
- if self.activation not in ["swish", "silu", "sigmoid"]:
1011
- raise ValueError(f"Unsupported activation: {self.activation}")
1012
-
1013
- if elementwise_affine:
1014
- self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1015
- else:
1016
- self.register_parameter("weight", None)
1017
- self.register_parameter("bias", None)
1018
-
1019
- self.reset_parameters()
1020
-
1021
- def reset_parameters(self):
1022
- if self.elementwise_affine:
1023
- nn.init.ones_(self.weight)
1024
-
1025
- def __repr__(self) -> str:
1026
- s = f"{self.__class__.__name__}({self.hidden_size}"
1027
- if not self.elementwise_affine:
1028
- s += f", elementwise_affine={self.elementwise_affine}"
1029
- s += f", eps={self.eps}"
1030
- s += f", activation={self.activation}"
1031
- s += ")"
1032
- return s
1033
-
1034
- def forward(
1035
- self,
1036
- x: torch.Tensor,
1037
- g: torch.Tensor,
1038
- residual: torch.Tensor | None = None,
1039
- prenorm: bool = False,
1040
- residual_in_fp32: bool = False,
1041
- ) -> torch.Tensor:
1042
- return rms_norm_gated(
1043
- x,
1044
- g,
1045
- self.weight,
1046
- self.bias,
1047
- self.activation,
1048
- residual=residual,
1049
- eps=self.eps,
1050
- prenorm=prenorm,
1051
- residual_in_fp32=residual_in_fp32,
1052
- )
1053
-
1054
-
1055
- class FusedLayerNormSwishGate(FusedLayerNormGated):
1056
- def __init__(
1057
- self,
1058
- hidden_size: int,
1059
- elementwise_affine: bool = True,
1060
- bias: bool = False,
1061
- eps: float = 1e-5,
1062
- device: torch.device | None = None,
1063
- dtype: torch.dtype | None = None,
1064
- ) -> FusedLayerNormSwishGate:
1065
- super().__init__(
1066
- hidden_size=hidden_size,
1067
- elementwise_affine=elementwise_affine,
1068
- bias=bias,
1069
- eps=eps,
1070
- device=device,
1071
- dtype=dtype,
1072
- )
1073
-
1074
-
1075
- class FusedRMSNormSwishGate(FusedRMSNormGated):
1076
- def __init__(
1077
- self,
1078
- hidden_size: int,
1079
- elementwise_affine: bool = True,
1080
- eps: float = 1e-5,
1081
- device: torch.device | None = None,
1082
- dtype: torch.dtype | None = None,
1083
- ) -> FusedRMSNormSwishGate:
1084
- super().__init__(
1085
- hidden_size=hidden_size,
1086
- elementwise_affine=elementwise_affine,
1087
- eps=eps,
1088
- device=device,
1089
- dtype=dtype,
1090
- )
1091
-
1092
-
1093
- class FusedLayerNormGatedLinear(nn.Module):
1094
- def __init__(
1095
- self,
1096
- hidden_size: int,
1097
- elementwise_affine: bool = True,
1098
- eps: float = 1e-5,
1099
- device: torch.device | None = None,
1100
- dtype: torch.dtype | None = None,
1101
- ) -> FusedLayerNormGatedLinear:
1102
- factory_kwargs = {"device": device, "dtype": dtype}
1103
- super().__init__()
1104
-
1105
- self.hidden_size = hidden_size
1106
- self.elementwise_affine = elementwise_affine
1107
- self.eps = eps
1108
-
1109
- if elementwise_affine:
1110
- self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1111
- else:
1112
- self.register_parameter("weight", None)
1113
- self.register_parameter("bias", None)
1114
-
1115
- self.reset_parameters()
1116
-
1117
- def reset_parameters(self):
1118
- if self.elementwise_affine:
1119
- nn.init.ones_(self.weight)
1120
-
1121
- def __repr__(self) -> str:
1122
- s = f"{self.__class__.__name__}({self.hidden_size}"
1123
- if not self.elementwise_affine:
1124
- s += f", elementwise_affine={self.elementwise_affine}"
1125
- s += f", eps={self.eps}"
1126
- s += ")"
1127
- return s
1128
-
1129
- def forward(
1130
- self,
1131
- x: torch.Tensor,
1132
- g: torch.Tensor,
1133
- weight: torch.Tensor | None = None,
1134
- bias: torch.Tensor | None = None,
1135
- residual: torch.Tensor | None = None,
1136
- prenorm: bool = False,
1137
- residual_in_fp32: bool = False,
1138
- ) -> torch.Tensor:
1139
- return layer_norm_swish_gate_linear(
1140
- x,
1141
- g,
1142
- self.weight,
1143
- self.bias,
1144
- weight,
1145
- bias,
1146
- residual=residual,
1147
- eps=self.eps,
1148
- prenorm=prenorm,
1149
- residual_in_fp32=residual_in_fp32,
1150
- )
1151
-
1152
-
1153
- class FusedLayerNormSwishGateLinear(FusedLayerNormGatedLinear):
1154
- def __init__(
1155
- self,
1156
- hidden_size: int,
1157
- elementwise_affine: bool = True,
1158
- eps: float = 1e-5,
1159
- device: torch.device | None = None,
1160
- dtype: torch.dtype | None = None,
1161
- ) -> FusedLayerNormSwishGateLinear:
1162
- super().__init__(
1163
- hidden_size=hidden_size,
1164
- elementwise_affine=elementwise_affine,
1165
- eps=eps,
1166
- device=device,
1167
- dtype=dtype,
1168
- )
1169
-
1170
-
1171
- class FusedRMSNormGatedLinear(nn.Module):
1172
- def __init__(
1173
- self,
1174
- hidden_size,
1175
- elementwise_affine: bool = True,
1176
- eps: float = 1e-5,
1177
- device: torch.device | None = None,
1178
- dtype: torch.dtype | None = None,
1179
- ) -> FusedRMSNormGatedLinear:
1180
- factory_kwargs = {"device": device, "dtype": dtype}
1181
- super().__init__()
1182
-
1183
- self.hidden_size = hidden_size
1184
- self.elementwise_affine = elementwise_affine
1185
- self.eps = eps
1186
-
1187
- self.register_parameter("weight", None)
1188
- self.register_parameter("bias", None)
1189
- if elementwise_affine:
1190
- self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1191
-
1192
- self.reset_parameters()
1193
-
1194
- def reset_parameters(self):
1195
- if self.elementwise_affine:
1196
- nn.init.ones_(self.weight)
1197
-
1198
- def __repr__(self) -> str:
1199
- s = f"{self.__class__.__name__}({self.hidden_size}"
1200
- if not self.elementwise_affine:
1201
- s += f", elementwise_affine={self.elementwise_affine}"
1202
- s += f", eps={self.eps}"
1203
- s += ")"
1204
- return s
1205
-
1206
- def forward(
1207
- self,
1208
- x: torch.Tensor,
1209
- g: torch.Tensor,
1210
- weight: torch.Tensor | None = None,
1211
- bias: torch.Tensor | None = None,
1212
- residual: torch.Tensor | None = None,
1213
- prenorm: bool = False,
1214
- residual_in_fp32: bool = False,
1215
- ) -> torch.Tensor:
1216
- return rms_norm_swish_gate_linear(
1217
- x,
1218
- g,
1219
- self.weight,
1220
- self.bias,
1221
- weight,
1222
- bias,
1223
- residual=residual,
1224
- eps=self.eps,
1225
- prenorm=prenorm,
1226
- residual_in_fp32=residual_in_fp32,
1227
- )
1228
-
1229
-
1230
- class FusedRMSNormSwishGateLinear(FusedRMSNormGatedLinear):
1231
- def __init__(
1232
- self,
1233
- hidden_size: int,
1234
- elementwise_affine: bool = True,
1235
- eps: float = 1e-5,
1236
- device: torch.device | None = None,
1237
- dtype: torch.dtype | None = None,
1238
- ) -> FusedRMSNormSwishGateLinear:
1239
- super().__init__(
1240
- hidden_size=hidden_size,
1241
- elementwise_affine=elementwise_affine,
1242
- eps=eps,
1243
- device=device,
1244
- dtype=dtype,
1245
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/{torch-xpu/modules/conv/cp → torch-cuda/modules/grpo}/__init__.py RENAMED
@@ -5,9 +5,7 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- from .ops import CausalConv1dFunctionCP, causal_conv1d_cp
 
 
9
 
10
- __all__ = [
11
- 'CausalConv1dFunctionCP',
12
- 'causal_conv1d_cp',
13
- ]
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ from ...modules.grpo.ops import fused_grpo_loss, grpo_loss_with_old_logps
9
+
10
+ __all__ = ['fused_grpo_loss', 'grpo_loss_with_old_logps']
11
 
 
 
 
 
build/torch-cuda/modules/{grpo.py → grpo/ops.py} RENAMED
@@ -6,65 +6,20 @@
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
  # modified from https://github.com/mdy666/mdy_triton/blob/e0a856347bd988e05e0152332bba35f1d33c5b1f/others/grpo/grpo_loss.ipynb
9
- # XHS ID: blueeeee
10
-
11
- # https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py
12
- """
13
- # Get the per-token log probabilities for the completions for the model and the reference model
14
- def _get_per_token_logps(self, model, input_ids, attention_mask, logits_to_keep):
15
- # We add 1 to `logits_to_keep` because the last logits of the sequence is later excluded
16
- logits = model(input_ids=input_ids, attention_mask=attention_mask, logits_to_keep=logits_to_keep + 1).logits
17
- logits = logits[:, :-1, :] # (B, L-1, V), exclude the last logit: it corresponds to the next token pred
18
-
19
- input_ids = input_ids[:, -logits_to_keep:]
20
- # For transformers<=4.48, logits_to_keep argument isn't supported, so here we drop logits ourselves.
21
- # See https://github.com/huggingface/trl/issues/2770
22
- logits = logits[:, -logits_to_keep:]
23
- return selective_log_softmax(logits, input_ids) # compute logprobs for the input tokens
24
-
25
- def compute_loss(self, model, inputs, return_outputs=False, num_items_in_batch=None):
26
- if return_outputs:
27
- raise ValueError("The GRPOTrainer does not support returning outputs")
28
- # Compute the per-token log probabilities for the model
29
-
30
- prompt_ids, prompt_mask = inputs["prompt_ids"], inputs["prompt_mask"]
31
- completion_ids, completion_mask = inputs["completion_ids"], inputs["completion_mask"]
32
- input_ids = torch.cat([prompt_ids, completion_ids], dim=1)
33
- attention_mask = torch.cat([prompt_mask, completion_mask], dim=1)
34
- logits_to_keep = completion_ids.size(1) # we only need to compute the logits for the completion tokens
35
-
36
- per_token_logps = self._get_per_token_logps(model, input_ids, attention_mask, logits_to_keep)
37
-
38
- # Compute the KL divergence between the model and the reference model
39
- ref_per_token_logps = inputs["ref_per_token_logps"]
40
- per_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
41
-
42
- # x - x.detach() allows for preserving gradients from x
43
- advantages = inputs["advantages"]
44
- per_token_loss = torch.exp(per_token_logps - per_token_logps.detach()) * advantages.unsqueeze(1)
45
- per_token_loss = -(per_token_loss - self.beta * per_token_kl)
46
- loss = ((per_token_loss * completion_mask).sum(dim=1) / completion_mask.sum(dim=1)).mean()
47
-
48
- # Log the metrics
49
- completion_length = self.accelerator.gather_for_metrics(completion_mask.sum(1)).float().mean().item()
50
- self._metrics["completion_length"].append(completion_length)
51
-
52
- mean_kl = ((per_token_kl * completion_mask).sum(dim=1) / completion_mask.sum(dim=1)).mean()
53
- self._metrics["kl"].append(self.accelerator.gather_for_metrics(mean_kl).mean().item())
54
-
55
- return loss
56
- """
57
 
58
 
59
  import torch
60
  import triton
61
  import triton.language as tl
62
 
63
- from ..modules.backends import dispatch
64
- from ..ops.utils.op import exp, log
65
- from ..utils import IS_AMD, autotune_cache_kwargs, input_guard
66
 
67
  NUM_WARPS_AUTOTUNE = [4, 8, 16] if IS_AMD else [4, 8, 16, 32]
 
 
68
 
69
 
70
  @triton.autotune(
@@ -72,7 +27,7 @@ NUM_WARPS_AUTOTUNE = [4, 8, 16] if IS_AMD else [4, 8, 16, 32]
72
  triton.Config({'BLOCK_SIZE': BLOCK_SIZE}, num_warps=NUM_WARPS, num_stages=NUM_STAGES)
73
  for BLOCK_SIZE in [1024, 2048, 4096, 8192]
74
  for NUM_WARPS in NUM_WARPS_AUTOTUNE
75
- for NUM_STAGES in [1, 2, 4]
76
  ],
77
  key=['B', 'N'],
78
  **autotune_cache_kwargs,
@@ -198,8 +153,7 @@ def grpo_bwd_kernel(
198
  tl.debug_barrier()
199
  logp = x - lse
200
 
201
- dlogp = (beta * (-1.0 * exp(ref_logp - logp) + 1)
202
- - advantage) * dloss
203
 
204
  for start_n in tl.range(0, N, BLOCK_SIZE):
205
  cols = start_n + base_cols
@@ -251,7 +205,10 @@ class GrpoLoss(torch.autograd.Function):
251
  lse_ptr=lse,
252
  beta=beta,
253
  save_kl=save_kl,
254
- B=B, M=M, N=N, L=L,
 
 
 
255
  start_idx=input_ids_start_index,
256
  )
257
  ctx.beta = beta
@@ -263,7 +220,6 @@ class GrpoLoss(torch.autograd.Function):
263
  @input_guard
264
  @staticmethod
265
  def backward(ctx, dloss):
266
- # The grad of logits comes from two parts, the reward part and the kl part
267
  lse, logits, input_ids, advantages, completion_mask = ctx.saved_tensors
268
  inplace = ctx.inplace
269
  B, L_ADD_1, N = ctx.input_shape
@@ -286,46 +242,52 @@ class GrpoLoss(torch.autograd.Function):
286
  completion_mask_ptr=completion_mask,
287
  lse_ptr=lse,
288
  beta=ctx.beta,
289
- B=B, N=N, L=L,
290
- BLOCK_SIZE=BN,
 
291
  start_idx=input_ids_start_index,
 
292
  )
293
- # The last token in the completion is not used in the loss computation
294
- # and therefore its gradient should be set to 0
295
  dlogits[:, -1, :].fill_(0.0)
296
  return dlogits.view(*ctx.input_shape), None, None, None, None, None, None, None
297
 
298
 
299
- @dispatch('modules')
300
- def fused_grpo_loss(logits, ref_logp, input_ids, advantages,
301
- beta=0.1, completion_mask=None, save_kl=False, inplace=False) -> torch.Tensor:
302
- '''
303
- compute grpo loss, save memory(no addition usage) and fast speed(6X for A800)
 
 
 
 
 
 
 
304
 
305
  Args:
306
- logtits: Tensor, [B, L+1, vocab_size], the origin output of model, it's not logits[:, :-1]
307
- ref_logp: Tensor, [B, L], the origin output of model, it's not ref_logits[:, :-1]
308
- input_ids: Tensor, [B, K+L], it's prompt_completion_id, it contains the prompt ids and output ids
309
- advantages: Tensor, [B], the advantages of each prompt
310
- beta: float, the weight of kl loss
311
- completion_mask: Tensor, loss mask
312
- save_kl: bool, if true will save kl
313
-
314
- Retutn:
315
- loss: Tensor, [B, L], the loss of grpo, it contains the advantage part and kl part
316
-
317
- NOTE: logits(ref_logits) is computed by these steps
318
- logits_to_keep = completion_ids.size(1)
319
-
320
- def get_per_token_logits(model, input_ids, attention_mask, logits_to_keep):
321
- # We add 1 to `logits_to_keep` because the last logits of the sequence is later excluded
322
- logits = model(
323
- input_ids=input_ids, attention_mask=attention_mask, logits_to_keep=logits_to_keep + 1
324
- ).logits
325
- return logits
326
-
327
- logits = get_per_token_logits(model, prompt_completion_ids, attention_mask, logits_to_keep)
328
- '''
329
  out = GrpoLoss.apply(logits, ref_logp, input_ids, advantages, beta, completion_mask, save_kl, inplace)
330
  if not save_kl:
331
  return out
@@ -356,7 +318,8 @@ def grpo_loss_torch(logits, ref_logp, input_ids, advantages, beta=0.1, completio
356
  return per_token_loss if not save_kl else (per_token_loss, per_token_kl)
357
 
358
 
359
- @torch.compile(fullgraph=True)
 
360
  def grpo_loss_with_old_logps(
361
  logps: torch.Tensor,
362
  ref_logps: torch.Tensor,
@@ -367,55 +330,52 @@ def grpo_loss_with_old_logps(
367
  beta: float = 0.2,
368
  epsilon: float = 0.2,
369
  ):
370
- """
371
- Compute the GRPO (Group Relative Policy Optimization) loss.
372
 
373
  Args:
374
- logps (torch.Tensor): [Batch, Token_length] Log probabilities of the current policy.
375
- ref_logps (torch.Tensor):[Batch, Token_length] Log probabilities of the reference policy.
376
- old_logps (torch.Tensor): [Batch, Token_length] Log probabilities of the old policy.
377
- completion_ids (torch.Tensor): [Batch, Token_length] Completion token IDs (bool).
378
- pad_token_id: Pad token ID.
379
- logits_to_keep (int): Number of logits to keep for masking.
380
- rewards (torch.Tensor): [Batch] Rewards for each generation.
381
- beta (float) = 0.2: A hyperparameter for weighting the KL divergence term.
382
- epsilon (float) = 0.2: An float hyperparameter for clipping the importance weights.
 
 
 
 
 
 
 
383
 
384
  Returns:
385
- torch.Tensor: The computed GRPO loss.
 
386
  """
387
  B = logps.shape[0]
388
  assert B > 1, "Batch * Num generations should be greater than 1"
389
 
390
  rewards_shaped = rewards.view(-1, B) # B,num_generations
391
- advantages = (rewards_shaped - rewards_shaped.mean(dim=1, keepdim=True)) / \
392
- (rewards_shaped.std(dim=1, keepdim=True) + 1e-8)
393
  advantages = advantages.view(-1) # B*num_generations
394
- # Calculate the per - token KL divergence
395
  per_token_kl = torch.exp(ref_logps - logps) - (ref_logps - logps) - 1
396
 
397
- # Calculate the ratio of probabilities (importance weights)
398
- # Importance weights are calculated as exp(log_pi_theta - log_pi_theta_old)
399
  importance_weights = torch.exp(logps - old_logps)
400
 
401
- # Clip the importance weights to the range [1 - epsilon, 1 + epsilon]
402
  importance_weights_clipped = torch.clamp(importance_weights, 1 - epsilon, 1 + epsilon)
403
 
404
- # Create a completion mask. It checks which positions are valid based on logits_to_keep
405
  completion_mask = torch.arange(logits_to_keep, device=logps.device)[None, :] >= 0
406
 
407
- # Combine the completion mask and padding mask
408
- completion_mask = completion_mask & pad_mask # Ensure matching shape
409
 
410
- # Add an extra dimension to advantages to match the shape for element - wise multiplication
411
  advantages = advantages.unsqueeze(1)
412
 
413
- # Calculate the per - token loss. It takes the minimum of the unclipped and clipped importance weights
414
- # and subtracts the KL divergence term weighted by beta, then multiplies by the completion mask
415
- token_loss = -(torch.min(advantages * importance_weights, advantages *
416
- importance_weights_clipped) - beta * per_token_kl) * completion_mask
417
 
418
- # Calculate the final loss by summing the token losses and normalizing by the number of valid tokens
419
- loss = -token_loss.sum() / completion_mask.sum()
420
 
421
  return loss
 
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
  # modified from https://github.com/mdy666/mdy_triton/blob/e0a856347bd988e05e0152332bba35f1d33c5b1f/others/grpo/grpo_loss.ipynb
9
+ # loss reference: https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
 
12
  import torch
13
  import triton
14
  import triton.language as tl
15
 
16
+ from ...backends import dispatch
17
+ from ...ops.utils.op import exp, log
18
+ from ...utils import IS_AMD, IS_INTEL, IS_NPU, autotune_cache_kwargs, input_guard
19
 
20
  NUM_WARPS_AUTOTUNE = [4, 8, 16] if IS_AMD else [4, 8, 16, 32]
21
+ # single-stage pipelining avoids Intel/XPU correctness failures with cold memory.
22
+ NUM_STAGES_AUTOTUNE = [1] if IS_INTEL else [1, 2, 4]
23
 
24
 
25
  @triton.autotune(
 
27
  triton.Config({'BLOCK_SIZE': BLOCK_SIZE}, num_warps=NUM_WARPS, num_stages=NUM_STAGES)
28
  for BLOCK_SIZE in [1024, 2048, 4096, 8192]
29
  for NUM_WARPS in NUM_WARPS_AUTOTUNE
30
+ for NUM_STAGES in NUM_STAGES_AUTOTUNE
31
  ],
32
  key=['B', 'N'],
33
  **autotune_cache_kwargs,
 
153
  tl.debug_barrier()
154
  logp = x - lse
155
 
156
+ dlogp = (beta * (-1.0 * exp(ref_logp - logp) + 1) - advantage) * dloss
 
157
 
158
  for start_n in tl.range(0, N, BLOCK_SIZE):
159
  cols = start_n + base_cols
 
205
  lse_ptr=lse,
206
  beta=beta,
207
  save_kl=save_kl,
208
+ B=B,
209
+ M=M,
210
+ N=N,
211
+ L=L,
212
  start_idx=input_ids_start_index,
213
  )
214
  ctx.beta = beta
 
220
  @input_guard
221
  @staticmethod
222
  def backward(ctx, dloss):
 
223
  lse, logits, input_ids, advantages, completion_mask = ctx.saved_tensors
224
  inplace = ctx.inplace
225
  B, L_ADD_1, N = ctx.input_shape
 
242
  completion_mask_ptr=completion_mask,
243
  lse_ptr=lse,
244
  beta=ctx.beta,
245
+ B=B,
246
+ N=N,
247
+ L=L,
248
  start_idx=input_ids_start_index,
249
+ BLOCK_SIZE=BN,
250
  )
251
+ # the final token has no loss contribution, so its gradient is zero.
 
252
  dlogits[:, -1, :].fill_(0.0)
253
  return dlogits.view(*ctx.input_shape), None, None, None, None, None, None, None
254
 
255
 
256
+ @dispatch
257
+ def fused_grpo_loss(
258
+ logits,
259
+ ref_logp,
260
+ input_ids,
261
+ advantages,
262
+ beta=0.1,
263
+ completion_mask=None,
264
+ save_kl=False,
265
+ inplace=False,
266
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
267
+ """Compute per-token GRPO loss from completion logits.
268
 
269
  Args:
270
+ logits (torch.Tensor):
271
+ Model logits of shape `[B, L + 1, V]`; the final token is excluded from the loss.
272
+ ref_logp (torch.Tensor):
273
+ Reference model log probabilities of shape `[B, L]` for the sampled completion tokens.
274
+ input_ids (torch.Tensor):
275
+ Prompt and completion token IDs of shape `[B, K + L]`.
276
+ advantages (torch.Tensor):
277
+ Per-sequence advantages of shape `[B]`.
278
+ beta (float, Optional):
279
+ KL penalty coefficient. Default: 0.1.
280
+ completion_mask (torch.Tensor, Optional):
281
+ Mask of shape `[B, L]`; masked tokens have zero loss and gradient. Default: `None`.
282
+ save_kl (bool, Optional):
283
+ Whether to also return the per-token KL penalty. Default: `False`.
284
+ inplace (bool, Optional):
285
+ Whether backward may overwrite logits with gradients. Default: `False`.
286
+
287
+ Returns:
288
+ torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
289
+ Per-token loss of shape `[B, L]`, with a separate KL tensor when `save_kl=True`.
290
+ """
 
 
291
  out = GrpoLoss.apply(logits, ref_logp, input_ids, advantages, beta, completion_mask, save_kl, inplace)
292
  if not save_kl:
293
  return out
 
318
  return per_token_loss if not save_kl else (per_token_loss, per_token_kl)
319
 
320
 
321
+ # keep this path eager on NPU to avoid an Inductor miscompile.
322
+ @torch.compile(fullgraph=True, disable=IS_NPU)
323
  def grpo_loss_with_old_logps(
324
  logps: torch.Tensor,
325
  ref_logps: torch.Tensor,
 
330
  beta: float = 0.2,
331
  epsilon: float = 0.2,
332
  ):
333
+ """Compute clipped GRPO loss using current, reference and old-policy log probabilities.
 
334
 
335
  Args:
336
+ logps (torch.Tensor):
337
+ Current policy log probabilities of shape `[B, T]`.
338
+ ref_logps (torch.Tensor):
339
+ Reference policy log probabilities of shape `[B, T]`.
340
+ old_logps (torch.Tensor):
341
+ Old policy log probabilities of shape `[B, T]`.
342
+ pad_mask (torch.Tensor):
343
+ Boolean mask selecting completion tokens of shape `[B, T]`.
344
+ logits_to_keep (int):
345
+ Number of completion tokens, equal to `T`.
346
+ rewards (torch.Tensor):
347
+ Per-sequence rewards used to normalize advantages.
348
+ beta (float, Optional):
349
+ KL penalty coefficient. Default: 0.2.
350
+ epsilon (float, Optional):
351
+ Clipping range around an importance weight of one. Default: 0.2.
352
 
353
  Returns:
354
+ torch.Tensor:
355
+ Scalar loss averaged over valid completion tokens.
356
  """
357
  B = logps.shape[0]
358
  assert B > 1, "Batch * Num generations should be greater than 1"
359
 
360
  rewards_shaped = rewards.view(-1, B) # B,num_generations
361
+ advantages = (rewards_shaped - rewards_shaped.mean(dim=1, keepdim=True)) / (rewards_shaped.std(dim=1, keepdim=True) + 1e-8)
 
362
  advantages = advantages.view(-1) # B*num_generations
 
363
  per_token_kl = torch.exp(ref_logps - logps) - (ref_logps - logps) - 1
364
 
 
 
365
  importance_weights = torch.exp(logps - old_logps)
366
 
 
367
  importance_weights_clipped = torch.clamp(importance_weights, 1 - epsilon, 1 + epsilon)
368
 
 
369
  completion_mask = torch.arange(logits_to_keep, device=logps.device)[None, :] >= 0
370
 
371
+ completion_mask = completion_mask & pad_mask
 
372
 
 
373
  advantages = advantages.unsqueeze(1)
374
 
375
+ token_loss = -(
376
+ torch.min(advantages * importance_weights, advantages * importance_weights_clipped) - beta * per_token_kl
377
+ ) * completion_mask
 
378
 
379
+ loss = token_loss.sum() / completion_mask.sum()
 
380
 
381
  return loss
build/torch-cuda/modules/l2warp.py CHANGED
@@ -39,13 +39,12 @@ class L2Wrap(torch.autograd.Function):
39
  return loss
40
 
41
  @staticmethod
42
- def backward(ctx, grad_output: torch.Tensor):
43
  maxx, ids = ctx.saved_tensors
44
- glogits = torch.zeros(ctx.logits_shape, device=grad_output.device, dtype=grad_output.dtype)
45
- # an autograd.Function must scale its input gradients by the upstream gradient; fold the
46
- # scalar grad_output into the sparse maxx to avoid a second full-size logits allocation
47
- glogits.scatter_(-1, ids, maxx * grad_output)
48
- return grad_output, glogits, None
49
 
50
 
51
  l2_warp = L2Wrap.apply
 
39
  return loss
40
 
41
  @staticmethod
42
+ def backward(ctx, dloss: torch.Tensor):
43
  maxx, ids = ctx.saved_tensors
44
+ dlogits = torch.zeros(ctx.logits_shape, device=dloss.device, dtype=maxx.dtype)
45
+ # scale before scattering to avoid another full-size logits allocation
46
+ dlogits.scatter_(-1, ids, (maxx * dloss).to(maxx.dtype))
47
+ return dloss, dlogits, None
 
48
 
49
 
50
  l2_warp = L2Wrap.apply
build/torch-cuda/modules/{conv/long_conv.py → long_conv.py} RENAMED
@@ -13,7 +13,13 @@ import torch.nn.functional as F
13
  from einops import rearrange
14
 
15
 
16
- def fft_conv(u, k, dropout_mask, gelu=True, k_rev=None):
 
 
 
 
 
 
17
  seqlen = u.shape[-1]
18
  fft_size = 2 * seqlen
19
  k_f = torch.fft.rfft(k, n=fft_size) / fft_size
@@ -37,60 +43,52 @@ def fft_conv(u, k, dropout_mask, gelu=True, k_rev=None):
37
 
38
  class LongConvolution(nn.Module):
39
  """
40
- LongConvolution applies a convolution operation on the input tensor using a fixed
41
- filter of length max_len.
42
- The filter is learned during training and is applied using FFT convolution.
43
 
44
  Args:
45
- hidden_size (int): The number of expected features in the input and output.
46
- max_len (int): The maximum sequence length.
 
 
47
 
48
  Returns:
49
- y: [batch_size, seq_len, hidden_size] tensor
 
50
  """
51
 
52
- def __init__(
53
- self,
54
- hidden_size: int,
55
- max_len: int,
56
- **kwargs,
57
- ):
58
- """
59
- Initializes the LongConvolution module.
60
- Args:
61
- hidden_size (int): The number of expected features in the input and output.
62
- max_len (int): The maximum sequence length.
63
- """
64
  super().__init__()
65
  self.hidden_size = hidden_size
66
  self.filter = nn.Parameter(torch.randn(self.hidden_size, max_len), requires_grad=True)
67
 
68
- def forward(self, x: torch.Tensor, *args, **kwargs):
69
  """
70
- Applies the LongConvolution operation on the input tensor.
71
  Args:
72
- x: [batch_size, seq_len, hidden_size] tensor
 
 
73
  Returns:
74
- y: [batch_size, seq_len, hidden_size] tensor
 
75
  """
76
  x = x.transpose(1, 2)
77
- y = fft_conv(x, self.filter, dropout_mask=None, gelu=False)
78
  y = y.transpose(1, 2)
79
  return y.to(dtype=x.dtype)
80
 
81
 
82
  class PositionalEmbedding(nn.Module):
83
- def __init__(self, emb_dim: int, seq_len: int, **kwargs):
84
  """Complex exponential positional embeddings for implicit long convolution filters."""
85
  super().__init__()
86
 
87
  self.seq_len = seq_len
88
- # The time embedding fed to the filteres is normalized so that t_f = 1
89
  t = torch.linspace(0, 1, self.seq_len)[None, :, None] # 1, L, 1
90
 
91
  if emb_dim > 1:
92
  bands = (emb_dim - 1) // 2
93
- # To compute the right embeddings we use the "proper" linspace
94
  t_rescaled = torch.linspace(0, seq_len - 1, seq_len)[None, :, None]
95
  w = 2 * math.pi * t_rescaled / seq_len # 1, L, 1
96
 
@@ -99,7 +97,7 @@ class PositionalEmbedding(nn.Module):
99
  z = torch.cat([t, z.real, z.imag], dim=-1)
100
  self.z = nn.Parameter(z, requires_grad=False)
101
 
102
- def forward(self, L):
103
  return self.z[:, :L]
104
 
105
 
@@ -112,61 +110,45 @@ class ImplicitLongConvolution(nn.Module):
112
  The number of expected features in the input and output.
113
  max_len (int):
114
  The maximum sequence length.
115
- d_emb (Optional[int]):
116
  The dimension of the positional embeddings. Must be odd and greater or equal to 3 (time, sine and cosine).
117
- Defaults to 3.
118
- d_hidden (Optional[int]):
119
- The number of features in the hidden layer of the MLP. Defaults to 16.
120
 
121
  Attributes:
122
- pos_emb (`PositionalEmbedding`): The positional embedding layer.
123
- mlp (`nn.Sequential`): The MLP that parameterizes the implicit filter.
124
-
 
125
  """
126
 
127
- def __init__(
128
- self,
129
- hidden_size: int,
130
- max_len: int,
131
- d_emb: int = 3,
132
- d_hidden: int = 16,
133
- **kwargs,
134
- ):
135
- """
136
- Long convolution with implicit filter parameterized by an MLP.
137
-
138
-
139
- """
140
  super().__init__()
141
  self.hidden_size = hidden_size
142
  self.d_emb = d_emb
143
 
144
- assert (
145
- d_emb % 2 != 0 and d_emb >= 3
146
- ), "d_emb must be odd and greater or equal to 3 (time, sine and cosine)"
147
- self.pos_emb = PositionalEmbedding(d_emb, max_len)
148
 
149
- # final linear layer
150
- self.mlp = nn.Sequential(
151
- nn.Linear(d_emb, d_hidden),
152
- torch.nn.ReLU(),
153
- nn.Linear(d_hidden, hidden_size),
154
- )
155
 
156
- def filter(self, seq_len: int, *args, **kwargs):
157
  return self.mlp(self.pos_emb(seq_len)).transpose(1, 2)
158
 
159
- def forward(self, x: torch.Tensor, *args, **kwargs):
160
  """
161
  Args:
162
- x: [batch_size, seq_len, hidden_size] tensor
 
163
 
164
  Returns:
165
- y: [batch_size, seq_len, hidden_size] tensor
 
166
  """
167
  x = x.transpose(1, 2)
168
  k = self.filter(x.shape[-1])
169
- y = fft_conv(x, k, dropout_mask=None, gelu=False)
170
 
171
  y = y.transpose(1, 2)
172
  return y.to(dtype=x.dtype)
 
13
  from einops import rearrange
14
 
15
 
16
+ def fft_conv(
17
+ u: torch.Tensor,
18
+ k: torch.Tensor,
19
+ dropout_mask: torch.Tensor | None,
20
+ gelu: bool = True,
21
+ k_rev: torch.Tensor | None = None,
22
+ ) -> torch.Tensor:
23
  seqlen = u.shape[-1]
24
  fft_size = 2 * seqlen
25
  k_f = torch.fft.rfft(k, n=fft_size) / fft_size
 
43
 
44
  class LongConvolution(nn.Module):
45
  """
46
+ Apply FFT convolution with a learned filter of length max_len.
 
 
47
 
48
  Args:
49
+ hidden_size (int):
50
+ The number of expected features in the input and output.
51
+ max_len (int):
52
+ The maximum sequence length.
53
 
54
  Returns:
55
+ y (torch.Tensor):
56
+ Output of shape [batch_size, seq_len, hidden_size].
57
  """
58
 
59
+ def __init__(self, hidden_size: int, max_len: int, **kwargs) -> None:
 
 
 
 
 
 
 
 
 
 
 
60
  super().__init__()
61
  self.hidden_size = hidden_size
62
  self.filter = nn.Parameter(torch.randn(self.hidden_size, max_len), requires_grad=True)
63
 
64
+ def forward(self, x: torch.Tensor, *args, **kwargs) -> torch.Tensor:
65
  """
 
66
  Args:
67
+ x (torch.Tensor):
68
+ Input of shape [batch_size, seq_len, hidden_size].
69
+
70
  Returns:
71
+ y (torch.Tensor):
72
+ Output of shape [batch_size, seq_len, hidden_size].
73
  """
74
  x = x.transpose(1, 2)
75
+ y = fft_conv(u=x, k=self.filter, dropout_mask=None, gelu=False)
76
  y = y.transpose(1, 2)
77
  return y.to(dtype=x.dtype)
78
 
79
 
80
  class PositionalEmbedding(nn.Module):
81
+ def __init__(self, emb_dim: int, seq_len: int, **kwargs) -> None:
82
  """Complex exponential positional embeddings for implicit long convolution filters."""
83
  super().__init__()
84
 
85
  self.seq_len = seq_len
86
+ # the time embedding fed to the filters is normalized so that t_f = 1
87
  t = torch.linspace(0, 1, self.seq_len)[None, :, None] # 1, L, 1
88
 
89
  if emb_dim > 1:
90
  bands = (emb_dim - 1) // 2
91
+ # use integer time steps for the complex exponential embeddings
92
  t_rescaled = torch.linspace(0, seq_len - 1, seq_len)[None, :, None]
93
  w = 2 * math.pi * t_rescaled / seq_len # 1, L, 1
94
 
 
97
  z = torch.cat([t, z.real, z.imag], dim=-1)
98
  self.z = nn.Parameter(z, requires_grad=False)
99
 
100
+ def forward(self, L: int) -> torch.Tensor:
101
  return self.z[:, :L]
102
 
103
 
 
110
  The number of expected features in the input and output.
111
  max_len (int):
112
  The maximum sequence length.
113
+ d_emb (int, Optional):
114
  The dimension of the positional embeddings. Must be odd and greater or equal to 3 (time, sine and cosine).
115
+ Default: 3.
116
+ d_hidden (int, Optional):
117
+ The number of features in the hidden layer of the MLP. Default: 16.
118
 
119
  Attributes:
120
+ pos_emb (PositionalEmbedding):
121
+ The positional embedding layer.
122
+ mlp (nn.Sequential):
123
+ The MLP that parameterizes the implicit filter.
124
  """
125
 
126
+ def __init__(self, hidden_size: int, max_len: int, d_emb: int = 3, d_hidden: int = 16, **kwargs) -> None:
 
 
 
 
 
 
 
 
 
 
 
 
127
  super().__init__()
128
  self.hidden_size = hidden_size
129
  self.d_emb = d_emb
130
 
131
+ assert d_emb % 2 != 0 and d_emb >= 3, "d_emb must be odd and greater or equal to 3 (time, sine and cosine)"
132
+ self.pos_emb = PositionalEmbedding(emb_dim=d_emb, seq_len=max_len)
 
 
133
 
134
+ self.mlp = nn.Sequential(nn.Linear(d_emb, d_hidden), torch.nn.ReLU(), nn.Linear(d_hidden, hidden_size))
 
 
 
 
 
135
 
136
+ def filter(self, seq_len: int, *args, **kwargs) -> torch.Tensor:
137
  return self.mlp(self.pos_emb(seq_len)).transpose(1, 2)
138
 
139
+ def forward(self, x: torch.Tensor, *args, **kwargs) -> torch.Tensor:
140
  """
141
  Args:
142
+ x (torch.Tensor):
143
+ Input of shape [batch_size, seq_len, hidden_size].
144
 
145
  Returns:
146
+ y (torch.Tensor):
147
+ Output of shape [batch_size, seq_len, hidden_size].
148
  """
149
  x = x.transpose(1, 2)
150
  k = self.filter(x.shape[-1])
151
+ y = fft_conv(u=x, k=k, dropout_mask=None, gelu=False)
152
 
153
  y = y.transpose(1, 2)
154
  return y.to(dtype=x.dtype)
build/{torch-xpu/modules → torch-cuda/modules/norm}/__init__.py RENAMED
@@ -5,48 +5,46 @@
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
- from ..modules.convolution import ImplicitLongConvolution, LongConvolution, ShortConvolution
9
- from ..modules.fused_bitlinear import BitLinear, FusedBitLinear
10
- from ..modules.fused_cross_entropy import FusedCrossEntropyLoss
11
- from ..modules.fused_kl_div import FusedKLDivLoss
12
- from ..modules.fused_linear_cross_entropy import FusedLinearCrossEntropyLoss
13
- from ..modules.fused_norm_gate import (
14
  FusedLayerNormGated,
 
15
  FusedLayerNormSwishGate,
16
  FusedLayerNormSwishGateLinear,
17
  FusedRMSNormGated,
 
18
  FusedRMSNormSwishGate,
19
  FusedRMSNormSwishGateLinear,
20
  )
21
- from ..modules.l2norm import L2Norm
22
- from ..modules.layernorm import GroupNorm, GroupNormLinear, LayerNorm, LayerNormLinear, RMSNorm, RMSNormLinear
23
- from ..modules.mlp import GatedMLP
24
- from ..modules.rotary import RotaryEmbedding
25
- from ..modules.token_shift import TokenShift
 
 
 
 
 
 
26
 
27
  __all__ = [
28
- 'BitLinear',
29
- 'FusedBitLinear',
30
- 'FusedCrossEntropyLoss',
31
- 'FusedKLDivLoss',
32
  'FusedLayerNormGated',
 
33
  'FusedLayerNormSwishGate',
34
  'FusedLayerNormSwishGateLinear',
35
- 'FusedLinearCrossEntropyLoss',
36
  'FusedRMSNormGated',
 
37
  'FusedRMSNormSwishGate',
38
  'FusedRMSNormSwishGateLinear',
39
- 'GatedMLP',
40
  'GroupNorm',
41
  'GroupNormLinear',
42
- 'ImplicitLongConvolution',
43
  'L2Norm',
44
  'LayerNorm',
 
45
  'LayerNormLinear',
46
- 'LongConvolution',
47
  'RMSNorm',
 
48
  'RMSNormLinear',
49
- 'RotaryEmbedding',
50
- 'ShortConvolution',
51
- 'TokenShift',
52
  ]
 
 
5
  # For a list of all contributors, visit:
6
  # https://github.com/fla-org/flash-linear-attention/graphs/contributors
7
 
8
+ from ...modules.norm.fused_norm_gate import (
 
 
 
 
 
9
  FusedLayerNormGated,
10
+ FusedLayerNormGatedLinear,
11
  FusedLayerNormSwishGate,
12
  FusedLayerNormSwishGateLinear,
13
  FusedRMSNormGated,
14
+ FusedRMSNormGatedLinear,
15
  FusedRMSNormSwishGate,
16
  FusedRMSNormSwishGateLinear,
17
  )
18
+ from ...modules.norm.l2norm import L2Norm
19
+ from ...modules.norm.layernorm import (
20
+ GroupNorm,
21
+ GroupNormLinear,
22
+ LayerNorm,
23
+ LayerNormLinear,
24
+ NormParallel,
25
+ RMSNorm,
26
+ RMSNormLinear,
27
+ )
28
+ from ...modules.norm.layernorm_gated import LayerNormGated, RMSNormGated
29
 
30
  __all__ = [
 
 
 
 
31
  'FusedLayerNormGated',
32
+ 'FusedLayerNormGatedLinear',
33
  'FusedLayerNormSwishGate',
34
  'FusedLayerNormSwishGateLinear',
 
35
  'FusedRMSNormGated',
36
+ 'FusedRMSNormGatedLinear',
37
  'FusedRMSNormSwishGate',
38
  'FusedRMSNormSwishGateLinear',
 
39
  'GroupNorm',
40
  'GroupNormLinear',
 
41
  'L2Norm',
42
  'LayerNorm',
43
+ 'LayerNormGated',
44
  'LayerNormLinear',
45
+ 'NormParallel',
46
  'RMSNorm',
47
+ 'RMSNormGated',
48
  'RMSNormLinear',
 
 
 
49
  ]
50
+
build/{torch-xpu/modules → torch-cuda/modules/norm}/fused_norm_gate.py RENAMED
@@ -15,7 +15,8 @@ import torch.nn.functional as F
15
  import triton
16
  import triton.language as tl
17
 
18
- from ..utils import autotune_cache_kwargs, get_multiprocessor_count, input_guard
 
19
 
20
 
21
  @triton.heuristics(
@@ -31,20 +32,20 @@ from ..utils import autotune_cache_kwargs, get_multiprocessor_count, input_guard
31
  key=["D", "NB", "IS_RMS_NORM", "STORE_RESIDUAL_OUT", "HAS_RESIDUAL", "HAS_WEIGHT"],
32
  **autotune_cache_kwargs,
33
  )
34
- @triton.jit
35
  def layer_norm_gated_fwd_kernel(
36
- x, # pointer to the input
37
- g, # pointer to the gate
38
- y, # pointer to the output
39
- w, # pointer to the weights
40
- b, # pointer to the biases
41
- residual, # pointer to the residual
42
- residual_out, # pointer to the residual
43
- mean, # pointer to the mean
44
- rstd, # pointer to the 1/std
45
- eps, # epsilon to avoid division by zero
46
- T, # number of rows in x
47
- D: tl.constexpr, # number of columns in x
48
  BT: tl.constexpr,
49
  BD: tl.constexpr,
50
  NB: tl.constexpr,
@@ -55,23 +56,26 @@ def layer_norm_gated_fwd_kernel(
55
  HAS_WEIGHT: tl.constexpr,
56
  HAS_BIAS: tl.constexpr,
57
  ):
58
- i_t = tl.program_id(0)
59
 
 
60
  o_d = tl.arange(0, BD)
61
  m_d = o_d < D
 
 
62
 
63
- p_x = tl.make_block_ptr(x, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
64
- b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
65
  if HAS_RESIDUAL:
66
- p_res = tl.make_block_ptr(residual, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
67
- b_x += tl.load(p_res, boundary_check=(0, 1)).to(tl.float32)
68
  if STORE_RESIDUAL_OUT:
69
- p_res_out = tl.make_block_ptr(residual_out, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
70
- tl.store(p_res_out, b_x.to(p_res_out.dtype.element_ty), boundary_check=(0, 1))
71
  if not IS_RMS_NORM:
72
  b_mean = tl.sum(b_x, axis=1) / D
73
- p_mean = tl.make_block_ptr(mean, (T,), (1,), (i_t * BT,), (BT,), (0,))
74
- tl.store(p_mean, b_mean.to(p_mean.dtype.element_ty), boundary_check=(0,))
75
  b_xbar = tl.where(m_d[None, :], b_x - b_mean[:, None], 0.0)
76
  b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
77
  else:
@@ -79,8 +83,8 @@ def layer_norm_gated_fwd_kernel(
79
  b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
80
  b_rstd = 1 / tl.sqrt(b_var + eps)
81
 
82
- p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t * BT,), (BT,), (0,))
83
- tl.store(p_rstd, b_rstd.to(p_rstd.dtype.element_ty), boundary_check=(0,))
84
 
85
  if HAS_WEIGHT:
86
  b_w = tl.load(w + o_d, mask=m_d).to(tl.float32)
@@ -92,16 +96,15 @@ def layer_norm_gated_fwd_kernel(
92
  b_y = b_y + b_b[None, :]
93
 
94
  # swish/sigmoid output gate
95
- p_g = tl.make_block_ptr(g, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
96
- b_g = tl.load(p_g, boundary_check=(0, 1)).to(tl.float32)
97
  if ACTIVATION == "swish" or ACTIVATION == "silu":
98
  b_y = b_y * b_g * tl.sigmoid(b_g)
99
  elif ACTIVATION == "sigmoid":
100
  b_y = b_y * tl.sigmoid(b_g)
101
 
102
- # Write output
103
- p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
104
- tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
105
 
106
 
107
  @triton.heuristics(
@@ -118,18 +121,18 @@ def layer_norm_gated_fwd_kernel(
118
  **autotune_cache_kwargs,
119
  )
120
  @triton.jit
121
- def layer_norm_gated_fwd_kernel1(
122
- x, # pointer to the input
123
- g, # pointer to the gate
124
- y, # pointer to the output
125
- w, # pointer to the weights
126
- b, # pointer to the biases
127
- residual, # pointer to the residual
128
- residual_out, # pointer to the residual
129
- mean, # pointer to the mean
130
- rstd, # pointer to the 1/std
131
- eps, # epsilon to avoid division by zero
132
- D: tl.constexpr, # number of columns in x
133
  BD: tl.constexpr,
134
  ACTIVATION: tl.constexpr,
135
  IS_RMS_NORM: tl.constexpr,
@@ -138,7 +141,7 @@ def layer_norm_gated_fwd_kernel1(
138
  HAS_WEIGHT: tl.constexpr,
139
  HAS_BIAS: tl.constexpr,
140
  ):
141
- i_t = tl.program_id(0)
142
  x += i_t * D
143
  y += i_t * D
144
  g += i_t * D
@@ -181,7 +184,6 @@ def layer_norm_gated_fwd_kernel1(
181
  elif ACTIVATION == "sigmoid":
182
  b_y = b_y * tl.sigmoid(b_g)
183
 
184
- # Write output
185
  tl.store(y + o_d, b_y, mask=m_d)
186
 
187
 
@@ -198,18 +200,18 @@ def layer_norm_gated_fwd_kernel1(
198
  key=["D", "NB", "IS_RMS_NORM", "HAS_DRESIDUAL", "HAS_WEIGHT"],
199
  **autotune_cache_kwargs,
200
  )
201
- @triton.jit
202
  def layer_norm_gated_bwd_kernel(
203
- x, # pointer to the input
204
- g, # pointer to the gate
205
- w, # pointer to the weights
206
- b, # pointer to the biases
207
- y, # pointer to the output to be recomputed
208
- dy, # pointer to the output gradient
209
- dx, # pointer to the input gradient
210
- dg, # pointer to the gate gradient
211
- dw, # pointer to the partial sum of weights gradient
212
- db, # pointer to the partial sum of biases gradient
213
  dresidual,
214
  dresidual_in,
215
  mean,
@@ -228,7 +230,7 @@ def layer_norm_gated_bwd_kernel(
228
  HAS_BIAS: tl.constexpr,
229
  RECOMPUTE_OUTPUT: tl.constexpr,
230
  ):
231
- i_s = tl.program_id(0)
232
  o_d = tl.arange(0, BD)
233
  m_d = o_d < D
234
  if HAS_WEIGHT:
@@ -238,45 +240,45 @@ def layer_norm_gated_bwd_kernel(
238
  b_b = tl.load(b + o_d, mask=m_d, other=0.0).to(tl.float32)
239
  b_db = tl.zeros((BT, BD), dtype=tl.float32)
240
 
241
- # the caller guarantees NS = min(SM, T), so every program has at least one token.
242
- # the last program's range may slightly exceed T (since BS = ceil(T/NS));
243
- # make_block_ptr uses the true tensor shape (T, D), so boundary_check
244
- # handles the partial tail tile by zero-padding loads and skipping stores.
245
- # the m_t mask below further ensures dw/db only accumulate valid rows (< T).
246
  for i_t in range(i_s * BS, i_s * BS + BS, BT):
247
- p_x = tl.make_block_ptr(x, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
248
- p_g = tl.make_block_ptr(g, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
249
- p_dy = tl.make_block_ptr(dy, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
250
- p_dx = tl.make_block_ptr(dx, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
251
- p_dg = tl.make_block_ptr(dg, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
 
 
 
252
  # [BT, BD]
253
- b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
254
- b_g = tl.load(p_g, boundary_check=(0, 1)).to(tl.float32)
255
- b_dy = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
256
 
257
  if not IS_RMS_NORM:
258
- p_mean = tl.make_block_ptr(mean, (T,), (1,), (i_t,), (BT,), (0,))
259
- b_mean = tl.load(p_mean, boundary_check=(0,))
260
- p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t,), (BT,), (0,))
261
- b_rstd = tl.load(p_rstd, boundary_check=(0,))
262
- # Compute dx
263
  b_xhat = (b_x - b_mean[:, None]) * b_rstd[:, None] if not IS_RMS_NORM else b_x * b_rstd[:, None]
264
  b_xhat = tl.where(m_d[None, :], b_xhat, 0.0)
265
 
266
  b_y = b_xhat * b_w[None, :] if HAS_WEIGHT else b_xhat
267
  if HAS_BIAS:
268
  b_y = b_y + b_b[None, :]
269
- if RECOMPUTE_OUTPUT:
270
- p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
271
- tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
272
 
273
  b_sigmoid_g = tl.sigmoid(b_g)
274
  if ACTIVATION == "swish" or ACTIVATION == "silu":
 
275
  b_dg = b_dy * b_y * (b_sigmoid_g + b_g * b_sigmoid_g * (1 - b_sigmoid_g))
276
- b_dy = b_dy * b_g * b_sigmoid_g
277
  elif ACTIVATION == "sigmoid":
 
278
  b_dg = b_dy * b_y * b_sigmoid_g * (1 - b_sigmoid_g)
279
- b_dy = b_dy * b_sigmoid_g
 
 
 
 
280
  b_wdy = b_dy
281
 
282
  if HAS_WEIGHT or HAS_BIAS:
@@ -296,16 +298,16 @@ def layer_norm_gated_bwd_kernel(
296
  b_c1 = tl.sum(b_xhat * b_wdy, axis=1) / D
297
  b_dx = (b_wdy - b_xhat * b_c1[:, None]) * b_rstd[:, None]
298
  if HAS_DRESIDUAL:
299
- p_dres = tl.make_block_ptr(dresidual, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
300
- b_dres = tl.load(p_dres, boundary_check=(0, 1)).to(tl.float32)
301
  b_dx += b_dres
302
- # Write dx
303
  if STORE_DRESIDUAL:
304
- p_dres_in = tl.make_block_ptr(dresidual_in, (T, D), (D, 1), (i_t, 0), (BT, BD), (1, 0))
305
- tl.store(p_dres_in, b_dx.to(p_dres_in.dtype.element_ty), boundary_check=(0, 1))
306
 
307
- tl.store(p_dx, b_dx.to(p_dx.dtype.element_ty), boundary_check=(0, 1))
308
- tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), boundary_check=(0, 1))
309
 
310
  if HAS_WEIGHT:
311
  tl.store(dw + i_s * D + o_d, tl.sum(b_dw, axis=0), mask=m_d)
@@ -326,18 +328,18 @@ def layer_norm_gated_bwd_kernel(
326
  key=["D", "IS_RMS_NORM", "STORE_DRESIDUAL", "HAS_DRESIDUAL", "HAS_WEIGHT"],
327
  **autotune_cache_kwargs,
328
  )
329
- @triton.jit
330
- def layer_norm_gated_bwd_kernel1(
331
- x, # pointer to the input
332
- g, # pointer to the gate
333
- w, # pointer to the weights
334
- b, # pointer to the biases
335
- y, # pointer to the output to be recomputed
336
- dy, # pointer to the output gradient
337
- dx, # pointer to the input gradient
338
- dg, # pointer to the gate gradient
339
- dw, # pointer to the partial sum of weights gradient
340
- db, # pointer to the partial sum of biases gradient
341
  dresidual,
342
  dresidual_in,
343
  mean,
@@ -354,7 +356,7 @@ def layer_norm_gated_bwd_kernel1(
354
  HAS_BIAS: tl.constexpr,
355
  RECOMPUTE_OUTPUT: tl.constexpr,
356
  ):
357
- i_s = tl.program_id(0)
358
  o_d = tl.arange(0, BD)
359
  mask = o_d < D
360
  x += i_s * BS * D
@@ -376,7 +378,7 @@ def layer_norm_gated_bwd_kernel1(
376
  b_db = tl.zeros((BD,), dtype=tl.float32)
377
 
378
  for i_t in range(i_s * BS, min(i_s * BS + BS, T)):
379
- # Load data to SRAM
380
  b_x = tl.load(x + o_d, mask=mask, other=0).to(tl.float32)
381
  b_g = tl.load(g + o_d, mask=mask, other=0).to(tl.float32)
382
  b_dy = tl.load(dy + o_d, mask=mask, other=0).to(tl.float32)
@@ -384,23 +386,25 @@ def layer_norm_gated_bwd_kernel1(
384
  if not IS_RMS_NORM:
385
  b_mean = tl.load(mean + i_t)
386
  b_rstd = tl.load(rstd + i_t)
387
- # Compute dx
388
  b_xhat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
389
  b_xhat = tl.where(mask, b_xhat, 0.0)
390
 
391
  b_y = b_xhat * b_w if HAS_WEIGHT else b_xhat
392
  if HAS_BIAS:
393
  b_y = b_y + b_b
394
- if RECOMPUTE_OUTPUT:
395
- tl.store(y + o_d, b_y, mask=mask)
396
 
397
  b_sigmoid_g = tl.sigmoid(b_g)
398
  if ACTIVATION == "swish" or ACTIVATION == "silu":
 
399
  b_dg = b_dy * b_y * (b_sigmoid_g + b_g * b_sigmoid_g * (1 - b_sigmoid_g))
400
- b_dy = b_dy * b_g * b_sigmoid_g
401
  elif ACTIVATION == "sigmoid":
 
402
  b_dg = b_dy * b_y * b_sigmoid_g * (1 - b_sigmoid_g)
403
- b_dy = b_dy * b_sigmoid_g
 
 
 
404
  b_wdy = b_dy
405
  if HAS_WEIGHT:
406
  b_wdy = b_dy * b_w
@@ -417,7 +421,7 @@ def layer_norm_gated_bwd_kernel1(
417
  if HAS_DRESIDUAL:
418
  b_dres = tl.load(dresidual + o_d, mask=mask, other=0).to(tl.float32)
419
  b_dx += b_dres
420
- # Write dx
421
  if STORE_DRESIDUAL:
422
  tl.store(dresidual_in + o_d, b_dx, mask=mask)
423
  tl.store(dx + o_d, b_dx, mask=mask)
@@ -440,6 +444,8 @@ def layer_norm_gated_bwd_kernel1(
440
  tl.store(db + i_s * D + o_d, b_db, mask=mask)
441
 
442
 
 
 
443
  def layer_norm_gated_fwd(
444
  x: torch.Tensor,
445
  g: torch.Tensor,
@@ -447,9 +453,9 @@ def layer_norm_gated_fwd(
447
  bias: torch.Tensor,
448
  activation: str = "swish",
449
  eps: float = 1e-5,
450
- residual: torch.Tensor = None,
451
- out_dtype: torch.dtype = None,
452
- residual_dtype: torch.dtype = None,
453
  is_rms_norm: bool = False,
454
  ):
455
  if residual is not None:
@@ -461,7 +467,7 @@ def layer_norm_gated_fwd(
461
  assert weight.shape == (D,)
462
  if bias is not None:
463
  assert bias.shape == (D,)
464
- # allocate output
465
  y = torch.empty_like(x, dtype=x.dtype if out_dtype is None else out_dtype)
466
  if residual is not None or (residual_dtype is not None and residual_dtype != x.dtype):
467
  residual_out = torch.empty(T, D, device=x.device, dtype=residual_dtype)
@@ -469,17 +475,13 @@ def layer_norm_gated_fwd(
469
  residual_out = None
470
  mean = torch.empty((T,), dtype=torch.float, device=x.device) if not is_rms_norm else None
471
  rstd = torch.empty((T,), dtype=torch.float, device=x.device)
472
- # Less than 64KB per feature: enqueue fused kernel
473
  MAX_FUSED_SIZE = 65536 // x.element_size()
474
  BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
475
  if D > BD:
476
  raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
477
- # heuristics for number of warps
478
-
479
  if D <= 512:
480
- # NOTE(tylerr): Avoid excessive recompilation and autotuning by tolerating a larger range
481
- # of T before recompiling the kernel.
482
- # NB = triton.cdiv(T, 2048)
483
  NB = triton.cdiv(T, 2048 * 32)
484
 
485
  def grid(meta):
@@ -504,7 +506,7 @@ def layer_norm_gated_fwd(
504
  IS_RMS_NORM=is_rms_norm,
505
  )
506
  else:
507
- layer_norm_gated_fwd_kernel1[(T,)](
508
  x=x,
509
  g=g,
510
  y=y,
@@ -524,6 +526,8 @@ def layer_norm_gated_fwd(
524
  return y, mean, rstd, residual_out if residual_out is not None else x
525
 
526
 
 
 
527
  def layer_norm_gated_bwd(
528
  dy: torch.Tensor,
529
  x: torch.Tensor,
@@ -532,12 +536,12 @@ def layer_norm_gated_bwd(
532
  bias: torch.Tensor,
533
  activation: str = "swish",
534
  eps: float = 1e-5,
535
- mean: torch.Tensor = None,
536
- rstd: torch.Tensor = None,
537
- dresidual: torch.Tensor = None,
538
  has_residual: bool = False,
539
  is_rms_norm: bool = False,
540
- x_dtype: torch.dtype = None,
541
  recompute_output: bool = False,
542
  ):
543
  T, D = x.shape
@@ -548,20 +552,17 @@ def layer_norm_gated_bwd(
548
  assert weight.shape == (D,)
549
  if bias is not None:
550
  assert bias.shape == (D,)
551
- # allocate output
552
  dx = torch.empty_like(x) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
553
  dg = torch.empty_like(g) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
554
  dresidual_in = torch.empty_like(x) if has_residual and dx.dtype != x.dtype else None
555
  y = torch.empty(T, D, dtype=dy.dtype, device=dy.device) if recompute_output else None
556
 
557
- # Less than 64KB per feature: enqueue fused kernel
558
  MAX_FUSED_SIZE = 65536 // x.element_size()
559
  BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
560
  if D > BD:
561
  raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
562
- # cap program count to T so no program is completely idle.
563
- # without this, high-SM GPUs (e.g. B200, 160 SMs) with small T would
564
- # launch idle programs whose make_block_ptr offsets exceed the tensor shape.
565
  NS = min(get_multiprocessor_count(x.device.index), T)
566
  BS = math.ceil(T / NS)
567
 
@@ -570,9 +571,7 @@ def layer_norm_gated_bwd(
570
  grid = (NS,)
571
 
572
  if D <= 512:
573
- # NOTE(tylerr): Avoid excessive recompilation and autotuning by tolerating a larger range
574
- # of T before recompiling the kernel.
575
- # NB = triton.cdiv(T, 2048)
576
  NB = triton.cdiv(T, 2048 * 32)
577
 
578
  layer_norm_gated_bwd_kernel[grid](
@@ -591,8 +590,8 @@ def layer_norm_gated_bwd(
591
  mean=mean,
592
  rstd=rstd,
593
  T=T,
594
- D=D,
595
  BS=BS,
 
596
  BD=BD,
597
  NB=NB,
598
  ACTIVATION=activation,
@@ -600,7 +599,7 @@ def layer_norm_gated_bwd(
600
  STORE_DRESIDUAL=dresidual_in is not None,
601
  )
602
  else:
603
- layer_norm_gated_bwd_kernel1[grid](
604
  x=x,
605
  g=g,
606
  w=weight,
@@ -616,8 +615,8 @@ def layer_norm_gated_bwd(
616
  mean=mean,
617
  rstd=rstd,
618
  T=T,
619
- D=D,
620
  BS=BS,
 
621
  BD=BD,
622
  ACTIVATION=activation,
623
  IS_RMS_NORM=is_rms_norm,
@@ -625,7 +624,7 @@ def layer_norm_gated_bwd(
625
  )
626
  dw = dw.sum(0).to(weight.dtype) if weight is not None else None
627
  db = db.sum(0).to(bias.dtype) if bias is not None else None
628
- # Don't need to compute dresidual_in separately in this case
629
  if has_residual and dx.dtype == x.dtype:
630
  dresidual_in = dx
631
  return (dx, dg, dw, db, dresidual_in) if not recompute_output else (dx, dg, dw, db, dresidual_in, y)
@@ -649,7 +648,7 @@ class LayerNormGatedFunction(torch.autograd.Function):
649
  ):
650
  x_shape_og = x.shape
651
  g_shape_og = g.shape
652
- # reshape input data into 2D tensor
653
  x = x.reshape(-1, x.shape[-1])
654
  g = g.reshape(-1, g.shape[-1])
655
  if residual is not None:
@@ -739,7 +738,7 @@ class LayerNormGatedLinearFunction(torch.autograd.Function):
739
  ):
740
  x_shape_og = x.shape
741
  g_shape_og = g.shape
742
- # reshape input data into 2D tensor
743
  x = x.reshape(-1, x.shape[-1])
744
  g = g.reshape(-1, g.shape[-1])
745
  if residual is not None:
@@ -761,7 +760,7 @@ class LayerNormGatedLinearFunction(torch.autograd.Function):
761
  linear_weight = linear_weight.to(dtype)
762
  linear_bias = linear_bias.to(dtype) if linear_bias is not None else None
763
  out = F.linear(y.to(linear_weight.dtype), linear_weight, linear_bias)
764
- # We don't store y, will be recomputed in the backward pass to save memory
765
  ctx.save_for_backward(residual_out, g, norm_weight, norm_bias, linear_weight, mean, rstd)
766
  ctx.x_shape_og = x_shape_og
767
  ctx.g_shape_og = g_shape_og
@@ -829,18 +828,7 @@ def layer_norm_gated(
829
  residual_in_fp32: bool = False,
830
  eps: float = 1e-6,
831
  ):
832
- return LayerNormGatedFunction.apply(
833
- x,
834
- g,
835
- weight,
836
- bias,
837
- activation,
838
- residual,
839
- eps,
840
- prenorm,
841
- residual_in_fp32,
842
- False,
843
- )
844
 
845
 
846
  def rms_norm_gated(
@@ -854,18 +842,7 @@ def rms_norm_gated(
854
  residual_in_fp32: bool = False,
855
  eps: float = 1e-6,
856
  ):
857
- return LayerNormGatedFunction.apply(
858
- x,
859
- g,
860
- weight,
861
- bias,
862
- activation,
863
- residual,
864
- eps,
865
- prenorm,
866
- residual_in_fp32,
867
- True,
868
- )
869
 
870
 
871
  def layer_norm_swish_gate_linear(
@@ -932,7 +909,7 @@ class FusedLayerNormGated(nn.Module):
932
  eps: float = 1e-5,
933
  device: torch.device | None = None,
934
  dtype: torch.dtype | None = None,
935
- ) -> FusedLayerNormGated:
936
  factory_kwargs = {"device": device, "dtype": dtype}
937
  super().__init__()
938
 
@@ -975,17 +952,17 @@ class FusedLayerNormGated(nn.Module):
975
  residual: torch.Tensor | None = None,
976
  prenorm: bool = False,
977
  residual_in_fp32: bool = False,
978
- ) -> torch.Tensor:
979
  return layer_norm_gated(
980
- x,
981
- g,
982
- self.weight,
983
- self.bias,
984
- self.activation,
985
  residual=residual,
986
- eps=self.eps,
987
  prenorm=prenorm,
988
  residual_in_fp32=residual_in_fp32,
 
989
  )
990
 
991
 
@@ -998,7 +975,7 @@ class FusedRMSNormGated(nn.Module):
998
  activation: str = "swish",
999
  device: torch.device | None = None,
1000
  dtype: torch.dtype | None = None,
1001
- ) -> FusedRMSNormGated:
1002
  factory_kwargs = {"device": device, "dtype": dtype}
1003
  super().__init__()
1004
 
@@ -1038,17 +1015,17 @@ class FusedRMSNormGated(nn.Module):
1038
  residual: torch.Tensor | None = None,
1039
  prenorm: bool = False,
1040
  residual_in_fp32: bool = False,
1041
- ) -> torch.Tensor:
1042
  return rms_norm_gated(
1043
- x,
1044
- g,
1045
- self.weight,
1046
- self.bias,
1047
- self.activation,
1048
  residual=residual,
1049
- eps=self.eps,
1050
  prenorm=prenorm,
1051
  residual_in_fp32=residual_in_fp32,
 
1052
  )
1053
 
1054
 
@@ -1061,7 +1038,7 @@ class FusedLayerNormSwishGate(FusedLayerNormGated):
1061
  eps: float = 1e-5,
1062
  device: torch.device | None = None,
1063
  dtype: torch.dtype | None = None,
1064
- ) -> FusedLayerNormSwishGate:
1065
  super().__init__(
1066
  hidden_size=hidden_size,
1067
  elementwise_affine=elementwise_affine,
@@ -1080,14 +1057,8 @@ class FusedRMSNormSwishGate(FusedRMSNormGated):
1080
  eps: float = 1e-5,
1081
  device: torch.device | None = None,
1082
  dtype: torch.dtype | None = None,
1083
- ) -> FusedRMSNormSwishGate:
1084
- super().__init__(
1085
- hidden_size=hidden_size,
1086
- elementwise_affine=elementwise_affine,
1087
- eps=eps,
1088
- device=device,
1089
- dtype=dtype,
1090
- )
1091
 
1092
 
1093
  class FusedLayerNormGatedLinear(nn.Module):
@@ -1098,7 +1069,7 @@ class FusedLayerNormGatedLinear(nn.Module):
1098
  eps: float = 1e-5,
1099
  device: torch.device | None = None,
1100
  dtype: torch.dtype | None = None,
1101
- ) -> FusedLayerNormGatedLinear:
1102
  factory_kwargs = {"device": device, "dtype": dtype}
1103
  super().__init__()
1104
 
@@ -1135,18 +1106,18 @@ class FusedLayerNormGatedLinear(nn.Module):
1135
  residual: torch.Tensor | None = None,
1136
  prenorm: bool = False,
1137
  residual_in_fp32: bool = False,
1138
- ) -> torch.Tensor:
1139
  return layer_norm_swish_gate_linear(
1140
- x,
1141
- g,
1142
- self.weight,
1143
- self.bias,
1144
- weight,
1145
- bias,
1146
  residual=residual,
1147
- eps=self.eps,
1148
  prenorm=prenorm,
1149
  residual_in_fp32=residual_in_fp32,
 
1150
  )
1151
 
1152
 
@@ -1158,25 +1129,19 @@ class FusedLayerNormSwishGateLinear(FusedLayerNormGatedLinear):
1158
  eps: float = 1e-5,
1159
  device: torch.device | None = None,
1160
  dtype: torch.dtype | None = None,
1161
- ) -> FusedLayerNormSwishGateLinear:
1162
- super().__init__(
1163
- hidden_size=hidden_size,
1164
- elementwise_affine=elementwise_affine,
1165
- eps=eps,
1166
- device=device,
1167
- dtype=dtype,
1168
- )
1169
 
1170
 
1171
  class FusedRMSNormGatedLinear(nn.Module):
1172
  def __init__(
1173
  self,
1174
- hidden_size,
1175
  elementwise_affine: bool = True,
1176
  eps: float = 1e-5,
1177
  device: torch.device | None = None,
1178
  dtype: torch.dtype | None = None,
1179
- ) -> FusedRMSNormGatedLinear:
1180
  factory_kwargs = {"device": device, "dtype": dtype}
1181
  super().__init__()
1182
 
@@ -1212,18 +1177,18 @@ class FusedRMSNormGatedLinear(nn.Module):
1212
  residual: torch.Tensor | None = None,
1213
  prenorm: bool = False,
1214
  residual_in_fp32: bool = False,
1215
- ) -> torch.Tensor:
1216
  return rms_norm_swish_gate_linear(
1217
- x,
1218
- g,
1219
- self.weight,
1220
- self.bias,
1221
- weight,
1222
- bias,
1223
  residual=residual,
1224
- eps=self.eps,
1225
  prenorm=prenorm,
1226
  residual_in_fp32=residual_in_fp32,
 
1227
  )
1228
 
1229
 
@@ -1235,11 +1200,23 @@ class FusedRMSNormSwishGateLinear(FusedRMSNormGatedLinear):
1235
  eps: float = 1e-5,
1236
  device: torch.device | None = None,
1237
  dtype: torch.dtype | None = None,
1238
- ) -> FusedRMSNormSwishGateLinear:
1239
- super().__init__(
1240
- hidden_size=hidden_size,
1241
- elementwise_affine=elementwise_affine,
1242
- eps=eps,
1243
- device=device,
1244
- dtype=dtype,
1245
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  import triton
16
  import triton.language as tl
17
 
18
+ from ...backends import dispatch
19
+ from ...utils import autotune_cache_kwargs, get_multiprocessor_count, input_guard
20
 
21
 
22
  @triton.heuristics(
 
32
  key=["D", "NB", "IS_RMS_NORM", "STORE_RESIDUAL_OUT", "HAS_RESIDUAL", "HAS_WEIGHT"],
33
  **autotune_cache_kwargs,
34
  )
35
+ @triton.jit(do_not_specialize=['T'])
36
  def layer_norm_gated_fwd_kernel(
37
+ x,
38
+ g,
39
+ y,
40
+ w,
41
+ b,
42
+ residual,
43
+ residual_out,
44
+ mean,
45
+ rstd,
46
+ eps,
47
+ T,
48
+ D: tl.constexpr,
49
  BT: tl.constexpr,
50
  BD: tl.constexpr,
51
  NB: tl.constexpr,
 
56
  HAS_WEIGHT: tl.constexpr,
57
  HAS_BIAS: tl.constexpr,
58
  ):
59
+ i_t = tl.program_id(0).to(tl.int64)
60
 
61
+ o_t = i_t * BT + tl.arange(0, BT)
62
  o_d = tl.arange(0, BD)
63
  m_d = o_d < D
64
+ m_t = o_t < T
65
+ m_x = m_t[:, None] & m_d[None, :]
66
 
67
+ p_x = x + o_t[:, None] * D + o_d[None, :]
68
+ b_x = tl.load(p_x, mask=m_x, other=0.0).to(tl.float32)
69
  if HAS_RESIDUAL:
70
+ p_res = residual + o_t[:, None] * D + o_d[None, :]
71
+ b_x += tl.load(p_res, mask=m_x, other=0.0).to(tl.float32)
72
  if STORE_RESIDUAL_OUT:
73
+ p_res_out = residual_out + o_t[:, None] * D + o_d[None, :]
74
+ tl.store(p_res_out, b_x.to(p_res_out.dtype.element_ty), mask=m_x)
75
  if not IS_RMS_NORM:
76
  b_mean = tl.sum(b_x, axis=1) / D
77
+ p_mean = mean + o_t
78
+ tl.store(p_mean, b_mean.to(p_mean.dtype.element_ty), mask=m_t)
79
  b_xbar = tl.where(m_d[None, :], b_x - b_mean[:, None], 0.0)
80
  b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
81
  else:
 
83
  b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
84
  b_rstd = 1 / tl.sqrt(b_var + eps)
85
 
86
+ p_rstd = rstd + o_t
87
+ tl.store(p_rstd, b_rstd.to(p_rstd.dtype.element_ty), mask=m_t)
88
 
89
  if HAS_WEIGHT:
90
  b_w = tl.load(w + o_d, mask=m_d).to(tl.float32)
 
96
  b_y = b_y + b_b[None, :]
97
 
98
  # swish/sigmoid output gate
99
+ p_g = g + o_t[:, None] * D + o_d[None, :]
100
+ b_g = tl.load(p_g, mask=m_x, other=0.0).to(tl.float32)
101
  if ACTIVATION == "swish" or ACTIVATION == "silu":
102
  b_y = b_y * b_g * tl.sigmoid(b_g)
103
  elif ACTIVATION == "sigmoid":
104
  b_y = b_y * tl.sigmoid(b_g)
105
 
106
+ p_y = y + o_t[:, None] * D + o_d[None, :]
107
+ tl.store(p_y, b_y.to(p_y.dtype.element_ty), mask=m_x)
 
108
 
109
 
110
  @triton.heuristics(
 
121
  **autotune_cache_kwargs,
122
  )
123
  @triton.jit
124
+ def layer_norm_gated_fwd_kernel_row(
125
+ x,
126
+ g,
127
+ y,
128
+ w,
129
+ b,
130
+ residual,
131
+ residual_out,
132
+ mean,
133
+ rstd,
134
+ eps,
135
+ D: tl.constexpr,
136
  BD: tl.constexpr,
137
  ACTIVATION: tl.constexpr,
138
  IS_RMS_NORM: tl.constexpr,
 
141
  HAS_WEIGHT: tl.constexpr,
142
  HAS_BIAS: tl.constexpr,
143
  ):
144
+ i_t = tl.program_id(0).to(tl.int64)
145
  x += i_t * D
146
  y += i_t * D
147
  g += i_t * D
 
184
  elif ACTIVATION == "sigmoid":
185
  b_y = b_y * tl.sigmoid(b_g)
186
 
 
187
  tl.store(y + o_d, b_y, mask=m_d)
188
 
189
 
 
200
  key=["D", "NB", "IS_RMS_NORM", "HAS_DRESIDUAL", "HAS_WEIGHT"],
201
  **autotune_cache_kwargs,
202
  )
203
+ @triton.jit(do_not_specialize=['T'])
204
  def layer_norm_gated_bwd_kernel(
205
+ x,
206
+ g,
207
+ w,
208
+ b,
209
+ y,
210
+ dy,
211
+ dx,
212
+ dg,
213
+ dw,
214
+ db,
215
  dresidual,
216
  dresidual_in,
217
  mean,
 
230
  HAS_BIAS: tl.constexpr,
231
  RECOMPUTE_OUTPUT: tl.constexpr,
232
  ):
233
+ i_s = tl.program_id(0).to(tl.int64)
234
  o_d = tl.arange(0, BD)
235
  m_d = o_d < D
236
  if HAS_WEIGHT:
 
240
  b_b = tl.load(b + o_d, mask=m_d, other=0.0).to(tl.float32)
241
  b_db = tl.zeros((BT, BD), dtype=tl.float32)
242
 
 
 
 
 
 
243
  for i_t in range(i_s * BS, i_s * BS + BS, BT):
244
+ o_t = (i_t + tl.arange(0, BT)).to(tl.int64)
245
+ m_t = o_t < T
246
+ m_x = m_t[:, None] & m_d[None, :]
247
+ p_x = x + o_t[:, None] * D + o_d[None, :]
248
+ p_g = g + o_t[:, None] * D + o_d[None, :]
249
+ p_dy = dy + o_t[:, None] * D + o_d[None, :]
250
+ p_dx = dx + o_t[:, None] * D + o_d[None, :]
251
+ p_dg = dg + o_t[:, None] * D + o_d[None, :]
252
  # [BT, BD]
253
+ b_x = tl.load(p_x, mask=m_x, other=0.0).to(tl.float32)
254
+ b_g = tl.load(p_g, mask=m_x, other=0.0).to(tl.float32)
255
+ b_dy = tl.load(p_dy, mask=m_x, other=0.0).to(tl.float32)
256
 
257
  if not IS_RMS_NORM:
258
+ p_mean = mean + o_t
259
+ b_mean = tl.load(p_mean, mask=m_t, other=0.0)
260
+ p_rstd = rstd + o_t
261
+ b_rstd = tl.load(p_rstd, mask=m_t, other=0.0)
262
+
263
  b_xhat = (b_x - b_mean[:, None]) * b_rstd[:, None] if not IS_RMS_NORM else b_x * b_rstd[:, None]
264
  b_xhat = tl.where(m_d[None, :], b_xhat, 0.0)
265
 
266
  b_y = b_xhat * b_w[None, :] if HAS_WEIGHT else b_xhat
267
  if HAS_BIAS:
268
  b_y = b_y + b_b[None, :]
 
 
 
269
 
270
  b_sigmoid_g = tl.sigmoid(b_g)
271
  if ACTIVATION == "swish" or ACTIVATION == "silu":
272
+ b_gate = b_g * b_sigmoid_g
273
  b_dg = b_dy * b_y * (b_sigmoid_g + b_g * b_sigmoid_g * (1 - b_sigmoid_g))
 
274
  elif ACTIVATION == "sigmoid":
275
+ b_gate = b_sigmoid_g
276
  b_dg = b_dy * b_y * b_sigmoid_g * (1 - b_sigmoid_g)
277
+ # b_dg needs pre-gate b_y; the recomputed output must include the gate to match the caller's linear input.
278
+ if RECOMPUTE_OUTPUT:
279
+ p_y = y + o_t[:, None] * D + o_d[None, :]
280
+ tl.store(p_y, (b_y * b_gate).to(p_y.dtype.element_ty), mask=m_x)
281
+ b_dy = b_dy * b_gate
282
  b_wdy = b_dy
283
 
284
  if HAS_WEIGHT or HAS_BIAS:
 
298
  b_c1 = tl.sum(b_xhat * b_wdy, axis=1) / D
299
  b_dx = (b_wdy - b_xhat * b_c1[:, None]) * b_rstd[:, None]
300
  if HAS_DRESIDUAL:
301
+ p_dres = dresidual + o_t[:, None] * D + o_d[None, :]
302
+ b_dres = tl.load(p_dres, mask=m_x, other=0.0).to(tl.float32)
303
  b_dx += b_dres
304
+
305
  if STORE_DRESIDUAL:
306
+ p_dres_in = dresidual_in + o_t[:, None] * D + o_d[None, :]
307
+ tl.store(p_dres_in, b_dx.to(p_dres_in.dtype.element_ty), mask=m_x)
308
 
309
+ tl.store(p_dx, b_dx.to(p_dx.dtype.element_ty), mask=m_x)
310
+ tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), mask=m_x)
311
 
312
  if HAS_WEIGHT:
313
  tl.store(dw + i_s * D + o_d, tl.sum(b_dw, axis=0), mask=m_d)
 
328
  key=["D", "IS_RMS_NORM", "STORE_DRESIDUAL", "HAS_DRESIDUAL", "HAS_WEIGHT"],
329
  **autotune_cache_kwargs,
330
  )
331
+ @triton.jit(do_not_specialize=['T'])
332
+ def layer_norm_gated_bwd_kernel_row(
333
+ x,
334
+ g,
335
+ w,
336
+ b,
337
+ y,
338
+ dy,
339
+ dx,
340
+ dg,
341
+ dw,
342
+ db,
343
  dresidual,
344
  dresidual_in,
345
  mean,
 
356
  HAS_BIAS: tl.constexpr,
357
  RECOMPUTE_OUTPUT: tl.constexpr,
358
  ):
359
+ i_s = tl.program_id(0).to(tl.int64)
360
  o_d = tl.arange(0, BD)
361
  mask = o_d < D
362
  x += i_s * BS * D
 
378
  b_db = tl.zeros((BD,), dtype=tl.float32)
379
 
380
  for i_t in range(i_s * BS, min(i_s * BS + BS, T)):
381
+
382
  b_x = tl.load(x + o_d, mask=mask, other=0).to(tl.float32)
383
  b_g = tl.load(g + o_d, mask=mask, other=0).to(tl.float32)
384
  b_dy = tl.load(dy + o_d, mask=mask, other=0).to(tl.float32)
 
386
  if not IS_RMS_NORM:
387
  b_mean = tl.load(mean + i_t)
388
  b_rstd = tl.load(rstd + i_t)
389
+
390
  b_xhat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
391
  b_xhat = tl.where(mask, b_xhat, 0.0)
392
 
393
  b_y = b_xhat * b_w if HAS_WEIGHT else b_xhat
394
  if HAS_BIAS:
395
  b_y = b_y + b_b
 
 
396
 
397
  b_sigmoid_g = tl.sigmoid(b_g)
398
  if ACTIVATION == "swish" or ACTIVATION == "silu":
399
+ b_gate = b_g * b_sigmoid_g
400
  b_dg = b_dy * b_y * (b_sigmoid_g + b_g * b_sigmoid_g * (1 - b_sigmoid_g))
 
401
  elif ACTIVATION == "sigmoid":
402
+ b_gate = b_sigmoid_g
403
  b_dg = b_dy * b_y * b_sigmoid_g * (1 - b_sigmoid_g)
404
+ # b_dg needs pre-gate b_y; the recomputed output must include the gate to match the caller's linear input.
405
+ if RECOMPUTE_OUTPUT:
406
+ tl.store(y + o_d, b_y * b_gate, mask=mask)
407
+ b_dy = b_dy * b_gate
408
  b_wdy = b_dy
409
  if HAS_WEIGHT:
410
  b_wdy = b_dy * b_w
 
421
  if HAS_DRESIDUAL:
422
  b_dres = tl.load(dresidual + o_d, mask=mask, other=0).to(tl.float32)
423
  b_dx += b_dres
424
+
425
  if STORE_DRESIDUAL:
426
  tl.store(dresidual_in + o_d, b_dx, mask=mask)
427
  tl.store(dx + o_d, b_dx, mask=mask)
 
444
  tl.store(db + i_s * D + o_d, b_db, mask=mask)
445
 
446
 
447
+ # Use triton only, other backends (cutlass, cute, etc.) need additional torch.compile support
448
+ # @dispatch
449
  def layer_norm_gated_fwd(
450
  x: torch.Tensor,
451
  g: torch.Tensor,
 
453
  bias: torch.Tensor,
454
  activation: str = "swish",
455
  eps: float = 1e-5,
456
+ residual: torch.Tensor | None = None,
457
+ out_dtype: torch.dtype | None = None,
458
+ residual_dtype: torch.dtype | None = None,
459
  is_rms_norm: bool = False,
460
  ):
461
  if residual is not None:
 
467
  assert weight.shape == (D,)
468
  if bias is not None:
469
  assert bias.shape == (D,)
470
+
471
  y = torch.empty_like(x, dtype=x.dtype if out_dtype is None else out_dtype)
472
  if residual is not None or (residual_dtype is not None and residual_dtype != x.dtype):
473
  residual_out = torch.empty(T, D, device=x.device, dtype=residual_dtype)
 
475
  residual_out = None
476
  mean = torch.empty((T,), dtype=torch.float, device=x.device) if not is_rms_norm else None
477
  rstd = torch.empty((T,), dtype=torch.float, device=x.device)
478
+
479
  MAX_FUSED_SIZE = 65536 // x.element_size()
480
  BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
481
  if D > BD:
482
  raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
 
 
483
  if D <= 512:
484
+ # bucket token counts to limit autotuning across sequence lengths.
 
 
485
  NB = triton.cdiv(T, 2048 * 32)
486
 
487
  def grid(meta):
 
506
  IS_RMS_NORM=is_rms_norm,
507
  )
508
  else:
509
+ layer_norm_gated_fwd_kernel_row[(T,)](
510
  x=x,
511
  g=g,
512
  y=y,
 
526
  return y, mean, rstd, residual_out if residual_out is not None else x
527
 
528
 
529
+ # Use triton only, other backends (cutlass, cute, etc.) need additional torch.compile support
530
+ # @dispatch
531
  def layer_norm_gated_bwd(
532
  dy: torch.Tensor,
533
  x: torch.Tensor,
 
536
  bias: torch.Tensor,
537
  activation: str = "swish",
538
  eps: float = 1e-5,
539
+ mean: torch.Tensor | None = None,
540
+ rstd: torch.Tensor | None = None,
541
+ dresidual: torch.Tensor | None = None,
542
  has_residual: bool = False,
543
  is_rms_norm: bool = False,
544
+ x_dtype: torch.dtype | None = None,
545
  recompute_output: bool = False,
546
  ):
547
  T, D = x.shape
 
552
  assert weight.shape == (D,)
553
  if bias is not None:
554
  assert bias.shape == (D,)
555
+
556
  dx = torch.empty_like(x) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
557
  dg = torch.empty_like(g) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
558
  dresidual_in = torch.empty_like(x) if has_residual and dx.dtype != x.dtype else None
559
  y = torch.empty(T, D, dtype=dy.dtype, device=dy.device) if recompute_output else None
560
 
 
561
  MAX_FUSED_SIZE = 65536 // x.element_size()
562
  BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
563
  if D > BD:
564
  raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
565
+ # cap the program count for short sequences.
 
 
566
  NS = min(get_multiprocessor_count(x.device.index), T)
567
  BS = math.ceil(T / NS)
568
 
 
571
  grid = (NS,)
572
 
573
  if D <= 512:
574
+ # bucket token counts to limit autotuning across sequence lengths.
 
 
575
  NB = triton.cdiv(T, 2048 * 32)
576
 
577
  layer_norm_gated_bwd_kernel[grid](
 
590
  mean=mean,
591
  rstd=rstd,
592
  T=T,
 
593
  BS=BS,
594
+ D=D,
595
  BD=BD,
596
  NB=NB,
597
  ACTIVATION=activation,
 
599
  STORE_DRESIDUAL=dresidual_in is not None,
600
  )
601
  else:
602
+ layer_norm_gated_bwd_kernel_row[grid](
603
  x=x,
604
  g=g,
605
  w=weight,
 
615
  mean=mean,
616
  rstd=rstd,
617
  T=T,
 
618
  BS=BS,
619
+ D=D,
620
  BD=BD,
621
  ACTIVATION=activation,
622
  IS_RMS_NORM=is_rms_norm,
 
624
  )
625
  dw = dw.sum(0).to(weight.dtype) if weight is not None else None
626
  db = db.sum(0).to(bias.dtype) if bias is not None else None
627
+ # reuse dx when the residual gradient has the same dtype.
628
  if has_residual and dx.dtype == x.dtype:
629
  dresidual_in = dx
630
  return (dx, dg, dw, db, dresidual_in) if not recompute_output else (dx, dg, dw, db, dresidual_in, y)
 
648
  ):
649
  x_shape_og = x.shape
650
  g_shape_og = g.shape
651
+
652
  x = x.reshape(-1, x.shape[-1])
653
  g = g.reshape(-1, g.shape[-1])
654
  if residual is not None:
 
738
  ):
739
  x_shape_og = x.shape
740
  g_shape_og = g.shape
741
+
742
  x = x.reshape(-1, x.shape[-1])
743
  g = g.reshape(-1, g.shape[-1])
744
  if residual is not None:
 
760
  linear_weight = linear_weight.to(dtype)
761
  linear_bias = linear_bias.to(dtype) if linear_bias is not None else None
762
  out = F.linear(y.to(linear_weight.dtype), linear_weight, linear_bias)
763
+ # recompute y in backward to save memory.
764
  ctx.save_for_backward(residual_out, g, norm_weight, norm_bias, linear_weight, mean, rstd)
765
  ctx.x_shape_og = x_shape_og
766
  ctx.g_shape_og = g_shape_og
 
828
  residual_in_fp32: bool = False,
829
  eps: float = 1e-6,
830
  ):
831
+ return LayerNormGatedFunction.apply(x, g, weight, bias, activation, residual, eps, prenorm, residual_in_fp32, False)
 
 
 
 
 
 
 
 
 
 
 
832
 
833
 
834
  def rms_norm_gated(
 
842
  residual_in_fp32: bool = False,
843
  eps: float = 1e-6,
844
  ):
845
+ return LayerNormGatedFunction.apply(x, g, weight, bias, activation, residual, eps, prenorm, residual_in_fp32, True)
 
 
 
 
 
 
 
 
 
 
 
846
 
847
 
848
  def layer_norm_swish_gate_linear(
 
909
  eps: float = 1e-5,
910
  device: torch.device | None = None,
911
  dtype: torch.dtype | None = None,
912
+ ) -> None:
913
  factory_kwargs = {"device": device, "dtype": dtype}
914
  super().__init__()
915
 
 
952
  residual: torch.Tensor | None = None,
953
  prenorm: bool = False,
954
  residual_in_fp32: bool = False,
955
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
956
  return layer_norm_gated(
957
+ x=x,
958
+ g=g,
959
+ weight=self.weight,
960
+ bias=self.bias,
961
+ activation=self.activation,
962
  residual=residual,
 
963
  prenorm=prenorm,
964
  residual_in_fp32=residual_in_fp32,
965
+ eps=self.eps,
966
  )
967
 
968
 
 
975
  activation: str = "swish",
976
  device: torch.device | None = None,
977
  dtype: torch.dtype | None = None,
978
+ ) -> None:
979
  factory_kwargs = {"device": device, "dtype": dtype}
980
  super().__init__()
981
 
 
1015
  residual: torch.Tensor | None = None,
1016
  prenorm: bool = False,
1017
  residual_in_fp32: bool = False,
1018
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1019
  return rms_norm_gated(
1020
+ x=x,
1021
+ g=g,
1022
+ weight=self.weight,
1023
+ bias=self.bias,
1024
+ activation=self.activation,
1025
  residual=residual,
 
1026
  prenorm=prenorm,
1027
  residual_in_fp32=residual_in_fp32,
1028
+ eps=self.eps,
1029
  )
1030
 
1031
 
 
1038
  eps: float = 1e-5,
1039
  device: torch.device | None = None,
1040
  dtype: torch.dtype | None = None,
1041
+ ) -> None:
1042
  super().__init__(
1043
  hidden_size=hidden_size,
1044
  elementwise_affine=elementwise_affine,
 
1057
  eps: float = 1e-5,
1058
  device: torch.device | None = None,
1059
  dtype: torch.dtype | None = None,
1060
+ ) -> None:
1061
+ super().__init__(hidden_size=hidden_size, elementwise_affine=elementwise_affine, eps=eps, device=device, dtype=dtype)
 
 
 
 
 
 
1062
 
1063
 
1064
  class FusedLayerNormGatedLinear(nn.Module):
 
1069
  eps: float = 1e-5,
1070
  device: torch.device | None = None,
1071
  dtype: torch.dtype | None = None,
1072
+ ) -> None:
1073
  factory_kwargs = {"device": device, "dtype": dtype}
1074
  super().__init__()
1075
 
 
1106
  residual: torch.Tensor | None = None,
1107
  prenorm: bool = False,
1108
  residual_in_fp32: bool = False,
1109
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1110
  return layer_norm_swish_gate_linear(
1111
+ x=x,
1112
+ g=g,
1113
+ norm_weight=self.weight,
1114
+ norm_bias=self.bias,
1115
+ linear_weight=weight,
1116
+ linear_bias=bias,
1117
  residual=residual,
 
1118
  prenorm=prenorm,
1119
  residual_in_fp32=residual_in_fp32,
1120
+ eps=self.eps,
1121
  )
1122
 
1123
 
 
1129
  eps: float = 1e-5,
1130
  device: torch.device | None = None,
1131
  dtype: torch.dtype | None = None,
1132
+ ) -> None:
1133
+ super().__init__(hidden_size=hidden_size, elementwise_affine=elementwise_affine, eps=eps, device=device, dtype=dtype)
 
 
 
 
 
 
1134
 
1135
 
1136
  class FusedRMSNormGatedLinear(nn.Module):
1137
  def __init__(
1138
  self,
1139
+ hidden_size: int,
1140
  elementwise_affine: bool = True,
1141
  eps: float = 1e-5,
1142
  device: torch.device | None = None,
1143
  dtype: torch.dtype | None = None,
1144
+ ) -> None:
1145
  factory_kwargs = {"device": device, "dtype": dtype}
1146
  super().__init__()
1147
 
 
1177
  residual: torch.Tensor | None = None,
1178
  prenorm: bool = False,
1179
  residual_in_fp32: bool = False,
1180
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1181
  return rms_norm_swish_gate_linear(
1182
+ x=x,
1183
+ g=g,
1184
+ norm_weight=self.weight,
1185
+ norm_bias=self.bias,
1186
+ linear_weight=weight,
1187
+ linear_bias=bias,
1188
  residual=residual,
 
1189
  prenorm=prenorm,
1190
  residual_in_fp32=residual_in_fp32,
1191
+ eps=self.eps,
1192
  )
1193
 
1194
 
 
1200
  eps: float = 1e-5,
1201
  device: torch.device | None = None,
1202
  dtype: torch.dtype | None = None,
1203
+ ) -> None:
1204
+ super().__init__(hidden_size=hidden_size, elementwise_affine=elementwise_affine, eps=eps, device=device, dtype=dtype)
1205
+
1206
+
1207
+ __all__ = [
1208
+ 'FusedLayerNormGated',
1209
+ 'FusedLayerNormGatedLinear',
1210
+ 'FusedLayerNormSwishGate',
1211
+ 'FusedLayerNormSwishGateLinear',
1212
+ 'FusedRMSNormGated',
1213
+ 'FusedRMSNormGatedLinear',
1214
+ 'FusedRMSNormSwishGate',
1215
+ 'FusedRMSNormSwishGateLinear',
1216
+ 'layer_norm_gated',
1217
+ 'layer_norm_gated_bwd',
1218
+ 'layer_norm_gated_fwd',
1219
+ 'layer_norm_swish_gate_linear',
1220
+ 'rms_norm_gated',
1221
+ 'rms_norm_swish_gate_linear',
1222
+ ]