AntonV HF Staff commited on
Commit
2858087
·
verified ·
1 Parent(s): 93e66b1

Uploaded using `kernel-builder`.

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. build/torch-cuda/__init__.py +10 -0
  2. build/torch-cuda/_ops.py +38 -0
  3. build/torch-cuda/fla/__init__.py +26 -0
  4. build/torch-cuda/layers.py +21 -0
  5. build/torch-cuda/metadata.json +307 -0
  6. build/torch-cuda/modules/__init__.py +52 -0
  7. build/torch-cuda/modules/activations.py +1205 -0
  8. build/torch-cuda/modules/backends/__init__.py +17 -0
  9. build/torch-cuda/modules/backends/triton_ascend/__init__.py +422 -0
  10. build/torch-cuda/modules/backends/triton_ascend/activations.py +931 -0
  11. build/torch-cuda/modules/backends/triton_ascend/causal_conv1d.py +1175 -0
  12. build/torch-cuda/modules/backends/triton_ascend/fused_cross_entropy.py +469 -0
  13. build/torch-cuda/modules/backends/triton_ascend/fused_kl_div.py +188 -0
  14. build/torch-cuda/modules/backends/triton_ascend/fused_linear_cross_entropy.py +347 -0
  15. build/torch-cuda/modules/backends/triton_ascend/grpo.py +266 -0
  16. build/torch-cuda/modules/backends/triton_ascend/layernorm.py +355 -0
  17. build/torch-cuda/modules/backends/triton_ascend/rotary.py +211 -0
  18. build/torch-cuda/modules/conv/__init__.py +19 -0
  19. build/torch-cuda/modules/conv/causal_conv1d.py +129 -0
  20. build/torch-cuda/modules/conv/cp/__init__.py +13 -0
  21. build/torch-cuda/modules/conv/cp/ops.py +258 -0
  22. build/torch-cuda/modules/conv/cuda/__init__.py +14 -0
  23. build/torch-cuda/modules/conv/cuda/ops.py +233 -0
  24. build/torch-cuda/modules/conv/long_conv.py +172 -0
  25. build/torch-cuda/modules/conv/short_conv.py +250 -0
  26. build/torch-cuda/modules/conv/triton/__init__.py +24 -0
  27. build/torch-cuda/modules/conv/triton/kernels.py +683 -0
  28. build/torch-cuda/modules/conv/triton/ops.py +424 -0
  29. build/torch-cuda/modules/convolution.py +42 -0
  30. build/torch-cuda/modules/feature_map.py +315 -0
  31. build/torch-cuda/modules/fused_bitlinear.py +638 -0
  32. build/torch-cuda/modules/fused_cross_entropy.py +459 -0
  33. build/torch-cuda/modules/fused_kl_div.py +372 -0
  34. build/torch-cuda/modules/fused_linear_cross_entropy.py +767 -0
  35. build/torch-cuda/modules/fused_norm_gate.py +1245 -0
  36. build/torch-cuda/modules/grpo.py +421 -0
  37. build/torch-cuda/modules/l2norm.py +288 -0
  38. build/torch-cuda/modules/l2warp.py +51 -0
  39. build/torch-cuda/modules/layernorm.py +1472 -0
  40. build/torch-cuda/modules/layernorm_gated.py +535 -0
  41. build/torch-cuda/modules/mlp.py +141 -0
  42. build/torch-cuda/modules/parallel.py +44 -0
  43. build/torch-cuda/modules/rotary.py +519 -0
  44. build/torch-cuda/modules/token_shift.py +573 -0
  45. build/torch-cuda/modules/token_shift_cp.py +229 -0
  46. build/torch-cuda/ops/__init__.py +91 -0
  47. build/torch-cuda/ops/abc/__init__.py +12 -0
  48. build/torch-cuda/ops/abc/chunk.py +1119 -0
  49. build/torch-cuda/ops/abc/naive.py +99 -0
  50. build/torch-cuda/ops/attn/__init__.py +14 -0
build/torch-cuda/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
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
4
+
5
+
6
+ __all__ = [
7
+ "layers",
8
+ "chunk_gated_delta_rule", "fused_recurrent_gated_delta_rule",
9
+ "chunk_kda", "fused_recurrent_kda",
10
+ ]
build/torch-cuda/_ops.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def get_backend() -> str:
4
+ """Detect the backend by inspecting torch."""
5
+ import torch
6
+
7
+ if hasattr(torch, "neuron"):
8
+ # Needs to be sorted before specific Torch builds, since Neuron
9
+ # extension can be loaded into e.g. CUDA Torch builds.
10
+ return "neuron"
11
+ elif torch.version.cuda is not None:
12
+ return "cuda"
13
+ elif torch.version.hip is not None:
14
+ return "rocm"
15
+ elif torch.backends.mps.is_available():
16
+ return "metal"
17
+ elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
18
+ return "xpu"
19
+ else:
20
+ return "cpu"
21
+
22
+
23
+ def _find_ops_name() -> str:
24
+ kernel_name = "fla"
25
+ unique_id = "3fe4aab"
26
+ backend = get_backend()
27
+ return f"_{kernel_name}_{backend}_{unique_id}"
28
+
29
+
30
+ _OPS_NAME = _find_ops_name()
31
+
32
+ ops = getattr(torch.ops, _OPS_NAME)
33
+
34
+ def add_op_namespace_prefix(op_name: str) -> str:
35
+ """
36
+ Prefix op by namespace.
37
+ """
38
+ return f"{_OPS_NAME}::{op_name}"
build/torch-cuda/fla/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch-cuda/layers.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch.nn as nn
2
+
3
+ from .modules.fused_norm_gate import rms_norm_gated
4
+
5
+
6
+ class FusedRMSNormGated(nn.Module):
7
+ def forward(self, hidden_states, gate=None):
8
+ return rms_norm_gated(
9
+ hidden_states,
10
+ gate,
11
+ self.weight,
12
+ None, # bias
13
+ self.activation,
14
+ residual=None,
15
+ eps=self.eps,
16
+ prenorm=False,
17
+ residual_in_fp32=False,
18
+ )
19
+
20
+
21
+ __all__ = ["FusedRMSNormGated"]
build/torch-cuda/metadata.json ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "fla",
3
+ "id": "_fla_cuda_3fe4aab",
4
+ "version": 1,
5
+ "license": "MIT",
6
+ "python-depends": [
7
+ "einops"
8
+ ],
9
+ "backend": {
10
+ "type": "cuda"
11
+ },
12
+ "digest": {
13
+ "algorithm": "sha256",
14
+ "files": {
15
+ "__init__.py": "+xNtg61+cXuUnGoNyG/jVOEEG4VhKgccGXktjfEkykA=",
16
+ "_ops.py": "5604/IYHp3cW4mhYvBRXPvsI/hs5cPmXQVE+hUc1m/4=",
17
+ "fla/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY=",
18
+ "layers.py": "UUyLiUiIY5vj9tmJwcOZN/zqjs1zi/w8I6+72KKnFP8=",
19
+ "modules/__init__.py": "6ZjjRMt6Rf+2OegXDC8w58RD1IW3SBK+dxBoZa4Uohc=",
20
+ "modules/activations.py": "Lp7xLUf7r785nt93NFhqoSQM+G7AK2e6kviE2/0TRJs=",
21
+ "modules/backends/__init__.py": "l0tLqWyPFwwDRTdnHZYBzoux6J/LOFveJ53KREAKKDk=",
22
+ "modules/backends/triton_ascend/__init__.py": "vbU1UZwQDjVfF75INGYuWlIhuUTzx2TuZHIvme/QSMw=",
23
+ "modules/backends/triton_ascend/activations.py": "eqE/iXLZZEan7XHSa9ziz7mWNYwxTC5kIcJiwDwRr/Y=",
24
+ "modules/backends/triton_ascend/causal_conv1d.py": "VRxcTWPBB221dgd0+eNHEzCi9Echi6B2XOINtNOjHsw=",
25
+ "modules/backends/triton_ascend/fused_cross_entropy.py": "RITRIWoSOKhG8Mz7odkko27gESEd8M48ICc1IXRSVPg=",
26
+ "modules/backends/triton_ascend/fused_kl_div.py": "jb7XzKT6AKmXl8NxjZ4/SNofjdD3ioi4Df/l4KsIAtI=",
27
+ "modules/backends/triton_ascend/fused_linear_cross_entropy.py": "Z8/wQfPzJTg9ZICxdsABldRPsMa+mUZUTHhCJS2OeQc=",
28
+ "modules/backends/triton_ascend/grpo.py": "Jy+RkxbJ4RgJsEbk0xNgnw/D3Gj/P+6xDaMfx3I7DPE=",
29
+ "modules/backends/triton_ascend/layernorm.py": "7uKqeIraFdvcAVR0g6CLw+NJDCX8cLRMjNGHoFx1AnM=",
30
+ "modules/backends/triton_ascend/rotary.py": "+CmvapJJ3AOlwHbRxxSEIeVDjmlFPRno+5aqdfAHznI=",
31
+ "modules/conv/__init__.py": "WZKA4OJWHEN0bg2Yl2i2dvfkSp9GZKaD65abY8pSKbw=",
32
+ "modules/conv/causal_conv1d.py": "sHN5nlzDzSQwgo40OPt9lzcLRzZZnPCFvS/pmfK8L9k=",
33
+ "modules/conv/cp/__init__.py": "BKKfEjTjwyEAwDu66avCFolqnmrv9qHHNvxjfOHi3fY=",
34
+ "modules/conv/cp/ops.py": "pv7PGhmkfBsFt4tY8Hd0UAHzUPqp6DhgjRJmUujWitU=",
35
+ "modules/conv/cuda/__init__.py": "q35Kd12ITYtHp5JtaAhQJ6/YCEY95RWWveKoFPJ/VCU=",
36
+ "modules/conv/cuda/ops.py": "YE2httgrnIB1iSE5Uw9h1wtnq0UtybPuouF3z2reUb0=",
37
+ "modules/conv/long_conv.py": "h+wI2DdlBSQIZC335mwgBsjxGbByGaklb6Nv4WcQMMg=",
38
+ "modules/conv/short_conv.py": "DdAbuCpegRP32xEECOQQ4Ty3gWAOiFUzJLCD2mxcU1w=",
39
+ "modules/conv/triton/__init__.py": "fDLG2FdP68jqqsBxNdKifAIEjmvGzXNrLm/xDMrfbvg=",
40
+ "modules/conv/triton/kernels.py": "EK+wcpRMAGXKGmFCrZzxEeuZJmWvzUqxdTBYLwrpFT8=",
41
+ "modules/conv/triton/ops.py": "jIxuam5Ch5U3ErbT8D6Zn/5FqBoTq15kcJ9BGMD8exU=",
42
+ "modules/convolution.py": "WREsoV3gCce7rMEdpuEsyBgvmfbhk2IFdpUrr4TMtBw=",
43
+ "modules/feature_map.py": "sfoRvsdgvhtWkKuMYE/f1c7eyINwEaZ9TfItktWD+RE=",
44
+ "modules/fused_bitlinear.py": "lWTe4Ym60A+rHZTdQ6cZqa1Cje5vG/xMWvzLSAzLpQc=",
45
+ "modules/fused_cross_entropy.py": "si+ZNK/vu/TERnt/v2/Tm+ZBfn3poUoPDDTuJmrEuYM=",
46
+ "modules/fused_kl_div.py": "IDl78EMB9KXWmbV2cflnbXmcQpzdoqkwlEj4tvAPsbA=",
47
+ "modules/fused_linear_cross_entropy.py": "SruIxSe0Hhlf7n0upOldvn9HwOfL6XQ42N6qGZC6aOA=",
48
+ "modules/fused_norm_gate.py": "LAOhNgUSEiIU3Y5Ya5EHRjMAxHT0StODm04AUcL4Nys=",
49
+ "modules/grpo.py": "JPZnSGESIMmHDVkfYDSUzbm1qb2vY6dmDmFvSMI53RU=",
50
+ "modules/l2norm.py": "LchUl4mEXyx2cgHPuNgUhkorbNeuN+P7SfJyGXX0OyI=",
51
+ "modules/l2warp.py": "5oJlj6JRXBGe5ZANNnUA8GLIjlzXCnddtz2lwgKn6WA=",
52
+ "modules/layernorm.py": "1QhCZYF7pFRmZTxCWC55oG1uB6Lak5RXYca8MF5q5SA=",
53
+ "modules/layernorm_gated.py": "s/c/gRsZ3O6jXwY3b9owPaN1QAoOvsSevWuW2EXuJjE=",
54
+ "modules/mlp.py": "q09Zw5Bttqq27hnlybbQ9ncr0jSw1UCrJH2Ewh9roV4=",
55
+ "modules/parallel.py": "y3ZBLQp3FeDfBlHYmSerllqbfE3RgzX4k26lA15DPeg=",
56
+ "modules/rotary.py": "qVsb20GcFf9RolZ3dChWzEi5S9rc5UaMdC3dlrhNUj4=",
57
+ "modules/token_shift.py": "IWlvUPZ/Zm4IKW0dWSsx4p4r+ovbpPHQhk5Dp09P5Wg=",
58
+ "modules/token_shift_cp.py": "Zvaa6d/GiuljKqNgLdkjX0vysktpIVV8KMSTOjdG3Hw=",
59
+ "ops/__init__.py": "PsKzHYHoje01DfrVdltffiZFdS5CTVps5xl9dXr2RHo=",
60
+ "ops/abc/__init__.py": "WwLiIkYi3xJ5RiG/bOCNNRaeeqmMuIPT6n1g23qfpDg=",
61
+ "ops/abc/chunk.py": "sqvLGP2r+frjpL2qWK8GHCoWWPU5Df8IT5y20oaTZTE=",
62
+ "ops/abc/naive.py": "AZ3uz9eDvycW3gTwZuv9J4k86KndfQMZiLBFyPmzsLw=",
63
+ "ops/attn/__init__.py": "gU/67mJonbeXpzfxgi8K7HIG2a8Xpjd0gOuCk/Pu9EI=",
64
+ "ops/attn/backends/__init__.py": "3xcuWH+iLBPRb3g4ZkyXBXm8wJFxQK4GHdUpmK8Nh88=",
65
+ "ops/attn/decoding.py": "ScKiTKcbl4kcnYDNz6jXd2a1qB5ymDcz8YVkotRnO9Y=",
66
+ "ops/attn/naive.py": "nBsWqmBWOMQdaQYvdFejDLoX0Pei9W8oMPrHZoCCin0=",
67
+ "ops/attn/parallel.py": "+Vkkjy4OIwR+7SXBf6fCJqrPg740qc6a1+21RSNt9Cs=",
68
+ "ops/attnres/__init__.py": "/ljBk1VNFjDa9XABPXqIiQ05vUdzRV6Dbx/KkQihrWs=",
69
+ "ops/attnres/fused.py": "Q8RKiwlUtFZkHELCLV3wtQGf1cVDPoDNMK1waEwbxFQ=",
70
+ "ops/attnres/naive.py": "nV4WPz/vfRwfRsIzF7pKV95fEx2dQP4BrrPoQCh0a0M=",
71
+ "ops/backends/__init__.py": "3DU4e6t7dmPuZLlHjPnt4q/MAtDXtaMmAcSY4ycyytY=",
72
+ "ops/based/__init__.py": "qdBLUigpgjQaIN3HTtq57uxoqAaBnQkZbFiEIf18Rvk=",
73
+ "ops/based/fused_chunk.py": "Evgk49vwK6VxWWmw3KdwhiUV5N8RF/PcxZP8J1dlYR8=",
74
+ "ops/based/naive.py": "YWIFSLeslp2ruyQDz/uCr5V4ao2cyRyeQH1wiVLMB8U=",
75
+ "ops/based/parallel.py": "7hNqqI2UIeJNebDTF7RwW+Q9ST15DwMxJugiaVO9s/k=",
76
+ "ops/comba/__init__.py": "FBKonbvPPFHY7an2Auh1sXVJLbcgwX8aAD5fxi2oecY=",
77
+ "ops/comba/chunk.py": "o+3soCUQ27uZXe+YOZpYF1/aiLFEyTdbb+bmetY6O2c=",
78
+ "ops/comba/fused_recurrent.py": "ujg1r9D2Jx+/sgli1msfkXoiCZb2gX9dL/OpOca3kGs=",
79
+ "ops/comba/naive.py": "QH967c5rxlSSDs6J6jRtLjZ3iLiwgAYgY9+0m9rgD18=",
80
+ "ops/comba/utils.py": "rcW3h8gjoEzSoLgY1HkrIwsn9XKEYtz274CGqr8wWzU=",
81
+ "ops/comba/wy_fast.py": "EsJfyG3HDGY9rgJ97cn2SAq1DeqnXdgj5mzdOlWtz2I=",
82
+ "ops/common/__init__.py": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
83
+ "ops/common/backends/__init__.py": "aEhY8S2W0I6qbjedaCYjqLt4VaNumWM+bjKtPEJSAN0=",
84
+ "ops/common/backends/intracard.py": "MrZzCaTWZbmwp7zOF8HAaFHaF/kAFYXTEiyFwHnEgNU=",
85
+ "ops/common/backends/tilelang/__init__.py": "fG3v0hLT2R2HkFAGZ3Z/KrA5uuCIBcILI4QWui69gqM=",
86
+ "ops/common/backends/tilelang/chunk_bwd.py": "5zXyBzqIXRgoZdZ8zjLNEIz3jBksHpaVxnqUk66ckjQ=",
87
+ "ops/common/backends/tilelang/parallel_attn_bwd.py": "4OlC0Vj9N1auoz9ZCUeOcBt8QSg4eTByDUnc9BPpZ7k=",
88
+ "ops/common/backends/tilelang/parallel_attn_fwd.py": "7INMyGs1d3H8BKcjRnMvu/NHMm9VeNFTETR5z7ZO15c=",
89
+ "ops/common/chunk_delta_h.py": "RvwbMKgbr6O8HVWzPRSJ0B90gjpJKkxTqoRuxOKa/fU=",
90
+ "ops/common/chunk_h.py": "imyz71p9AD6nOtStpN+zzgE0yVbGnq4DO3FitrO3hvc=",
91
+ "ops/common/chunk_h_parallel.py": "YGNR5a+eC7UShlVAvQF3fSLK+HXoP2Nc06H3p/PyzVg=",
92
+ "ops/common/chunk_h_split.py": "zYoWf1Li3Q7MFPusp5BRf1F+OuGzxxTeiZ3hdunYnCY=",
93
+ "ops/common/chunk_o.py": "14g/dwOOarecHQQbiQ0msu7e+hL4NdqSd2HPPrgGHoM=",
94
+ "ops/common/chunk_scaled_dot_kkt.py": "MyoKTPKUUZlOzdC4FdP4etL6pPBGzaNL5DJQyH3603E=",
95
+ "ops/common/fused_chunk.py": "nKIxBC8UD00KuY7WGFysC8P4ELNLEJhHmT60VcqyHbc=",
96
+ "ops/common/fused_recurrent.py": "RnQdVD59iOcRa8sSK4XcQT59RhZJr42YplDaQrDt97o=",
97
+ "ops/common/gate.py": "ExEV4w/h/y44Qb6L1cwOFEsMutGjs/t338KimPVjeHM=",
98
+ "ops/common/intracard_cp.py": "R+wNrYC2m0gUn0jr5CpsxpCIVRCRkRnFXjBus9XbDE8=",
99
+ "ops/cp/__init__.py": "uud2WkodJvH5U5DjBa5oSLJtZ1G8O4D4XEuspXyC9lA=",
100
+ "ops/cp/chunk_delta_h.py": "+pPn7+hNImlTrCZLHNpaJKabA+OaoNXofYbX2F4fTD0=",
101
+ "ops/cp/comm.py": "lqyQDwmK19vowTtGJGO+6SEn/neFFE0G8xFvlxLOmb0=",
102
+ "ops/cp/context.py": "N/p87Th0z6m2cGhP6icCN7rEg3QPVua8ymP8oDD4qy0=",
103
+ "ops/delta_rule/__init__.py": "1gIkBajhtbVwuJOgPQ+FYYdq4tYgNgib+veOnont+N4=",
104
+ "ops/delta_rule/chunk.py": "r1b6g3/4OO1oDZ8n/MMyzagYHs0c5FPKU8//7KskU8w=",
105
+ "ops/delta_rule/fused_chunk.py": "2NiUBRAl98YgRVGr2uzYJV22pQW3WNOe6euTX1Mdqyw=",
106
+ "ops/delta_rule/fused_recurrent.py": "aerY1sPJ0y6IknruBVOwbu3v/pLz2PjP3aNEzsArp5M=",
107
+ "ops/delta_rule/naive.py": "IK4DL04IAZtCmMyfzyw5fYcfqsD9HxgCQ9n2makZKWg=",
108
+ "ops/delta_rule/parallel.py": "GMhpAZoEDuzyIjK0Qcpu+ku528WnLgDUujcsoRnXU6o=",
109
+ "ops/delta_rule/wy_fast.py": "O0UuKiOw3mImzRQHFLKHifSewFAoqRJ9i9r0puiX9fw=",
110
+ "ops/deltaformer/__init__.py": "51ZmNFoHy3P0lVn/2EHKu1QS2BMC6h+hnAwSIr9szYg=",
111
+ "ops/deltaformer/invcum.py": "ren181AxlHBv2MoZIHmSGMmJ0sW6HDb3t8fqG7gDELU=",
112
+ "ops/deltaformer/naive.py": "8b88rviKkDZpW+F9GwgEkVkwuSioECwgRJX//CVksnI=",
113
+ "ops/deltaformer/parallel.py": "0ryf3d3zmD53IorXkXXJnXQIPEg8+3PXsXMbjvzWTbA=",
114
+ "ops/dsa/__init__.py": "ZNyws889LPBiY8gwB5zTs2vQwJYV8bX5jg6Cldkkq9s=",
115
+ "ops/dsa/naive.py": "lKgrdKOoE/qCdF4DtrI0uEkRdB28kUYbtn8hO63fzyo=",
116
+ "ops/forgetting_attn/__init__.py": "A6ozg9zdEsz4lgUAXqEIUY8CeNp+5IZqYKOpChpj/A4=",
117
+ "ops/forgetting_attn/naive.py": "RDPXw01ZnhTMV125OFvEVeDDMht3BYj5gQI4oaT+T1M=",
118
+ "ops/forgetting_attn/parallel.py": "fcKF7Q84XXmeeKI2Tpv5XIfmxFovuXlmBov7Ie8OjKg=",
119
+ "ops/gated_delta_product/__init__.py": "0MAzYdxP0ODZjN4WNQ+3HLwigokyfMv15rXUpTX+qnw=",
120
+ "ops/gated_delta_product/chunk.py": "QNHYG+1YKzot3cQ2oWvzuv7nDoZjllEXEomCk11WUJM=",
121
+ "ops/gated_delta_product/chunk_deltaproduct_h.py": "5IvD8ZZxaN216Wj8dop1cwJE9P5rvZ+Si98qB6kC5DQ=",
122
+ "ops/gated_delta_product/chunk_deltaproduct_o.py": "nvMmsV+E9AG4Z9HA6UV69DdGMUYH2XcL8NAAubUtIHc=",
123
+ "ops/gated_delta_product/chunk_ref.py": "R1dHdPy4rU+lTQQKodAn9h2xPLxmwI2z0T6pRvh8Llo=",
124
+ "ops/gated_delta_product/naive.py": "Q907vtcavFpalFlttThkWSb/SaTa4vqAG+I/nWvKDno=",
125
+ "ops/gated_delta_rule/__init__.py": "Xt0hhHr3CECyZDmBIZakdufk3bvvIa/V/iBmpkYwpnw=",
126
+ "ops/gated_delta_rule/chunk.py": "DFEZWmp016p+65pnDynAMVOY+NsUTGOfVcXsAhQr1i4=",
127
+ "ops/gated_delta_rule/chunk_fwd.py": "DisrNyeiIB6SxcYZn5jsUnrbmkLzVbcqQubFI14qLsY=",
128
+ "ops/gated_delta_rule/fused_recurrent.py": "M7x3/U1Xnf5mndUc+lvGpzxu4KDNc2Sfevv+RVZZFNk=",
129
+ "ops/gated_delta_rule/gate.py": "H9JLPKjgZmorhyV+1IJFSMIa6x9admDn+EXXlwg167g=",
130
+ "ops/gated_delta_rule/naive.py": "0c8XmSNJ/T6Ur5mbIuPTqBvkotGIHOW3CjRXJXFm4Ms=",
131
+ "ops/gated_delta_rule/wy_fast.py": "ufSeCLwEQxwRS4pYiiNsIeXN3A6sLKgLdu1Fy/gkXoM=",
132
+ "ops/gated_oja_rule/__init__.py": "oShC/cL0jMy+xWR7YBV9QxkAdxt1xasXDCypGAU6Hgo=",
133
+ "ops/gated_oja_rule/chunk.py": "lq+5BXEaJLfL4dqqcJb4TmtyF9OrP9Wh+JpRfpdzd2s=",
134
+ "ops/gated_oja_rule/chunk_h.py": "kr6KBcPbFRrIQFBoQjmt1wsyvO1pyLeGW5v/k+NI87Q=",
135
+ "ops/gated_oja_rule/chunk_kkt.py": "N/sU3E2i5+jQBXnFuWyaUAiQkAgWUxNjO9lxdlpEkDA=",
136
+ "ops/gated_oja_rule/chunk_o.py": "4tPVh8KWj+D2onfXbIhnhaGWq35az5uJUwxTUbbRll0=",
137
+ "ops/gated_oja_rule/fused_recurrent.py": "yPfAcIXWBlr50lvK59bbF2qkhKWt40eT+EXx6exojK0=",
138
+ "ops/gated_oja_rule/wy_fast.py": "mS9GSN2RVUbEMkz2o1Hfi6j0nOhXtjLLSIccXkwmW3c=",
139
+ "ops/gdn2/__init__.py": "xiEsgvVEfHhKcurQqL7ZKGGzEBW2BtGAylE9vxoNmAk=",
140
+ "ops/gdn2/chunk.py": "zgJK/Mwhn1fA9PpvagLdy6yFuxo2c1Z8DmUY5FJp2kw=",
141
+ "ops/gdn2/chunk_bwd.py": "Ay8kHSsnyD1wxGE1YcEfV3d4L3yae/8bDBMReKPbVBw=",
142
+ "ops/gdn2/chunk_fwd.py": "7Z0d9SnKSby4yCAeXx5kKx6HIdYfaXl0ZhYDDAniClU=",
143
+ "ops/gdn2/chunk_intra.py": "UjnmvfEg9EB41jGA9YHcZT9R2oHpBQI4PCq1Dwj1ZiQ=",
144
+ "ops/gdn2/chunk_intra_token_parallel.py": "U1lf9FZ6mfW2iIm06YJ5qPX8IQeT4U/G+J7RlZoWutw=",
145
+ "ops/gdn2/fused_recurrent.py": "3QsJl80MRQYrQXVN9lmgw4cVD1hLHrQXg6xcIgU5Bz8=",
146
+ "ops/gdn2/naive.py": "amWAVAHrMA34W2KXYRoN0VjdGDQjzyvNXg8Ozd4+nno=",
147
+ "ops/gdn2/wy_fast.py": "DNH9FhRaZl4nqZ/YrxH2SqCgZdC77bkJ24sFIemnkRM=",
148
+ "ops/generalized_delta_rule/__init__.py": "cn6rKWEDrxWw428Dk6EEy4Y7nyWHpc8rtR+EbRO9ndA=",
149
+ "ops/generalized_delta_rule/dplr/__init__.py": "F8TbaiZeRKjbKhlI4XYopNd4bfzV10feNO06i32h2Vo=",
150
+ "ops/generalized_delta_rule/dplr/chunk.py": "qeR4srXoGT8NjNx93IQEyPGq3sqsK78CdOovbRwbntU=",
151
+ "ops/generalized_delta_rule/dplr/chunk_A_bwd.py": "MDoA0HOWDti16rAh8Q4D4RJonJkmIN04dCwUsHOM+sk=",
152
+ "ops/generalized_delta_rule/dplr/chunk_A_fwd.py": "wQSOE/PoqvjP6ugh3wSmbsst0ECSclxJEl5x/4NQ0KI=",
153
+ "ops/generalized_delta_rule/dplr/chunk_h_bwd.py": "hAISrlVF9QETkcoDAgEKXRY0w9DSDKdohqTXfkg421E=",
154
+ "ops/generalized_delta_rule/dplr/chunk_h_fwd.py": "vWtEs8L4DXX52UF5qt6Vuu/pOsHzKOFOIFtyezheWPY=",
155
+ "ops/generalized_delta_rule/dplr/chunk_o_bwd.py": "CuHt6fYI2g/6m0k2mj7JSLhCQ+zcKrMUYc2LOmKuUUs=",
156
+ "ops/generalized_delta_rule/dplr/chunk_o_fwd.py": "KOF8wWIEMIyhDfSL44bucPPsCp6u3AdYPlRDKK10KyQ=",
157
+ "ops/generalized_delta_rule/dplr/fused_recurrent.py": "ABaz+l4n77U9FrE3bdsGx/5jexAnvJ1cJcI/XJkeIac=",
158
+ "ops/generalized_delta_rule/dplr/naive.py": "4flLSZUtwaq5S/PH+fu04rzOM2+USdRX6Ofte4lanE8=",
159
+ "ops/generalized_delta_rule/dplr/wy_fast_bwd.py": "HiMBn/U+a0vSCOVmn29UYLylI5x26qqualdl4E6ipd0=",
160
+ "ops/generalized_delta_rule/dplr/wy_fast_fwd.py": "R+CamNa7b49LfvrNZQs4roi+TReWcLGvBcoT3DbhoLM=",
161
+ "ops/generalized_delta_rule/iplr/__init__.py": "6Vg8MTc+Byrnmx3HJz2yySi9nk2kyKCSg7aL4CMJ+GA=",
162
+ "ops/generalized_delta_rule/iplr/chunk.py": "BsCf/fiYn3wpS0eDPrR+mCrqsAihXD5v/QD2QkA5ATc=",
163
+ "ops/generalized_delta_rule/iplr/fused_recurrent.py": "nNo+ww/QmydOqBLnpmxXHAbANGDPTeWGjFI7nA9Vrw0=",
164
+ "ops/generalized_delta_rule/iplr/naive.py": "YotccLOueXwBcQys4ICmFAysSrX8fj9eRasXIpmpRMo=",
165
+ "ops/generalized_delta_rule/iplr/wy_fast.py": "SoTcF2asZEIAnZS1qR9PXHGkHwnBspjuUsz6RpuqNiU=",
166
+ "ops/gla/__init__.py": "PTUqqQlC4cXrhRhG/KRx+7X3m9WJKxGyzrIB/WJEXSo=",
167
+ "ops/gla/chunk.py": "B7reIP0UzTvYgK8Ds50r0uABlaucJWm70JVKKdYe1L0=",
168
+ "ops/gla/fused_chunk.py": "OjzKcjSQujVQ+Q+d1Lmg4tsTzZaK6xWs8AGuplRn5zo=",
169
+ "ops/gla/fused_recurrent.py": "ZIzMstFj8WAXagDzTlZo3hFZy0k+egHNg8Qr61sb4vw=",
170
+ "ops/gla/naive.py": "rEuH5bEAieya1CTvtZbebVFo2J5LdOX1ztL1WiDkZB0=",
171
+ "ops/gsa/__init__.py": "9uygbV6orNtaTkZl9Hz+aAp7EvT6RuffawfeFtuUNEE=",
172
+ "ops/gsa/chunk.py": "SsfwmTOpKRG2Fja/lArJkw1teaomk/Vgczjr2/Oa+l0=",
173
+ "ops/gsa/fused_recurrent.py": "K7qBWOmh0uL9CO7D3pSlrxKFHy1fobTUyTFV5p7okXg=",
174
+ "ops/gsa/naive.py": "rezNszsU38KD1iHBW+ErV/Z01T5LIPadEjJnWiXz9LI=",
175
+ "ops/hgrn/__init__.py": "69DMv7FQYvobQTXqOFtu7muAt1k1k9nvsFXcMAmjQmg=",
176
+ "ops/hgrn/chunk.py": "Br3GeHkw1EcOF/4X2BOvUTclhfLDZwkthIshoHTMMqg=",
177
+ "ops/hgrn/fused_recurrent.py": "KvO/5l/3wnJiT0jT0UA9IYxQhGHJy8jC7lYaOOyLM1w=",
178
+ "ops/hgrn/naive.py": "XKo+5zJ+G4elkgK5ARsoXm5WYkG1QidTKNIuPT4k588=",
179
+ "ops/kda/__init__.py": "JFZPAQH4eiYFbpBh7Xccrw2PbZoAtNve1wHqdSW0Wss=",
180
+ "ops/kda/backends/__init__.py": "pk1xLku49AblKggFmQjmGlr1oNqUgdzxggzzgx8yeZk=",
181
+ "ops/kda/backends/flashkda.py": "j2FmWHLh09/B1EE0SFrNw+OR7JFOJuD5O4x49bNpKRg=",
182
+ "ops/kda/backends/tilelang/__init__.py": "w+bbm7//9PdpfGGOTCOxsTwrC95ERRuYmxPROcEXHLc=",
183
+ "ops/kda/backends/tilelang/chunk_bwd_dqkg.py": "Fk1eT3M/KPFvfVRrJQ6C0158h5GaR72YK3tgSxkKoUo=",
184
+ "ops/kda/chunk.py": "LnoIH4MI8gHR8613fOKLqJGVNQkbtzjEJ0R7qX8/kQE=",
185
+ "ops/kda/chunk_bwd.py": "AahkP3AepxWsoyGCZHlwUGc7rGIHJh68Im5r/Zs3MK0=",
186
+ "ops/kda/chunk_fwd.py": "wUTWWQVWLRGeDj9e7dO03+0IosaRuWAfYqsl2KBy0ZA=",
187
+ "ops/kda/chunk_intra.py": "Ao7C9D/HzYJkiFxIL5s/LIlVE2YNgpL9yASJl5giPOs=",
188
+ "ops/kda/chunk_intra_token_parallel.py": "ENjjhDAR0cK2mqoU4q7QpBMPHmICgoLO06e4dEnd5hY=",
189
+ "ops/kda/fused_recurrent.py": "r6ZJMiLVmCRLpbYXS25mIX58KU2yKtDJqGwwHTXY0MY=",
190
+ "ops/kda/gate.py": "5056ezuomW2CNvPE04MraGJtf0gHvGqicxin1H0DICg=",
191
+ "ops/kda/naive.py": "YKMihdS2cGj/YztIu+irMQKAZtJPANJ+EhmaiPxz8BY=",
192
+ "ops/kda/wy_fast.py": "9pMMqS5E/l0mFV/Tyap1QKY39CB5Iy94/Yn4s9WzjUw=",
193
+ "ops/lightning_attn/__init__.py": "FPVVYNLjfO3GJQYIp+4HCQ4MdJtF8UZx5yktWDIatFA=",
194
+ "ops/lightning_attn/chunk.py": "kFjfxpUOVj61TnH0vXWZhDnw318suRLhtT2Qo50pkKM=",
195
+ "ops/lightning_attn/fused_recurrent.py": "SsJ+HC5opio7H9LsPWyrbnPOoK2EvOcxLuaq8Vp69GY=",
196
+ "ops/linear_attn/__init__.py": "3OeyduLmyi1manyo7FQz1ZLSoLgEXAKbHKn1uqDcK90=",
197
+ "ops/linear_attn/chunk.py": "5xqgOnPPA7nGPt/Rda4wOxSljFTD3Izw63VpuDLJNd8=",
198
+ "ops/linear_attn/fused_chunk.py": "MD6FycgZSdn8jV88UDvxpioPAw6hCxvsSq49qNIKpmo=",
199
+ "ops/linear_attn/fused_recurrent.py": "jUZ5gPuN2VngqbebHbN0lrIAOiZOYkxIku+C3Eh/9Vw=",
200
+ "ops/linear_attn/naive.py": "46BX2exbemO1c8XXVhmkz2m6H1ywTmrvT4/odStEA8g=",
201
+ "ops/linear_attn/utils.py": "7X94oxSkHAGSpk7SQ5izs5Q0rYEDZZzUV/RorFdzY1o=",
202
+ "ops/log_linear_attn/__init__.py": "qsAhfXSfoQBDmP6Li6+CjNd9ysMDGjcbYLonvcZcrjU=",
203
+ "ops/log_linear_attn/chunk.py": "NTaU1ErT6E0PzSt9JL9KayX8dQgLG++85PFwj3sRm1Q=",
204
+ "ops/log_linear_attn/naive.py": "xmXXuoIdEkTucVYJ/s4dTCvMWuJuO8KP0299J4jdtEU=",
205
+ "ops/mesa_net/__init__.py": "32w1RDjxggXJ9JobAR01dDnN2UZSG2Fui7qePyQT2J4=",
206
+ "ops/mesa_net/chunk.py": "GTupTJ0BI3Ix2giVi7sMA2ocs71yvZ4uuKvf8rkUqKQ=",
207
+ "ops/mesa_net/chunk_cg_solver_bwd.py": "MvXMHU8MSOTNG62zURV+iMpvkwEh4eCo7epde0DIvBc=",
208
+ "ops/mesa_net/chunk_cg_solver_fwd.py": "KG4SXzD6dZomzRmYmqaWtmBoYVgSk6Fxda8uXjJc5go=",
209
+ "ops/mesa_net/chunk_h_fwd.py": "F3AUgFAYjqUAxslqpjsv+zW1PjdxGnwaU/Qjh5tT0iw=",
210
+ "ops/mesa_net/chunk_h_kk_intra_bwd.py": "17Le7w0rK0MrLUROFQqcZ9yCQfxpRYMhe5o3dRHGIfo=",
211
+ "ops/mesa_net/chunk_h_kv_intra_bwd.py": "mQnvHOYVu3HMX6IA4rD2zl+9w5BOi2yzFsr0GSo5+Nw=",
212
+ "ops/mesa_net/chunk_h_kv_intra_bwd_separate.py": "FOrwfCVxiMtLYaR9JIBqzxExwDWMWPVuOkaxgHD1AgY=",
213
+ "ops/mesa_net/decoding_one_step.py": "ZFuKB5H06F+uDagQLPGiu+Qk/qpZxjRbKcnuqWtC6/0=",
214
+ "ops/mesa_net/naive.py": "zv3jT+71UegNb0JyIKnPv8OddyzHu4weEETtt0nJvzA=",
215
+ "ops/moba/__init__.py": "P7ARNbLLMsmTtxcxPk09NPmjcvHGwWCmooXPYvT6ANk=",
216
+ "ops/moba/parallel.py": "8qUN2zo88Pldh809UyDowBVj8kCNplsS00fIxLRdSyQ=",
217
+ "ops/nsa/__init__.py": "6Mw2zDl4Z74QXVjME1d88PMSKTSO/AYUa00lW/IK+eI=",
218
+ "ops/nsa/compression.py": "XjFqvEXvuIEeCBrJU+YzhWRH5UAkPGcUX4Tz/xx2M6Y=",
219
+ "ops/nsa/naive.py": "5A7lyoN39oK0Wc+Yzm/dQbNEtlaGmZfJsMDv8II+m7c=",
220
+ "ops/nsa/parallel.py": "BLaPXXsO8tcAJ79ltV3xwX/1qTganF2Nh+Lto/3zvyQ=",
221
+ "ops/nsa/utils.py": "lbhybLC0CqXTn6liY8d1sQfTdZ2Gl+++Y5Go53OX4xE=",
222
+ "ops/parallax/__init__.py": "g2pTOybF7cyoyaMAku1gpjV5MBY6s/Ll1X2NT6/w/YM=",
223
+ "ops/parallax/decode.py": "qHX0Le4hA66S2LLTcMa+p0VMB0uWvhYGI9urSPLrB4I=",
224
+ "ops/parallax/naive.py": "XEvAU0cSJtunA3ElSy4GFs6idarjK7Vb+xkrDKSp2OQ=",
225
+ "ops/parallax/parallel.py": "ZrLKoopSj60DSxmqbaS3MIf3AUyEpYh7evJPlTUN+AQ=",
226
+ "ops/path_attn/__init__.py": "eyvdV5LkkxztzDTDYAl862vhXITUOcMtF6D8n6n0O/E=",
227
+ "ops/path_attn/cumprod_householder_bwd.py": "EZ0t+c2hG8Z/0IvrVxMBLDgO7zLgJsI9Bc7L0WYgvh4=",
228
+ "ops/path_attn/cumprod_householder_fwd.py": "UF1wvSIMRHeJmPf5qbFuxbC+qCm3ipAfeAVf3IEOA6A=",
229
+ "ops/path_attn/intra_chunk_preprocess_bwd.py": "HMQYfpKCHEMmzomrzb60u3gJK419yOAg2kx9XolFf6U=",
230
+ "ops/path_attn/intra_chunk_preprocess_bwd_prepare.py": "6XIZztbrFYFR/7XJSIy10RxKid3+6dcZ5c+A6XL2fBs=",
231
+ "ops/path_attn/intra_chunk_preprocess_fwd.py": "U8P46fLq//0vDN0SiE3cwI4n6FbAO6/UGNhfOiXegRo=",
232
+ "ops/path_attn/naive.py": "4QsKiZUcF0q4IJbrku4Xu/IR3YBg+n6Epio7FAOW9lk=",
233
+ "ops/path_attn/parallel.py": "V1c/T2VKbs8MgNJ0AT2JA5ULpx49aLb5maoxizAbVjk=",
234
+ "ops/path_attn/parallel_path_bwd_inter_dkv.py": "6/EOBq9NQdKaCrEGwPwbi69vKbOoNCsKU1q2vRv7YFc=",
235
+ "ops/path_attn/parallel_path_bwd_inter_dqh.py": "fp1NEXRSVAqhkmGcvoNfQn2pt5dtD0plx6W0TGR2xDw=",
236
+ "ops/path_attn/parallel_path_bwd_intra.py": "+3QE/gzK3fAYqgo/WNHNC5LV8NJrL4+UHjDN6wOd15w=",
237
+ "ops/path_attn/parallel_path_fwd.py": "cx6fGpfZmEuxHhl81gsLOBAYa61ZnkI9usqTakOIV7A=",
238
+ "ops/path_attn/prepare_k_cache.py": "LqEoXyDlVfIaJQhrmpoS3pmetIaavAm+SmIm1NKFohQ=",
239
+ "ops/path_attn/transform_q.py": "uZ9u6JDzXFKkWiim4KUkXWoAHYKlSDK5L1fMBNFrEGY=",
240
+ "ops/rebased/__init__.py": "pawYkzOJ/mQY5Qj0Ibuq81P3KNgO9Lv6swOxCjlDlGs=",
241
+ "ops/rebased/naive.py": "XOrMn2Ohd7MDJ7QvP93dPfjv/6prnZW/ozxHtU0KqWQ=",
242
+ "ops/rebased/parallel.py": "+3WRG3oxV0T78e8HWkN32Q8KBe0V2GMQ/OpxowWfXFI=",
243
+ "ops/retention/__init__.py": "i6uHA1ndTR6T/fVfooceKo12wyBFGYLe1EAm0Mo/sg0=",
244
+ "ops/retention/chunk.py": "x0fX3U8KTjr9WLQK9+2C6Mn7cYNtACQ8RhFuUoCGpBw=",
245
+ "ops/retention/fused_chunk.py": "sAmIqFiH4IFM4yiM1G/pNvlsZxLtkORemYy5WW4i1pw=",
246
+ "ops/retention/fused_recurrent.py": "beES/zDbxCUWWWL9HSCc72a1+DWVvDKpBIsjKjkUet0=",
247
+ "ops/retention/naive.py": "MG107SmOiRKLTZ8ziqaWN85nbgIPj/GqNJJ7KMt5Tag=",
248
+ "ops/retention/parallel.py": "gTnLcV5w17fZAOJyJttw52LGAe9XlkuS++lMGTZ3vRs=",
249
+ "ops/rwkv4/__init__.py": "GnWakdtZiQJ0+fTYxqqIyosoPwjH6Yp8P1AbpHIDnts=",
250
+ "ops/rwkv4/fused_recurrent.py": "0fvq/4+NZMq6tzq7oHMUCaPg10qOzteSKLj5cmQKhvk=",
251
+ "ops/rwkv6/__init__.py": "wPYgC6wG+xc4I2vessGhv1EaZ4tL5yuVQVSwi1rEEUk=",
252
+ "ops/rwkv6/chunk.py": "2VMAk6p0tKEudhieXLIg2bYg+89JbsGUiuhFpfvx6C8=",
253
+ "ops/rwkv6/chunk_naive.py": "zyj0Og15jGxNMzmlD9FoEdf/odzu3tqio/jiceVMNk4=",
254
+ "ops/rwkv6/fused_recurrent.py": "wMTP/Y17ayzQXxXw+fN8P/FB5KGefLB4bB0wJ9kDg6Q=",
255
+ "ops/rwkv6/recurrent_naive.py": "/lCgua1vSaJDqA4tOWWUaVxol5Axi3cOcjI+mJj7OAM=",
256
+ "ops/rwkv7/__init__.py": "dPKBISLNnwlKb+xp/8ejsc2peC9gKQ3NedtMT5wQ1sE=",
257
+ "ops/rwkv7/channel_mixing.py": "+XrwuE2RjLxjsiKrdhssskBTvW/zL9RpyFp85uv5J5A=",
258
+ "ops/rwkv7/chunk.py": "9+rVTjACj0FP2Mnwo30DHWIHkpZG6YfYN4gE6891Oa0=",
259
+ "ops/rwkv7/fused_addcmul.py": "uUzh8wc75jBLJhCagQfeE/yor25Jw34d9kQQNsQWg04=",
260
+ "ops/rwkv7/fused_k_update.py": "CETn2jde9U8K8kfVihcyjARgpcDT9nN09qw9wM1QQac=",
261
+ "ops/rwkv7/fused_recurrent.py": "nnNrjgLS5nv9plvU/MuMjt4/vDcGD0Wax9Dfigvwmp8=",
262
+ "ops/rwkv7/gate_output_correction.py": "Ah6eqJrlTgFGaGI711+pMmWlxCOpvHrExx8H9U+nO0Q=",
263
+ "ops/simple_gla/__init__.py": "sSyIh8oSfhyZI+VnYUM/uhIp+HL+gDcQBReLn/nJ5OQ=",
264
+ "ops/simple_gla/chunk.py": "eA/L+T+42GiIhYu1oG0fM66QFGnzW9Uz8oc2SnLTKig=",
265
+ "ops/simple_gla/fused_chunk.py": "eCALWbi61vaG6zPewlPmATvjlidM9lso9xQC8H4xAlM=",
266
+ "ops/simple_gla/fused_recurrent.py": "Ab98sk1dnnFQmBmueO3D95vUs0zK8PC30ZRJYEGaIOM=",
267
+ "ops/simple_gla/naive.py": "g3fAN++vd0aWvZcdOW7Y0boMYcs2mOld8kePWRbl2R0=",
268
+ "ops/simple_gla/parallel.py": "hB6Zq+allqAYg/H8/9kpk0l7bsZCK9IsITkfLIhqZQ8=",
269
+ "ops/titans/__init__.py": "4t9OK1aYdW03I+sUlfUjj3ALTez1M/XFD/H6KzeZtGc=",
270
+ "ops/titans/log_impl.py": "Y5tMWJgtVE+GX394DdxQHd6Yirrg3sLx6sinMdQvkAE=",
271
+ "ops/titans/naive.py": "NUmchQQpsxFwNaEpSQbwZ/638zSr0tuy2xGa3DD07+A=",
272
+ "ops/ttt/__init__.py": "cFaLRxAWRsKjcmXh01bJbh5U59ZaFaM7DwYgm/KBAbo=",
273
+ "ops/ttt/chunk.py": "xk4Cw5LLMUKTauseXa+oybDIOp6A0e+McU/vqzp3KhM=",
274
+ "ops/ttt/fused_chunk.py": "KDdTpBvHj3jcIm2EkkNZTxWkhGbf0qvSQYEFkTwf+d4=",
275
+ "ops/ttt/naive.py": "IaJu/Zd1/PgFA5Bnbo9WQkpdz6BPZD0bl8iGKkWQlCo=",
276
+ "ops/utils/__init__.py": "eUiV7OAFYzGDyI/WtrSNltVrfU3crATGfKFWo9U87Hc=",
277
+ "ops/utils/backends/__init__.py": "Bfq8p7M9iRlUzfIfPTlvsKd/LMutWWtEzp2wfwOOK54=",
278
+ "ops/utils/backends/triton_ascend/__init__.py": "PEm11g9OLX2mdI66BW5JI+xL9vxIyNhZFzJxxGRaEkY=",
279
+ "ops/utils/backends/triton_ascend/cumsum.py": "AOxEn57vvwiNwOaDcVK3KbP+n5IUGrsbUWkG+kkukBg=",
280
+ "ops/utils/cache.py": "pksJ/9P1Gu1UetclWd7pTLmur9Kl8CxsjmCm4CAUepw=",
281
+ "ops/utils/constant.py": "uVK9zwOd7kM/DPSQk8cllx5nkYeK0K/KZVyCu0ZFdPg=",
282
+ "ops/utils/csr.py": "comXJRo/nULFkkZk556qHNxBfWeXk0EofLnQ69EIEjw=",
283
+ "ops/utils/cumsum.py": "6oQ1YDSIgMFZOZRn2oX8VKy/O+B5mbVWXl6146frCmY=",
284
+ "ops/utils/index.py": "BdattFUI0BkVExltE0Zxl/BNO9Ldp6hSUfU1zbM3710=",
285
+ "ops/utils/logcumsumexp.py": "7uEgeqUuLxi06jN/hOcTHvqJEITDGXECjNzk5SKPce0=",
286
+ "ops/utils/logsumexp.py": "7g4hdIjRKkO8CylqAs80XR02A5PyIWYxrKcc79qRcy4=",
287
+ "ops/utils/matmul.py": "TOLhNlPSm2JJeFmVsgh5D3t9LPUjw30w+mq5TIHef5Y=",
288
+ "ops/utils/op.py": "nFuDfGU88dXF1Pe8xrcxoda8QWZA5QTojiFsse/53fQ=",
289
+ "ops/utils/pack.py": "XT3/YxB71p6VO9oXPkpD0te42kbwU0dFaG3aZawdBHQ=",
290
+ "ops/utils/pooling.py": "LB2lncX1ThcjDIEWvMwiQpW2T8KE/AQ5dTqcJC/hNhs=",
291
+ "ops/utils/softmax.py": "uq/rygFZaAImpnHgOmAN9D+jrq/m3WHTyu6QGQR/EoU=",
292
+ "ops/utils/softplus.py": "tdlxnzZRHWEZWhVl9uFVl/EGQ6phOBMEEwlbCtu8jz0=",
293
+ "ops/utils/solve_tril.py": "oy0zat1/r4IjvqQ0MkJbrn1MYoayluYORr3nvX7z9NE=",
294
+ "ops/wall_attn/__init__.py": "r/FbDGjGO8WK1DaMbl9axWHpENUa3pzSc0v9qviHJ6I=",
295
+ "ops/wall_attn/decode.py": "JphI1kWj0Wwu024gnwgUWrWmhkAnKloXi1ik6cDpRrY=",
296
+ "ops/wall_attn/naive.py": "9YKwBSvQ9imy229T5KMYKpuVNb+xEzhXsXdMemRWqjQ=",
297
+ "ops/wall_attn/parallel.py": "oa2UJtSi8ouSG8g6bUWHwOehbwLH74SSHIU8LUiwVrg=",
298
+ "utils/__init__.py": "bdtrr5b2e7dzcO88Il6bH72LypiM103tHkbY2kPoaeE=",
299
+ "utils/_compat.py": "kYAtqK4JdTczM1FpqT44T/mILN7DcnPb8C4uPnO4Ho0=",
300
+ "utils/_config.py": "l1PBnf2C6SABltOAhs9GB0GTr0olQ1fA7d/bhzTb6IA=",
301
+ "utils/_decorators.py": "8OxDii2b+D+deTCQLPK1sdFx7McS/Wi9+AO67mlLrYg=",
302
+ "utils/_device.py": "yXcM5ZkBLCOvHl/Dh25SveTgbSemR6PkFAl/UabLYWE=",
303
+ "utils/_testing.py": "eFchc/74QJDXWv5T1YmIZFzRRpFWh+3gaYpQ1doztzs=",
304
+ "utils/ascend_ub_manager.py": "TcjVL6L7YiNwsa7YZ4+9KrIPQ2RmG7PJCfmXYc24uEc="
305
+ }
306
+ }
307
+ }
build/torch-cuda/modules/__init__.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.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
+ ]
build/torch-cuda/modules/activations.py ADDED
@@ -0,0 +1,1205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 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:
51
+ # 2D: any layout with stride(-1)==1 is valid (can view as (T, D))
52
+ return True
53
+ if ndim == 3:
54
+ # 3D (B, T, D): stride should be (T*D, D, 1)
55
+ return x.stride(0) == x.stride(-2) * x.shape[-2]
56
+ if ndim == 4:
57
+ # 4D (B, H, T, D): stride should be (H*T*D, T*D, D, 1)
58
+ if x.stride(1) != x.stride(-2) * x.shape[-2]:
59
+ return False
60
+ return x.stride(0) == x.stride(1) * x.shape[1]
61
+ # 5D+ fallback to loop
62
+ expected = x.stride(-2) * x.shape[-2]
63
+ for d in range(ndim - 3, -1, -1):
64
+ if x.stride(d) != expected:
65
+ return False
66
+ expected *= x.shape[d]
67
+ return True
68
+
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
+
76
+
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
109
+ row = offs // D
110
+ col = offs % D
111
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
112
+ b_y = tl.sigmoid(b_x)
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
138
+ row = offs // D
139
+ col = offs % D
140
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
141
+ b_dy = tl.load(dy + row * stride_dy_row + col, mask=mask, other=0.).to(tl.float32)
142
+ b_s = tl.sigmoid(b_x)
143
+ b_dx = b_dy * b_s * (1.0 - b_s)
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
+ )
179
+ return dx
180
+
181
+
182
+ class SigmoidFunction(torch.autograd.Function):
183
+
184
+ @staticmethod
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
223
+ row = offs // D
224
+ col = offs % D
225
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
226
+ b_m = tl.minimum(0., b_x)
227
+ b_z = 1. + exp(-tl.abs(b_x))
228
+ b_y = (b_m - log(b_z)) / temperature
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,
244
+ dy,
245
+ dx,
246
+ stride_x_row,
247
+ stride_dy_row,
248
+ stride_dx_row,
249
+ temperature,
250
+ T,
251
+ D: tl.constexpr,
252
+ B: tl.constexpr,
253
+ ):
254
+ i_n = tl.program_id(0).to(tl.int64)
255
+ offs = i_n * B + tl.arange(0, B)
256
+ mask = offs < T
257
+ row = offs // D
258
+ col = offs % D
259
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
260
+ b_dy = tl.load(dy + row * stride_dy_row + col, mask=mask, other=0.).to(tl.float32)
261
+ b_dx = b_dy * ((1. - tl.sigmoid(b_x)) / temperature)
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,
278
+ )
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,
303
+ )
304
+ return dx
305
+
306
+
307
+ class LogSigmoidFunction(torch.autograd.Function):
308
+
309
+ @staticmethod
310
+ @input_guard(no_guard_contiguous=True)
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
348
+ row = offs // D
349
+ col = offs % D
350
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
351
+ b_y = b_x * tl.sigmoid(b_x)
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
377
+ row = offs // D
378
+ col = offs % D
379
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
380
+ b_dy = tl.load(dy + row * stride_dy_row + col, mask=mask, other=0.).to(tl.float32)
381
+ b_s = tl.sigmoid(b_x)
382
+ b_dx = b_dy * b_s * (1.0 + b_x * (1.0 - b_s))
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
+ )
418
+ return dx
419
+
420
+
421
+ class SwishFunction(torch.autograd.Function):
422
+
423
+ @staticmethod
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
437
+
438
+ # 1/sqrt(2*pi)-> 0.3989423
439
+ # 1/sqrt(2) -> 0.70710678
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
+
467
+
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
+
522
+ fast_gelu_impl = FastGeLUFunction.apply
523
+
524
+
525
+ @torch.compile
526
+ 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)
541
+
542
+
543
+ class SquaredReLUFunction(torch.autograd.Function):
544
+
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
581
+ row = offs // D
582
+ col = offs % D
583
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
584
+ b_y = tl.load(y + row * stride_y_row + col, mask=mask, other=0.).to(tl.float32)
585
+ b_z = b_x * tl.sigmoid(b_x) * b_y
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,
607
+ stride_dx_row,
608
+ stride_dy_row,
609
+ stride_z_row,
610
+ T,
611
+ D: tl.constexpr,
612
+ B: tl.constexpr,
613
+ HAS_WEIGHT: tl.constexpr,
614
+ ):
615
+ i_n = tl.program_id(0).to(tl.int64)
616
+ offs = i_n * B + tl.arange(0, B)
617
+ mask = offs < T
618
+ row = offs // D
619
+ col = offs % D
620
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
621
+ b_y = tl.load(y + row * stride_y_row + col, mask=mask, other=0.).to(tl.float32)
622
+ b_g = tl.load(g + row * stride_g_row + col, mask=mask, other=0.).to(tl.float32)
623
+
624
+ b_s = tl.sigmoid(b_x)
625
+ b_xs = b_x * b_s
626
+ b_dx = b_g * b_s * (1.0 + b_x * (1.0 - b_s)) * b_y
627
+ b_dy = b_g * b_xs
628
+
629
+ tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
630
+ tl.store(dy + row * stride_dy_row + col, b_dy.to(dy.dtype.element_ty), mask=mask)
631
+ if HAS_WEIGHT:
632
+ b_z = b_xs * b_y
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,
660
+ g: torch.Tensor,
661
+ use_weight: bool = False,
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']),)](
676
+ x=x,
677
+ y=y,
678
+ g=g,
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
+ )
691
+ if use_weight:
692
+ return dx, dy, z
693
+ return dx, dy
694
+
695
+
696
+ class SwiGLUFunction(torch.autograd.Function):
697
+ r"""
698
+ Swish-Gated Linear Unit (SwiGLU) function.
699
+
700
+ .. math::
701
+ \text{SwiGLU}(x, y) = swish(x) * y = \frac{x}{1 + \exp(-x)} * y
702
+ """
703
+
704
+ @staticmethod
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):
718
+ r"""
719
+ Swish-Gated Linear Unit (SwiGLU) function followed by a linear transformation.
720
+
721
+ .. math::
722
+ \text{SwiGLULinear}(x, y, W, b) = (swish(x) * y) W + b
723
+
724
+ This simple wrap discards the intermediate results of SwiGLU(x, y) to save memory.
725
+ """
726
+
727
+ @staticmethod
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
738
+ @input_guard(no_guard_contiguous=True)
739
+ @autocast_custom_bwd
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
780
+ row = offs // D
781
+ col = offs % D
782
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
783
+ b_y = tl.load(y + row * stride_y_row + col, mask=mask, other=0.).to(tl.float32)
784
+ b_z = tl.sigmoid(b_x) * b_y
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,
806
+ stride_dx_row,
807
+ stride_dy_row,
808
+ stride_z_row,
809
+ T,
810
+ D: tl.constexpr,
811
+ B: tl.constexpr,
812
+ HAS_WEIGHT: tl.constexpr,
813
+ ):
814
+ i_n = tl.program_id(0).to(tl.int64)
815
+ offs = i_n * B + tl.arange(0, B)
816
+ mask = offs < T
817
+ row = offs // D
818
+ col = offs % D
819
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
820
+ b_y = tl.load(y + row * stride_y_row + col, mask=mask, other=0.).to(tl.float32)
821
+ b_g = tl.load(g + row * stride_g_row + col, mask=mask, other=0.).to(tl.float32)
822
+
823
+ b_s = tl.sigmoid(b_x)
824
+ b_dx = b_g * b_s * (1.0 - b_s) * b_y
825
+ b_dy = b_g * b_s
826
+
827
+ tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
828
+ tl.store(dy + row * stride_dy_row + col, b_dy.to(dy.dtype.element_ty), mask=mask)
829
+ if HAS_WEIGHT:
830
+ b_z = b_s * b_y
831
+ tl.store(z + row * stride_z_row + col, b_z.to(z.dtype.element_ty), mask=mask)
832
+
833
+
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
+ )
851
+ return z
852
+
853
+
854
+ @torch.compiler.disable
855
+ def sigmoidglu_fwdbwd(
856
+ x: torch.Tensor,
857
+ y: torch.Tensor,
858
+ g: torch.Tensor,
859
+ use_weight: bool = False,
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']),)](
874
+ x=x,
875
+ y=y,
876
+ g=g,
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
+ )
889
+ if use_weight:
890
+ return dx, dy, z
891
+ return dx, dy
892
+
893
+
894
+ class SigmoidGLUFunction(torch.autograd.Function):
895
+ r"""
896
+ Sigmoid-Gated Linear Unit (SigmoidGLU) function.
897
+
898
+ .. math::
899
+ \text{SigmoidGLU}(x, y) = sigmoid(x) * y = \frac{1}{1 + \exp(-x)} * y
900
+ """
901
+
902
+ @staticmethod
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):
916
+ r"""
917
+ Sigmoid-Gated Linear Unit (SigmoidGLU) function followed by a linear transformation.
918
+
919
+ .. math::
920
+ \text{SigmoidGLULinear}(x, y, W, b) = (sigmoid(x) * y) W + b
921
+
922
+ This simple wrap discards the intermediate results of SigmoidGLU(x, y) to save memory.
923
+ """
924
+
925
+ @staticmethod
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
933
+ return out
934
+
935
+ @staticmethod
936
+ @input_guard(no_guard_contiguous=True)
937
+ @autocast_custom_bwd
938
+ def backward(ctx, dout, *args):
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
946
+
947
+
948
+ sigmoidglu = SigmoidGLUFunction.apply
949
+
950
+
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
977
+ row = offs // D
978
+ col = offs % D
979
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
980
+ b_y = tl.load(y + row * stride_y_row + col, mask=mask, other=0.).to(tl.float32)
981
+ b_s = tl.sigmoid(b_x)
982
+ b_pos = b_x > 0
983
+ # feed only positive lanes to log/sqrt; masked lanes give x**p = 1 and are dropped by the where
984
+ b_xp = tl.where(b_pos, b_x, 1.0)
985
+ b_sqrt = tl.sqrt(b_xp)
986
+ b_p = m / (b_sqrt + 1.0)
987
+ b_pow = exp(b_p * log(b_xp))
988
+ b_g = tl.where(b_pos, b_pow * b_s, b_x * b_s)
989
+ b_z = b_g * b_y
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,
1011
+ stride_dx_row,
1012
+ stride_dy_row,
1013
+ stride_z_row,
1014
+ m,
1015
+ T,
1016
+ D: tl.constexpr,
1017
+ B: tl.constexpr,
1018
+ HAS_WEIGHT: tl.constexpr,
1019
+ ):
1020
+ i_n = tl.program_id(0).to(tl.int64)
1021
+ offs = i_n * B + tl.arange(0, B)
1022
+ mask = offs < T
1023
+ row = offs // D
1024
+ col = offs % D
1025
+ b_x = tl.load(x + row * stride_x_row + col, mask=mask, other=0.).to(tl.float32)
1026
+ b_y = tl.load(y + row * stride_y_row + col, mask=mask, other=0.).to(tl.float32)
1027
+ b_g = tl.load(g + row * stride_g_row + col, mask=mask, other=0.).to(tl.float32)
1028
+
1029
+ b_s = tl.sigmoid(b_x)
1030
+ b_pos = b_x > 0
1031
+ b_xp = tl.where(b_pos, b_x, 1.0)
1032
+ b_sqrt = tl.sqrt(b_xp)
1033
+ b_ln = log(b_xp)
1034
+ b_p = m / (b_sqrt + 1.0)
1035
+ b_pow = exp(b_p * b_ln)
1036
+
1037
+ b_gate_pos = b_pow * b_s
1038
+ # d/dx of the exponent term: p' = -m / (2*sqrt(x)*(sqrt(x)+1)**2)
1039
+ b_pprime = -m / (2.0 * b_sqrt * (b_sqrt + 1.0) * (b_sqrt + 1.0))
1040
+ b_dgate_pos = b_gate_pos * (b_pprime * b_ln + b_p / b_xp + 1.0 - b_s)
1041
+ b_gate_neg = b_x * b_s
1042
+ b_dgate_neg = b_s * (1.0 + b_x * (1.0 - b_s))
1043
+
1044
+ b_gate = tl.where(b_pos, b_gate_pos, b_gate_neg)
1045
+ b_dgate = tl.where(b_pos, b_dgate_pos, b_dgate_neg)
1046
+
1047
+ b_dx = b_g * b_y * b_dgate
1048
+ b_dy = b_g * b_gate
1049
+
1050
+ tl.store(dx + row * stride_dx_row + col, b_dx.to(dx.dtype.element_ty), mask=mask)
1051
+ tl.store(dy + row * stride_dy_row + col, b_dy.to(dy.dtype.element_ty), mask=mask)
1052
+ if HAS_WEIGHT:
1053
+ b_z = b_gate * b_y
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,
1074
+ )
1075
+ return z
1076
+
1077
+
1078
+ @dispatch('modules')
1079
+ def powglu_fwdbwd(
1080
+ x: torch.Tensor,
1081
+ y: torch.Tensor,
1082
+ g: torch.Tensor,
1083
+ power: float = 3.0,
1084
+ use_weight: bool = False,
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']),)](
1099
+ x=x,
1100
+ y=y,
1101
+ g=g,
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,
1114
+ )
1115
+ if use_weight:
1116
+ return dx, dy, z
1117
+ return dx, dy
1118
+
1119
+
1120
+ class PowGLUFunction(torch.autograd.Function):
1121
+ r"""
1122
+ Power-Gated Linear Unit (PowGLU) function.
1123
+
1124
+ .. math::
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
1133
+ @input_guard(no_guard_contiguous=True)
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
+
1147
+ class PowGLULinearFunction(torch.autograd.Function):
1148
+ r"""
1149
+ Power-Gated Linear Unit (PowGLU) function followed by a linear transformation.
1150
+
1151
+ .. math::
1152
+ \text{PowGLULinear}(x, y, W, b) = (g(x) * y) W + b
1153
+
1154
+ This simple wrap discards the intermediate results of PowGLU(x, y) to save memory.
1155
+ """
1156
+
1157
+ @staticmethod
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
1165
+ ctx.power = power
1166
+ return out
1167
+
1168
+ @staticmethod
1169
+ @input_guard(no_guard_contiguous=True)
1170
+ @autocast_custom_bwd
1171
+ def backward(ctx, dout, *args):
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
1179
+
1180
+
1181
+ 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,
1189
+ weight: torch.Tensor,
1190
+ bias: torch.Tensor,
1191
+ power: float = 3.0,
1192
+ ) -> torch.Tensor:
1193
+ return PowGLULinearFunction.apply(x, y, weight, bias, power)
1194
+
1195
+
1196
+ ACT2FN = {
1197
+ 'relu': F.relu,
1198
+ 'sigmoid': sigmoid,
1199
+ 'logsigmoid': logsigmoid,
1200
+ 'silu': swish,
1201
+ 'swish': swish,
1202
+ 'sqrelu': sqrelu,
1203
+ 'gelu': fast_gelu_impl,
1204
+ 'bias_gelu': bias_gelu_impl,
1205
+ }
build/torch-cuda/modules/backends/__init__.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,931 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,1175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-cuda/modules/conv/__init__.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .causal_conv1d import causal_conv1d
9
+ from .long_conv import ImplicitLongConvolution, LongConvolution, PositionalEmbedding, fft_conv
10
+ from .short_conv import ShortConvolution
11
+
12
+ __all__ = [
13
+ 'ImplicitLongConvolution',
14
+ 'LongConvolution',
15
+ 'PositionalEmbedding',
16
+ 'ShortConvolution',
17
+ 'causal_conv1d',
18
+ 'fft_conv',
19
+ ]
build/torch-cuda/modules/conv/causal_conv1d.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/cp/__init__.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .ops import CausalConv1dFunctionCP, causal_conv1d_cp
9
+
10
+ __all__ = [
11
+ 'CausalConv1dFunctionCP',
12
+ 'causal_conv1d_cp',
13
+ ]
build/torch-cuda/modules/conv/cp/ops.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 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):
16
+ """
17
+ Context Parallel version of CausalConv1dFunction.
18
+
19
+ Forward:
20
+ 1. Get tails from previous rank to construct initial_state
21
+ 2. Call causal_conv1d_fwd
22
+
23
+ Backward:
24
+ 1. Call causal_conv1d_bwd to get dx
25
+ 2. Sync communication: add next rank's first W-1 token gradients to current rank's last W-1 tokens
26
+ """
27
+
28
+ @staticmethod
29
+ def _prepare_initial_state_for_cp(
30
+ x: torch.Tensor,
31
+ weight: torch.Tensor,
32
+ cu_seqlens: torch.Tensor | None,
33
+ context: FLACPContext,
34
+ group: dist.ProcessGroup | None,
35
+ ) -> torch.Tensor | None:
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)
64
+ if valid_len > 0:
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
+
75
+ @staticmethod
76
+ def _correct_dx_for_cp(
77
+ dx: torch.Tensor,
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:
103
+ # Only keep gradients for positions that had real data from the
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]
117
+ # Add to current rank's last W-1 tokens (these tokens are used as initial_state by next rank)
118
+ dx[0, -(W-1):, :].add_(recv_d_init)
119
+
120
+ @staticmethod
121
+ def forward(
122
+ ctx,
123
+ x: torch.Tensor,
124
+ weight: torch.Tensor,
125
+ bias: torch.Tensor | None,
126
+ activation: str | None,
127
+ chunk_indices: torch.Tensor | None,
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")
137
+ cu_seqlens = cp_context.cu_seqlens
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,
147
+ cu_seqlens=cu_seqlens,
148
+ context=cp_context,
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
156
+ ctx.chunk_indices = chunk_indices
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(
219
+ x: torch.Tensor,
220
+ weight: torch.Tensor,
221
+ bias: torch.Tensor | None = None,
222
+ activation: str | None = None,
223
+ chunk_indices: torch.Tensor | None = None,
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
+ )
build/torch-cuda/modules/conv/cuda/__init__.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .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
+ ]
build/torch-cuda/modules/conv/cuda/ops.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/long_conv.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 math
9
+
10
+ import torch
11
+ import torch.nn as nn
12
+ 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
20
+ if k_rev is not None:
21
+ k_rev_f = torch.fft.rfft(k_rev, n=fft_size) / fft_size
22
+ k_f = k_f + k_rev_f.conj()
23
+ u_f = torch.fft.rfft(u.to(dtype=k.dtype), n=fft_size)
24
+
25
+ if len(u.shape) > 3:
26
+ k_f = k_f.unsqueeze(1)
27
+ y = torch.fft.irfft(u_f * k_f, n=fft_size, norm="forward")[..., :seqlen]
28
+
29
+ out = y + u
30
+ if gelu:
31
+ out = F.gelu(out)
32
+ if dropout_mask is not None:
33
+ return (out * rearrange(dropout_mask, "b H -> b H 1")).to(dtype=u.dtype)
34
+ else:
35
+ return out.to(dtype=u.dtype)
36
+
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
+
97
+ f = torch.linspace(1e-4, bands - 1, bands)[None, None]
98
+ z = torch.exp(-1j * f * w)
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
+
106
+ class ImplicitLongConvolution(nn.Module):
107
+ """
108
+ Long convolution with implicit filter parameterized by an MLP.
109
+
110
+ Args:
111
+ hidden_size (int):
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)
build/torch-cuda/modules/conv/short_conv.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .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
+ ]
build/torch-cuda/modules/conv/triton/kernels.py ADDED
@@ -0,0 +1,683 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ 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
18
+
19
+
20
+ @triton.heuristics({
21
+ 'HAS_WEIGHT': lambda args: args['weight'] is not None,
22
+ 'HAS_BIAS': lambda args: args['bias'] is not None,
23
+ 'HAS_RESIDUAL': lambda args: args['residual'] is not None,
24
+ 'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
25
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
26
+ })
27
+ @fla_cache_autotune(
28
+ configs=[
29
+ triton.Config({'BD': BD}, num_warps=num_warps)
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
37
+ def causal_conv1d_fwd_kernel(
38
+ x,
39
+ y,
40
+ weight,
41
+ bias,
42
+ residual,
43
+ cu_seqlens,
44
+ initial_state,
45
+ chunk_indices,
46
+ B,
47
+ T,
48
+ stride_x_n,
49
+ stride_x_t,
50
+ stride_x_d,
51
+ D: tl.constexpr,
52
+ W: tl.constexpr,
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,
60
+ HAS_RESIDUAL: tl.constexpr,
61
+ USE_INITIAL_STATE: tl.constexpr,
62
+ IS_VARLEN: tl.constexpr,
63
+ ):
64
+ i_d, i_t, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2)
65
+
66
+ if IS_VARLEN:
67
+ 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)
68
+ bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
69
+ T = eos - bos
70
+ p_x = x + bos * stride_x_t
71
+ else:
72
+ i_n = i_b
73
+ bos, eos = (i_b * T).to(tl.int64), (i_b * T + T).to(tl.int64)
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]
83
+ b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0).to(tl.float32)
84
+
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
+
118
+ if HAS_WEIGHT:
119
+ b_yi *= tl.sum(b_w * (o_w == (i_w + W - 1)), 1)
120
+ b_y += b_yi
121
+
122
+ if HAS_BIAS:
123
+ b_y += tl.load(bias + o_d, mask=m_d).to(tl.float32)
124
+
125
+ if ACTIVATION == 'swish' or ACTIVATION == 'silu':
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({
138
+ 'HAS_WEIGHT': lambda args: args['dw'] is not None,
139
+ 'HAS_BIAS': lambda args: args['db'] is not None,
140
+ 'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
141
+ 'USE_FINAL_STATE': lambda args: args['dht'] is not None,
142
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
143
+ })
144
+ @fla_cache_autotune(
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
154
+ def causal_conv1d_bwd_kernel(
155
+ x,
156
+ y,
157
+ weight,
158
+ initial_state,
159
+ dht,
160
+ dy,
161
+ dx,
162
+ dw,
163
+ db,
164
+ cu_seqlens,
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,
186
+ USE_INITIAL_STATE: tl.constexpr,
187
+ USE_FINAL_STATE: tl.constexpr,
188
+ IS_VARLEN: tl.constexpr,
189
+ ):
190
+ i_d, i_t, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2)
191
+ if IS_VARLEN:
192
+ i_tg = i_t
193
+ 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)
194
+ bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(cu_seqlens + i_n + 1).to(tl.int64)
195
+ T = eos - bos
196
+ p_x = x + bos * stride_x_t
197
+ else:
198
+ i_tg = i_b * tl.num_programs(1) + i_t
199
+ i_n = i_b
200
+ bos, eos = (i_b * T).to(tl.int64), (i_b * T + T).to(tl.int64)
201
+ p_x = x + tl.cast(i_b, tl.int64) * stride_x_n
202
+
203
+ if IS_VARLEN:
204
+ p_dy = dy + bos * stride_dy_t
205
+ else:
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
+
219
+ b_dx = tl.zeros((BT, BD), dtype=tl.float32)
220
+ if HAS_BIAS:
221
+ b_db = tl.zeros((BD,), dtype=tl.float32)
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
235
+ if HAS_WEIGHT:
236
+ # [BT, BD]
237
+ b_wdy = b_wdy * tl.sum(b_w * (o_w == (W - i_w - 1)), 1)
238
+ # [BD]
239
+ b_dw = tl.sum(b_dy * b_x, 0)
240
+ tl.store(dw + i_tg * D*W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
241
+ if HAS_BIAS and i_w == 0:
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
257
+ if HAS_WEIGHT:
258
+ # [BT, BD]
259
+ b_wdy = b_wdy * tl.sum(b_w * (o_w == (W - i_w - 1)), 1)
260
+ # [BD]
261
+ b_dw = tl.sum(b_dy * b_x, 0)
262
+ tl.store(dw + i_tg * D*W + o_d * W + W - i_w - 1, b_dw.to(dw.dtype.element_ty), mask=m_d)
263
+ if HAS_BIAS and i_w == 0:
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
+
302
+ if HAS_BIAS and i_w == 0:
303
+ b_db += tl.sum(b_dy_shift, 0)
304
+ b_wdy = b_dy_shift if not HAS_WEIGHT else (b_dy_shift * tl.sum(b_w * (o_w == (W - i_w - 1)), 1))
305
+ b_dx += b_wdy
306
+
307
+ if HAS_BIAS:
308
+ b_db = tl.cast(b_db, dtype=db.dtype.element_ty, fp_downcast_rounding='rtne')
309
+ tl.store(db + i_tg * D + o_d, b_db, mask=m_d)
310
+
311
+ if USE_FINAL_STATE:
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
321
+
322
+ if IS_VARLEN:
323
+ p_dx = dx + bos * stride_dx_t
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({
332
+ 'USE_INITIAL_STATE': lambda args: args['cache'] is not None,
333
+ 'HAS_WEIGHT': lambda args: args['weight'] is not None,
334
+ 'HAS_BIAS': lambda args: args['bias'] is not None,
335
+ 'HAS_RESIDUAL': lambda args: args['residual'] is not None,
336
+ })
337
+ @fla_cache_autotune(
338
+ configs=[
339
+ triton.Config({'BD': BD}, num_warps=num_warps)
340
+ for BD in [8, 16, 32, 64, 128, 256]
341
+ for num_warps in NUM_WARPS_AUTOTUNE
342
+ ],
343
+ key=['D', 'W'],
344
+ restore_value=['cache'],
345
+ **autotune_cache_kwargs,
346
+ )
347
+ @triton.jit
348
+ def causal_conv1d_update_kernel(
349
+ x,
350
+ cache,
351
+ residual,
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,
362
+ BW: tl.constexpr,
363
+ ACTIVATION: tl.constexpr,
364
+ USE_INITIAL_STATE: tl.constexpr,
365
+ HAS_WEIGHT: tl.constexpr,
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)
373
+ m_d = o_d < D
374
+ m_w = o_w < W
375
+
376
+ # [BD]
377
+ b_x = tl.load(x + i_n * stride_x_n + o_d * stride_x_d, mask=m_d, other=0).to(tl.float32)
378
+
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
+
397
+ if HAS_WEIGHT:
398
+ b_w = tl.load(weight + o_d[:, None] * W + o_w, mask=m_d[:, None] & m_w, other=0)
399
+ b_y = tl.sum(b_cache * b_w, 1)
400
+ else:
401
+ b_y = tl.sum(b_cache, 1)
402
+
403
+ if HAS_BIAS:
404
+ b_y += tl.load(bias + o_d, mask=m_d)
405
+
406
+ if ACTIVATION == 'swish' or ACTIVATION == 'silu':
407
+ b_y = b_y * tl.sigmoid(b_y)
408
+
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
433
+ def compute_dh0_kernel(
434
+ dy,
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
+
504
+
505
+ @triton.heuristics({
506
+ 'USE_INITIAL_STATE': lambda args: args['initial_state'] is not None,
507
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
508
+ })
509
+ @triton.jit
510
+ def causal_conv1d_states_fwd_kernel(
511
+ x,
512
+ initial_state,
513
+ final_state,
514
+ cu_seqlens,
515
+ T,
516
+ D,
517
+ W,
518
+ stride_x_n,
519
+ stride_x_t,
520
+ stride_x_d,
521
+ BD: tl.constexpr,
522
+ BW: tl.constexpr,
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
+
532
+ if IS_VARLEN:
533
+ bos = tl.load(cu_seqlens + i_n).to(tl.int64)
534
+ eos = tl.load(cu_seqlens + i_n + 1).to(tl.int64)
535
+ seq_len = (eos - bos).to(tl.int32)
536
+ p_x = x + bos * stride_x_t
537
+ else:
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:
548
+ o_c = W - (BW - seq_len) + tl.arange(0, BW)
549
+ m_c = (o_c >= 0) & (o_c < W)
550
+
551
+ p_init = initial_state + i_n * D*W + o_d[None, :] * W + o_c[:, None]
552
+ mask_init = m_d[None, :] & m_c[:, None]
553
+
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,
575
+ state_len: int,
576
+ initial_state: torch.Tensor | None = None,
577
+ cu_seqlens: torch.Tensor | None = None,
578
+ ) -> torch.Tensor:
579
+ if cu_seqlens is not None:
580
+ N = len(cu_seqlens) - 1
581
+ if x.dim() == 2:
582
+ stride_x_n = 0
583
+ stride_x_t, stride_x_d = x.stride()
584
+ T = x.shape[0]
585
+ else:
586
+ stride_x_n = x.stride(0)
587
+ stride_x_t, stride_x_d = x.stride(1), x.stride(2)
588
+ T = x.shape[1]
589
+ D = x.shape[-1]
590
+ else:
591
+ B, T, D = x.shape
592
+ N = B
593
+ stride_x_n, stride_x_t, stride_x_d = x.stride()
594
+
595
+ W = state_len
596
+ final_state = torch.empty(N, D, W, dtype=x.dtype, device=x.device)
597
+
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,
605
+ initial_state=initial_state,
606
+ final_state=final_state,
607
+ cu_seqlens=cu_seqlens,
608
+ T=T,
609
+ D=D,
610
+ W=W,
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,
623
+ cache: torch.Tensor,
624
+ residual: torch.Tensor | None = None,
625
+ weight: torch.Tensor | None = None,
626
+ bias: torch.Tensor | None = None,
627
+ activation: str | None = None,
628
+ ) -> torch.Tensor:
629
+ shape = x.shape
630
+ if weight is not None and x.shape[-1] != weight.shape[0]:
631
+ x = rearrange(x, 'b t ... -> b t (...)')
632
+
633
+ D = x.shape[-1]
634
+ N = x.numel() // D
635
+ W = weight.shape[1] if weight is not None else None
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)
657
+
658
+ if y.dim() == 2:
659
+ stride_y_n, stride_y_d = y.stride(0), y.stride(1)
660
+ elif y.dim() == 3 and y.shape[0] == 1:
661
+ stride_y_n, stride_y_d = y.stride(1), y.stride(2)
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,
669
+ cache=cache,
670
+ residual=residual,
671
+ y=y,
672
+ weight=weight,
673
+ bias=bias,
674
+ stride_x_n=stride_x_n,
675
+ stride_x_d=stride_x_d,
676
+ stride_y_n=stride_y_n,
677
+ stride_y_d=stride_y_d,
678
+ D=D,
679
+ W=W,
680
+ BW=BW,
681
+ ACTIVATION=activation,
682
+ )
683
+ return y.view(shape), cache
build/torch-cuda/modules/conv/triton/ops.py ADDED
@@ -0,0 +1,424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 functools
11
+ import math
12
+
13
+ import torch
14
+ import torch.nn.functional as F
15
+ from torch import nn
16
+
17
+ from ..modules.activations import fast_gelu_impl, sigmoid, sqrelu, swish
18
+ from ..modules.layernorm import layer_norm
19
+ from ..utils import checkpoint
20
+
21
+
22
+ @functools.cache
23
+ def _triu_indices(n: int, offset: int, device: torch.device) -> torch.Tensor:
24
+ # cache the upper-triangular gather indices per (size, offset, device) to avoid rebuilding
25
+ # them and copying host -> device on every forward
26
+ return torch.triu_indices(n, n, offset, device=device)
27
+
28
+
29
+ @checkpoint
30
+ def flatten_diag_outer_product(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
31
+ z = torch.einsum("...i,...j->...ij", x, y)
32
+ N = z.size(-1)
33
+ indices = _triu_indices(N, 0, z.device)
34
+ return z[..., indices[0], indices[1]]
35
+
36
+
37
+ @checkpoint
38
+ def flatten_diag_outer_product_off1(x: torch.Tensor, y: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
39
+ z = torch.einsum("...i,...j->...ij", x, y)
40
+ N = z.size(-1)
41
+ indices = _triu_indices(N, 1, z.device)
42
+ diag = torch.arange(N, device=z.device)
43
+ return z[..., indices[0], indices[1]], z[..., diag, diag]
44
+
45
+
46
+ def is_power_of_2(n: int) -> bool:
47
+ return (n & (n - 1) == 0) and n != 0
48
+
49
+
50
+ class HedgehogFeatureMap(nn.Module):
51
+
52
+ r"""
53
+ Hedgehog feature map as introduced in
54
+ `The Hedgehog & the Porcupine: Expressive Linear Attentions with Softmax Mimicry <https://arxiv.org/abs/2402.04347>`_
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ head_dim: int,
60
+ ) -> None:
61
+ super().__init__()
62
+ # Trainable map
63
+ self.layer = nn.Linear(head_dim, head_dim)
64
+ self.init_weights_()
65
+
66
+ def init_weights_(self):
67
+ """Initialize trainable map as identity"""
68
+ nn.init.eye_(self.layer.weight)
69
+ nn.init.zeros_(self.layer.bias)
70
+
71
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
72
+ x = self.layer(x) # b, h, l, d
73
+ y = 2 * x
74
+ return torch.cat([y, -y], dim=-1).softmax(-1)
75
+
76
+
77
+ class T2RFeatureMap(nn.Module):
78
+
79
+ r"""
80
+ Simple linear mapping feature map as in
81
+ `Finetuning Pretrained Transformers into RNNs <https://arxiv.org/abs/2103.13076>`_
82
+ """
83
+
84
+ def __init__(
85
+ self,
86
+ head_dim: int,
87
+ dot_dim: int | None = None,
88
+ bias: bool | None = False,
89
+ ) -> None:
90
+ super().__init__()
91
+ # Trainable map
92
+ if dot_dim is None:
93
+ dot_dim = head_dim
94
+
95
+ self.head_dim = head_dim
96
+ self.dot_dim = dot_dim
97
+ self.bias = bias
98
+
99
+ self.layer = nn.Linear(head_dim, dot_dim, bias=bias)
100
+
101
+ def __repr__(self) -> str:
102
+ return f"{self.__class__.__name__}(head_dim={self.head_dim}, dot_dim={self.dot_dim}, bias={self.bias})"
103
+
104
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
105
+ return self.layer(x).relu()
106
+
107
+
108
+ class DPFPFeatureMap(nn.Module):
109
+
110
+ r"""
111
+ Deterministic Parameter-Free Projection (DPFP) feature map in
112
+ `Linear Transformers Are Secretly Fast Weight Programmers <https://arxiv.org/abs/2102.11174>`_
113
+ """
114
+
115
+ def __init__(
116
+ self,
117
+ head_dim: int,
118
+ nu: int = 4,
119
+ ) -> None:
120
+ super().__init__()
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
129
+
130
+
131
+ class HadamardFeatureMap(nn.Module):
132
+ def __init__(
133
+ self,
134
+ head_dim: int,
135
+ ) -> None:
136
+ super().__init__()
137
+ # Trainable map
138
+ self.layer1 = nn.Linear(head_dim, head_dim)
139
+ self.layer2 = nn.Linear(head_dim, head_dim)
140
+
141
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
142
+ return self.layer1(x) * self.layer2(x)
143
+
144
+
145
+ class LearnableOuterProductFeatureMap(nn.Module):
146
+ def __init__(
147
+ self,
148
+ head_dim: int,
149
+ feature_dim: int,
150
+ ) -> None:
151
+ super().__init__()
152
+ # Trainable map
153
+ self.layer1 = nn.Linear(head_dim, feature_dim, bias=False)
154
+ self.layer2 = nn.Linear(head_dim, feature_dim, bias=False)
155
+ self.normalizer = feature_dim ** -0.5
156
+
157
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
158
+ return flatten_diag_outer_product(self.layer1(x), self.layer2(x))
159
+
160
+
161
+ class LearnablePolySketchNonNegativeFeatureMap(nn.Module):
162
+
163
+ def __init__(
164
+ self,
165
+ head_dim: int,
166
+ sketch_size: int | None = None,
167
+ degree: int | None = 2,
168
+ ) -> None:
169
+ super().__init__()
170
+
171
+ assert is_power_of_2(degree) and degree >= 2, f"The degree {degree} must be a power of 2"
172
+
173
+ if sketch_size is None:
174
+ sketch_size = head_dim
175
+
176
+ self.head_dim = head_dim
177
+ self.sketch_size = sketch_size
178
+ self.degree = degree
179
+
180
+ self.gamma = nn.Parameter(torch.ones(head_dim))
181
+ self.beta = nn.Parameter(torch.zeros(head_dim))
182
+ # NOTE: the sketch layers defined here are quite different from the original paper
183
+ # currently we simply use linear layers without any non-linear activations
184
+ self.sketches1 = nn.ModuleList([
185
+ nn.Linear(head_dim, sketch_size, bias=False),
186
+ *[nn.Linear(sketch_size, sketch_size, bias=False) for _ in range(int(math.log2(self.degree)) - 2)],
187
+ ])
188
+ self.sketches2 = nn.ModuleList([
189
+ nn.Linear(head_dim, sketch_size, bias=False),
190
+ *[nn.Linear(sketch_size, sketch_size, bias=False) for _ in range(int(math.log2(self.degree)) - 2)],
191
+ ])
192
+
193
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
194
+ # Section 2.1
195
+ x = layer_norm(x, self.gamma, self.beta)
196
+ # first map the input to sketch size with learnable parameters
197
+ x = self.sketches1[0](x) * self.sketches2[0](x) * self.head_dim ** -0.5
198
+ for i in range(1, int(math.log2(self.degree)) - 1):
199
+ x = self.sketches1[i](x) * self.sketches2[i](x) * self.head_dim ** -0.5
200
+ # do sketch mapping for log2(p) - 1 times in total
201
+ # do p=2 mapping to ensure non-negativity
202
+ return flatten_diag_outer_product(x, x)
203
+
204
+
205
+ class TaylorFeatureMap(nn.Module):
206
+ def __init__(
207
+ self,
208
+ head_dim: int,
209
+ ) -> None:
210
+ super().__init__()
211
+ self.head_dim = head_dim
212
+ self.r2 = math.sqrt(2)
213
+ self.rd = math.sqrt(self.head_dim)
214
+ self.rrd = math.sqrt(self.rd)
215
+
216
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
217
+ x2_1, x2_2 = flatten_diag_outer_product_off1(x, x)
218
+ return torch.cat([torch.ones_like(x[..., 0:1]), x / self.rrd, x2_2 / (self.rd * self.r2), x2_1 / self.rd], dim=-1)
219
+
220
+
221
+ class RebasedFeatureMap(nn.Module):
222
+
223
+ def __init__(
224
+ self,
225
+ head_dim: int,
226
+ use_gamma: bool | None = True,
227
+ use_beta: bool | None = True,
228
+ normalize: bool | None = True,
229
+ ) -> None:
230
+ super().__init__()
231
+
232
+ self.head_dim = head_dim
233
+ self.use_gamma = use_gamma
234
+ self.use_beta = use_beta
235
+ self.normalize = normalize
236
+
237
+ self.gamma = None
238
+ self.beta = None
239
+ if use_gamma:
240
+ self.gamma = nn.Parameter(torch.ones(head_dim))
241
+ if use_beta:
242
+ self.beta = nn.Parameter(torch.zeros(head_dim))
243
+
244
+ def forward(self, x: torch.Tensor, flatten: bool | None = True) -> torch.Tensor:
245
+ if self.use_beta and self.use_gamma and self.normalize:
246
+ x = layer_norm(x, self.gamma, self.beta)
247
+ elif self.normalize:
248
+ x = F.layer_norm(x, (self.head_dim,), self.gamma, self.beta)
249
+ elif self.use_gamma and self.use_beta:
250
+ x = torch.addcmul(self.beta, x, self.gamma)
251
+ elif self.use_gamma:
252
+ x = x.mul(self.gamma)
253
+ else:
254
+ raise RuntimeError(f"Not supported combination of `use_gamma`, `use_beta` and `normalize`, "
255
+ f"which is currently set as (`{self.use_gamma}`, `{self.use_beta}`, `{self.normalize}`)")
256
+ if not flatten:
257
+ return x
258
+ x2_1, x2_2 = flatten_diag_outer_product_off1(x, x)
259
+ # rebased use learnable parameters to approximate any quadratic function
260
+ return torch.cat([x2_2 * self.head_dim ** -0.5, x2_1 * (2 / self.head_dim) ** 0.5], dim=-1)
261
+
262
+
263
+ class ReLUFeatureMap(nn.Module):
264
+
265
+ def __init__(
266
+ self,
267
+ ) -> None:
268
+ super().__init__()
269
+
270
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
271
+ return F.relu(x)
272
+
273
+
274
+ class SquaredReLUFeatureMap(nn.Module):
275
+
276
+ def __init__(
277
+ self,
278
+ ) -> None:
279
+ super().__init__()
280
+
281
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
282
+ return sqrelu(x)
283
+
284
+
285
+ class GELUFeatureMap(nn.Module):
286
+
287
+ def __init__(
288
+ self,
289
+ ) -> None:
290
+ super().__init__()
291
+
292
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
293
+ return fast_gelu_impl(x)
294
+
295
+
296
+ class SwishFeatureMap(nn.Module):
297
+
298
+ def __init__(
299
+ self,
300
+ ) -> None:
301
+ super().__init__()
302
+
303
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
304
+ return swish(x)
305
+
306
+
307
+ class SigmoidFeatureMap(nn.Module):
308
+
309
+ def __init__(
310
+ self,
311
+ ) -> None:
312
+ super().__init__()
313
+
314
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
315
+ return sigmoid(x)
build/torch-cuda/modules/fused_bitlinear.py ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # 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,
496
+ linear_weight,
497
+ linear_bias,
498
+ residual,
499
+ eps,
500
+ prenorm,
501
+ residual_in_fp32,
502
+ is_rms_norm,
503
+ )
504
+
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,
521
+ linear_weight=linear_weight,
522
+ linear_bias=linear_bias,
523
+ residual=residual,
524
+ eps=eps,
525
+ prenorm=prenorm,
526
+ residual_in_fp32=residual_in_fp32,
527
+ is_rms_norm=True,
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
+ )
build/torch-cuda/modules/fused_cross_entropy.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_kl_div.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 torch.nn as nn
10
+ 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
+
25
+
26
+ @triton.jit
27
+ def kl_div_kernel(
28
+ logits,
29
+ target_logits,
30
+ loss,
31
+ s_logits,
32
+ s_loss,
33
+ reduction: tl.constexpr,
34
+ N: tl.constexpr,
35
+ V: tl.constexpr,
36
+ BV: tl.constexpr,
37
+ ):
38
+ # https://github.com/triton-lang/triton/issues/1058
39
+ # If N*V is too large, i_n * stride will overflow out of int32, so we convert to int64
40
+ i_n = tl.program_id(0).to(tl.int64)
41
+
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)
64
+ td = td * exp(tm - m_new) + tl.sum(exp(b_tl - m_new))
65
+ tm = m_new
66
+
67
+ b_loss = 0.
68
+ # KL(y_true || y) = exp(y_true) * (log(y_true) - log(y))
69
+ for iv in range(0, NV):
70
+ o_x = iv * BV + tl.arange(0, BV)
71
+ b_sl = tl.load(logits + o_x, mask=o_x < V, other=float('-inf'))
72
+ b_tl = tl.load(target_logits + o_x, mask=o_x < V, other=float('-inf'))
73
+ b_sp_log = b_sl - sm - log(sd)
74
+ b_tp_log = b_tl - tm - log(td)
75
+ b_sp = exp(b_sp_log)
76
+ b_tp = exp(b_tp_log)
77
+ b_kl = tl.where(o_x < V, b_tp * (b_tp_log - b_sp_log), 0)
78
+ b_dl = -b_tp + b_sp
79
+ b_loss += tl.sum(b_kl)
80
+ if reduction == 'batchmean':
81
+ b_dl = b_dl / N
82
+ tl.store(logits + o_x, b_dl, mask=o_x < V)
83
+
84
+ # Normalize the loss by the number of elements if reduction is 'batchmean'
85
+ if reduction == 'batchmean':
86
+ b_loss = b_loss / N
87
+
88
+ tl.store(loss + i_n * s_loss, b_loss)
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
+
134
+ # ideally, we would like to achieve the same memory consumption as [N, H],
135
+ # so the expected chunk size should be:
136
+ # NC = ceil(V / H)
137
+ # C = ceil(N / NC)
138
+ # for ex: N = 4096*4, V = 32000, H = 4096 ==> NC = 8, C = ceil(N / NC) = 2048
139
+ N, H, V = *x.shape, weight.shape[0]
140
+ BV = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
141
+ # TODO: in real cases, we may need to limit the number of chunks NC to
142
+ # ensure the precisions of accumulated gradients
143
+ NC = min(8, triton.cdiv(V, H))
144
+ C = triton.next_power_of_2(triton.cdiv(N, NC))
145
+ NC = triton.cdiv(N, C)
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):
155
+ start, end = ic * C, min((ic + 1) * C, N)
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.
170
+ kl_div_kernel[(c_sx.shape[0],)](
171
+ logits=c_sl,
172
+ target_logits=c_tl,
173
+ loss=c_loss,
174
+ s_logits=c_sl.stride(-2),
175
+ s_loss=c_loss.stride(-1),
176
+ reduction=reduction,
177
+ N=N,
178
+ V=V,
179
+ BV=BV,
180
+ num_warps=STATIC_WARPS,
181
+ )
182
+
183
+ # gradient of logits is computed in-place by the above triton kernel and is of shape: C x V
184
+ # thus dx[start: end] should be of shape: C x H
185
+ # additionally, since we are chunking the inputs, observe that the loss and gradients are calculated only
186
+ # on `n_non_ignore` tokens. However, the gradient of the input should be calculated for all tokens.
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:
202
+ dw = dw.to(weight)
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
+
240
+
241
+ class FusedKLDivLossFunction(torch.autograd.Function):
242
+
243
+ @staticmethod
244
+ @input_guard
245
+ def forward(
246
+ ctx,
247
+ x: torch.Tensor,
248
+ target_x: torch.Tensor,
249
+ weight: torch.Tensor,
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
264
+
265
+ @staticmethod
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(
274
+ x: torch.Tensor,
275
+ target_x: torch.Tensor,
276
+ weight: torch.Tensor,
277
+ target_weight: torch.Tensor,
278
+ reduction: str = 'batchmean',
279
+ accumulate_grad_in_fp32: bool = True,
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(
303
+ "FusedKLDivLoss treats target_x and target_weight as a frozen teacher and does not compute "
304
+ "gradients for them. Detach target_x/target_weight before calling FusedKLDivLoss, or use "
305
+ "torch.nn.functional.kl_div if teacher gradients are required."
306
+ )
307
+ return FusedKLDivLossFunction.apply(
308
+ x,
309
+ target_x,
310
+ weight,
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
+
337
+ assert reduction in ['batchmean'], f"reduction: {reduction} is not supported"
338
+
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,
366
+ target_x=target_x,
367
+ weight=weight,
368
+ target_weight=target_weight,
369
+ reduction=self.reduction,
370
+ accumulate_grad_in_fp32=self.accumulate_grad_in_fp32,
371
+ )
372
+ return loss
build/torch-cuda/modules/fused_linear_cross_entropy.py ADDED
@@ -0,0 +1,767 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # 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
15
+ import torch.nn.functional as F
16
+ import triton
17
+ import triton.language as tl
18
+ 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
+
38
+
39
+ @triton.heuristics({
40
+ 'HAS_SCALE': lambda args: args['scale'] is not None,
41
+ 'HAS_SOFTCAPPING': lambda args: args['softcapping'] is not None,
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)
310
+
311
+ # [N, H]
312
+ dx = torch.zeros_like(x, device=device)
313
+ grad_dtype = torch.float32 if accumulate_grad_in_fp32 else weight.dtype
314
+ bias_grad_dtype = None
315
+ if bias is not None:
316
+ bias_grad_dtype = torch.float32 if accumulate_grad_in_fp32 else bias.dtype
317
+
318
+ # [V, H]
319
+ dw = torch.zeros_like(weight, device=device, dtype=grad_dtype) if weight is not None else None
320
+ # [V]
321
+ db = torch.zeros_like(bias, device=device, dtype=bias_grad_dtype) if bias is not None else None
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:
409
+ db = db.to(bias)
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
461
+ @input_guard
462
+ def forward(
463
+ ctx,
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,
540
+ db.detach() if bias is not None else None,
541
+ )
542
+ return loss
543
+
544
+ @staticmethod
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,
607
+ target,
608
+ weight,
609
+ bias,
610
+ ignore_index,
611
+ label_smoothing,
612
+ logit_scale,
613
+ logit_softcapping,
614
+ num_chunks,
615
+ reduction,
616
+ use_l2warp,
617
+ l2_penalty_factor,
618
+ accumulate_grad_in_fp32,
619
+ )
620
+
621
+
622
+ class FusedLinearCrossEntropyLoss(nn.Module):
623
+
624
+ def __init__(
625
+ self,
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
+
667
+ assert reduction in ["mean", "sum"], f"reduction: {reduction} is not supported"
668
+
669
+ self.ignore_index = ignore_index
670
+ self.label_smoothing = label_smoothing
671
+ self.logit_scale = logit_scale
672
+ self.logit_softcapping = logit_softcapping
673
+ self.num_chunks = num_chunks
674
+ self.reduction = reduction
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,
705
+ label_smoothing=self.label_smoothing,
706
+ logit_scale=self.logit_scale,
707
+ logit_softcapping=self.logit_softcapping,
708
+ num_chunks=self.num_chunks,
709
+ reduction=self.reduction,
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),)
727
+ self.use_local_output = use_local_output
728
+
729
+ @staticmethod
730
+ def _prepare_input_fn(sequence_sharding, mod, inputs, device_mesh):
731
+ x, target, weight, bias = inputs
732
+
733
+ if not isinstance(x, DTensor):
734
+ # assume the input passed in already sharded on the sequence dim and create the DTensor
735
+ x = DTensor.from_local(x, device_mesh, sequence_sharding)
736
+ if x.placements != sequence_sharding:
737
+ x = x.redistribute(placements=sequence_sharding, async_op=True)
738
+ if not isinstance(target, DTensor):
739
+ target = DTensor.from_local(target, device_mesh, [Replicate()])
740
+ if target.placements != sequence_sharding:
741
+ target = target.redistribute(placements=sequence_sharding, async_op=True)
742
+
743
+ if not isinstance(weight, DTensor):
744
+ weight = DTensor.from_local(weight, device_mesh, [Replicate()])
745
+ if weight.placements != [Replicate()]:
746
+ # we replicate the weight/bias in FLCE
747
+ weight = weight.redistribute(placements=[Replicate()], async_op=True)
748
+
749
+ if bias is not None and not isinstance(bias, DTensor):
750
+ bias = DTensor.from_local(bias, device_mesh, [Replicate()])
751
+ if bias is not None and bias.placements != [Replicate()]:
752
+ bias = bias.redistribute(placements=[Replicate()], async_op=True)
753
+
754
+ return x.to_local(), target.to_local(), weight.to_local(), bias.to_local() if bias is not None else bias
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(
762
+ module,
763
+ device_mesh,
764
+ partition_fn=None,
765
+ input_fn=partial(self._prepare_input_fn, self.sequence_sharding),
766
+ output_fn=partial(self._prepare_output_fn, self.use_local_output),
767
+ )
build/torch-cuda/modules/fused_norm_gate.py ADDED
@@ -0,0 +1,1245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-cuda/modules/grpo.py ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # 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(
71
+ configs=[
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,
79
+ )
80
+ @triton.jit
81
+ def grpo_fwd_kernel(
82
+ logits_ptr,
83
+ ref_logp_ptr,
84
+ input_ids_ptr,
85
+ advantages_ptr,
86
+ completion_mask_ptr,
87
+ loss_ptr,
88
+ lse_ptr,
89
+ beta,
90
+ save_kl: tl.constexpr,
91
+ B,
92
+ M,
93
+ N,
94
+ L,
95
+ start_idx,
96
+ BLOCK_SIZE: tl.constexpr,
97
+ ):
98
+ row_idx = tl.program_id(0)
99
+
100
+ off_b = row_idx // L
101
+ N = tl.cast(N, tl.int64)
102
+
103
+ loss_ptr += row_idx
104
+
105
+ completion_mask_ptr += row_idx
106
+ not_skip = tl.load(completion_mask_ptr).to(tl.int1)
107
+ if not_skip == 1:
108
+ ref_logp_ptr += row_idx
109
+ lse_ptr += row_idx
110
+ advantages_ptr += off_b
111
+ logits_ptr += N * (row_idx + off_b)
112
+ input_ids_ptr += row_idx + (off_b+1) * start_idx
113
+ base_cols = tl.arange(0, BLOCK_SIZE)
114
+
115
+ m_i = -float("inf")
116
+ l_i = 0.0
117
+ for start_n in tl.range(0, N, BLOCK_SIZE):
118
+ cols = start_n + base_cols
119
+ mask = cols < N
120
+ logits = tl.load(logits_ptr+cols, mask=mask, other=-float('inf')).to(tl.float32)
121
+ m_ij = tl.max(logits)
122
+ new_m_i = tl.maximum(m_i, m_ij)
123
+ l_i = l_i * exp(m_i - new_m_i) + tl.sum(exp(logits - new_m_i))
124
+ m_i = new_m_i
125
+ lse = log(l_i) + m_i
126
+
127
+ idx = tl.load(input_ids_ptr)
128
+ x = tl.load(logits_ptr+idx).to(tl.float32)
129
+ advantage = tl.load(advantages_ptr).to(tl.float32)
130
+ ref_logp = tl.load(ref_logp_ptr)
131
+ logp = x - lse
132
+ diff = ref_logp - logp
133
+ kl = exp(diff) - diff - 1
134
+ loss = kl * beta - advantage
135
+
136
+ tl.store(loss_ptr, loss.to(loss_ptr.dtype.element_ty))
137
+ tl.store(lse_ptr, lse.to(lse_ptr.dtype.element_ty))
138
+ if save_kl:
139
+ tl.store(loss_ptr+M, kl.to(loss_ptr.dtype.element_ty))
140
+ else:
141
+ # store 0
142
+ tl.store(loss_ptr, 0.0)
143
+ if save_kl:
144
+ tl.store(loss_ptr+M, 0.0)
145
+
146
+
147
+ @triton.autotune(
148
+ configs=[
149
+ triton.Config({}, num_warps=NUM_WARPS, num_stages=NUM_STAGES)
150
+ for NUM_WARPS in [32]
151
+ for NUM_STAGES in [4]
152
+ ],
153
+ key=['B', 'N'],
154
+ **autotune_cache_kwargs,
155
+ )
156
+ @triton.jit
157
+ def grpo_bwd_kernel(
158
+ dloss_ptr,
159
+ dlogits_ptr,
160
+ logits_ptr,
161
+ ref_logp_ptr,
162
+ input_ids_ptr,
163
+ advantages_ptr,
164
+ completion_mask_ptr,
165
+ lse_ptr,
166
+ beta,
167
+ B,
168
+ N,
169
+ L,
170
+ start_idx,
171
+ BLOCK_SIZE: tl.constexpr,
172
+ ):
173
+
174
+ row_idx = tl.program_id(0) # B*L
175
+ off_b = row_idx // L
176
+
177
+ N = tl.cast(N, tl.int64)
178
+
179
+ dlogits_ptr += N * (row_idx + off_b)
180
+ base_cols = tl.arange(0, BLOCK_SIZE)
181
+ completion_mask_ptr += row_idx
182
+ not_skip = tl.load(completion_mask_ptr).to(tl.int1)
183
+
184
+ if not_skip == 1:
185
+ lse_ptr += row_idx
186
+ dloss_ptr += row_idx
187
+ advantages_ptr += off_b
188
+ ref_logp_ptr += row_idx
189
+ logits_ptr += N * (row_idx + off_b)
190
+ input_ids_ptr += row_idx + (off_b+1) * start_idx
191
+ dloss = tl.load(dloss_ptr).to(tl.float32)
192
+ lse = tl.load(lse_ptr).to(tl.float32)
193
+ idx = tl.load(input_ids_ptr)
194
+ x = tl.load(logits_ptr+idx).to(tl.float32)
195
+ advantage = tl.load(advantages_ptr).to(tl.float32)
196
+ ref_logp = tl.load(ref_logp_ptr)
197
+ # Need for in-place grad.
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
206
+ mask = cols < N
207
+ logits = tl.load(logits_ptr+cols, mask=mask, other=-float('inf')).to(tl.float32)
208
+ probs = exp(logits - lse)
209
+ dlogits = tl.where(cols == idx, 1-probs, -probs) * dlogp
210
+
211
+ tl.store(dlogits_ptr+cols, dlogits.to(dlogits_ptr.dtype.element_ty), mask=mask)
212
+ else:
213
+ dlogits = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
214
+ for start_n in tl.range(0, N, BLOCK_SIZE):
215
+ cols = start_n + base_cols
216
+ mask = cols < N
217
+
218
+ tl.store(dlogits_ptr+cols, dlogits.to(dlogits_ptr.dtype.element_ty), mask=mask)
219
+
220
+
221
+ class GrpoLoss(torch.autograd.Function):
222
+
223
+ @input_guard
224
+ @staticmethod
225
+ def forward(ctx, logits, ref_logp, input_ids, advantages, beta, completion_mask, save_kl, inplace=True):
226
+ ctx.input_shape = logits.shape
227
+ B, L_ADD_1, N = ctx.input_shape
228
+ L = L_ADD_1 - 1
229
+ M = B * L
230
+ input_ids_start_index = input_ids.size(1) - L
231
+
232
+ if not save_kl:
233
+ loss = torch.empty(B, L, device=logits.device, dtype=torch.float32)
234
+ else:
235
+ loss = torch.empty(B*2, L, device=logits.device, dtype=torch.float32)
236
+
237
+ lse = torch.empty(B, L, device=logits.device, dtype=torch.float32)
238
+
239
+ if completion_mask is None:
240
+ completion_mask = torch.ones(B, L, device=logits.device, dtype=torch.int32)
241
+ else:
242
+ loss[:B].masked_fill_(completion_mask.logical_not(), 0.0)
243
+
244
+ grpo_fwd_kernel[(M,)](
245
+ logits_ptr=logits,
246
+ ref_logp_ptr=ref_logp,
247
+ input_ids_ptr=input_ids,
248
+ advantages_ptr=advantages,
249
+ completion_mask_ptr=completion_mask,
250
+ loss_ptr=loss,
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
258
+ ctx.save_for_backward(lse, logits, input_ids, advantages, completion_mask)
259
+ ctx.ref_logp = ref_logp
260
+ ctx.inplace = inplace
261
+ return loss
262
+
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
270
+ L = L_ADD_1 - 1
271
+ M = B * L
272
+
273
+ input_ids_start_index = input_ids.size(1) - L
274
+
275
+ # B, L_ADD_1, N
276
+ dlogits = logits if inplace else torch.empty_like(logits)
277
+ BN = min(65536, triton.next_power_of_2(N))
278
+
279
+ grpo_bwd_kernel[(M,)](
280
+ dloss_ptr=dloss,
281
+ dlogits_ptr=dlogits,
282
+ logits_ptr=logits,
283
+ ref_logp_ptr=ctx.ref_logp,
284
+ input_ids_ptr=input_ids,
285
+ advantages_ptr=advantages,
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
332
+ else:
333
+ return out.chunk(2, axis=0)
334
+
335
+
336
+ def grpo_loss_torch(logits, ref_logp, input_ids, advantages, beta=0.1, completion_mask=None, save_kl=False):
337
+ def get_log_probs(logits, input_ids):
338
+ per_token_logps = []
339
+ for logits_row, input_ids_row in zip(logits, input_ids[:, -logits.size(1):], strict=False):
340
+ log_probs = logits_row.log_softmax(dim=-1)
341
+ token_log_prob = torch.gather(log_probs, dim=1, index=input_ids_row.unsqueeze(1)).squeeze(1)
342
+ per_token_logps.append(token_log_prob)
343
+ return torch.stack(per_token_logps)
344
+
345
+ logits = logits[:, :-1]
346
+ per_token_logps = get_log_probs(logits, input_ids)
347
+ ref_per_token_logps = ref_logp
348
+ per_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
349
+
350
+ per_token_loss = torch.exp(per_token_logps - per_token_logps.detach()) * advantages.unsqueeze(1)
351
+ per_token_loss = -(per_token_loss - beta * per_token_kl)
352
+ if completion_mask is not None:
353
+ per_token_loss *= completion_mask
354
+ if save_kl:
355
+ per_token_kl *= completion_mask
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,
363
+ old_logps: torch.Tensor,
364
+ pad_mask: torch.Tensor,
365
+ logits_to_keep: int,
366
+ rewards: torch.Tensor,
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
build/torch-cuda/modules/l2norm.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 torch.nn as nn
10
+ import triton
11
+ import triton.language as tl
12
+
13
+ from ..ops.utils.cache import fla_cache_autotune
14
+ from ..utils import IS_AMD, autotune_cache_kwargs, input_guard
15
+
16
+ BT_LIST = [8, 16, 32, 64, 128]
17
+ NUM_WARPS_AUTOTUNE = [1, 2, 4, 8, 16] if IS_AMD else [1, 2, 4, 8, 16, 32]
18
+
19
+
20
+ @triton.autotune(
21
+ configs=[triton.Config({}, num_warps=num_warps) for num_warps in NUM_WARPS_AUTOTUNE],
22
+ key=["D"],
23
+ **autotune_cache_kwargs,
24
+ )
25
+ @triton.jit
26
+ def l2norm_fwd_kernel1(
27
+ x,
28
+ y,
29
+ rstd,
30
+ eps,
31
+ D,
32
+ BD: tl.constexpr,
33
+ ):
34
+ i_t = tl.program_id(0)
35
+ x += i_t * D
36
+ y += i_t * D
37
+ # Compute mean and variance
38
+ cols = tl.arange(0, BD)
39
+ mask = cols < D
40
+
41
+ b_x = tl.load(x + cols, mask=mask, other=0.0).to(tl.float32)
42
+ b_rstd = 1 / tl.sqrt(tl.sum(b_x * b_x) + eps)
43
+ b_y = b_x * b_rstd
44
+ tl.store(y + cols, b_y, mask=mask)
45
+ tl.store(rstd + i_t, b_rstd)
46
+
47
+
48
+ @triton.autotune(
49
+ configs=[triton.Config({}, num_warps=num_warps) for num_warps in NUM_WARPS_AUTOTUNE],
50
+ key=["D"],
51
+ **autotune_cache_kwargs,
52
+ )
53
+ @triton.jit
54
+ def l2norm_bwd_kernel1(
55
+ y,
56
+ rstd,
57
+ dy,
58
+ dx,
59
+ eps,
60
+ D,
61
+ BD: tl.constexpr,
62
+ ):
63
+ i_t = tl.program_id(0)
64
+ y += i_t * D
65
+ dx += i_t * D
66
+ dy += i_t * D
67
+
68
+ cols = tl.arange(0, BD)
69
+ mask = cols < D
70
+ b_y = tl.load(y + cols, mask=mask, other=0.0).to(tl.float32)
71
+ b_rstd = tl.load(rstd + i_t).to(tl.float32)
72
+ b_dy = tl.load(dy + cols, mask=mask, other=0.0).to(tl.float32)
73
+ b_dx = b_dy * b_rstd - tl.sum(b_dy * b_y) * b_y * b_rstd
74
+ tl.store(dx + cols, b_dx, mask=mask)
75
+
76
+
77
+ @fla_cache_autotune(
78
+ configs=[triton.Config({"BT": BT}, num_warps=num_warps) for num_warps in [1, 2, 4, 8, 16] for BT in BT_LIST],
79
+ key=["D", "NB"],
80
+ **autotune_cache_kwargs,
81
+ )
82
+ @triton.jit(do_not_specialize=["T"])
83
+ def l2norm_fwd_kernel(
84
+ x,
85
+ y,
86
+ rstd,
87
+ eps,
88
+ T,
89
+ D: tl.constexpr,
90
+ BD: tl.constexpr,
91
+ NB: tl.constexpr,
92
+ BT: tl.constexpr,
93
+ ):
94
+ i_t = tl.program_id(0)
95
+ p_x = tl.make_block_ptr(x, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
96
+ p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
97
+ p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t * BT,), (BT,), (0,))
98
+
99
+ b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
100
+ b_rstd = 1 / tl.sqrt(tl.sum(b_x * b_x, 1) + eps)
101
+ b_y = b_x * b_rstd[:, None]
102
+
103
+ tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
104
+ tl.store(p_rstd, b_rstd.to(p_rstd.dtype.element_ty), boundary_check=(0,))
105
+
106
+
107
+ @fla_cache_autotune(
108
+ configs=[triton.Config({"BT": BT}, num_warps=num_warps) for num_warps in [1, 2, 4, 8, 16] for BT in BT_LIST],
109
+ key=["D", "NB"],
110
+ **autotune_cache_kwargs,
111
+ )
112
+ @triton.jit(do_not_specialize=["T"])
113
+ def l2norm_bwd_kernel(
114
+ y,
115
+ rstd,
116
+ dy,
117
+ dx,
118
+ eps,
119
+ T,
120
+ D: tl.constexpr,
121
+ BD: tl.constexpr,
122
+ NB: tl.constexpr,
123
+ BT: tl.constexpr,
124
+ ):
125
+ i_t = tl.program_id(0)
126
+ p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
127
+ p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t * BT,), (BT,), (0,))
128
+ p_dy = tl.make_block_ptr(dy, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
129
+ p_dx = tl.make_block_ptr(dx, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
130
+
131
+ b_y = tl.load(p_y, boundary_check=(0, 1)).to(tl.float32)
132
+ b_rstd = tl.load(p_rstd, boundary_check=(0,)).to(tl.float32)
133
+ b_dy = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
134
+ b_dx = b_dy * b_rstd[:, None] - tl.sum(b_dy * b_y, 1)[:, None] * b_y * b_rstd[:, None]
135
+ tl.store(p_dx, b_dx.to(p_dx.dtype.element_ty), boundary_check=(0, 1))
136
+
137
+
138
+ def l2norm_fwd(
139
+ x: torch.Tensor,
140
+ eps: float = 1e-6,
141
+ output_dtype: torch.dtype | None = None,
142
+ ):
143
+ x_shape_og = x.shape
144
+ x = x.view(-1, x.shape[-1])
145
+ # allocate output
146
+ if output_dtype is None:
147
+ y = torch.empty_like(x)
148
+ else:
149
+ y = torch.empty_like(x, dtype=output_dtype)
150
+ assert y.stride(-1) == 1
151
+ T, D = x.shape[0], x.shape[-1]
152
+ # Less than 64KB per feature: enqueue fused kernel
153
+ MAX_FUSED_SIZE = 65536 // x.element_size()
154
+ BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
155
+ if D > BD:
156
+ raise RuntimeError("This layer doesn't support feature dim >= 64KB.")
157
+
158
+ rstd = torch.empty((T,), dtype=torch.float32, device=x.device)
159
+ if D <= 512:
160
+ # NOTE(tylerr): Avoid excessive recompilation and autotuning by tolerating a larger range
161
+ # of T before recompiling the kernel.
162
+ # NB = triton.cdiv(T, 2048)
163
+ NB = triton.cdiv(T, 2048 * 32)
164
+
165
+ def grid(meta):
166
+ return (triton.cdiv(T, meta["BT"]),)
167
+
168
+ l2norm_fwd_kernel[grid](
169
+ x=x,
170
+ y=y,
171
+ rstd=rstd,
172
+ eps=eps,
173
+ T=T,
174
+ D=D,
175
+ BD=BD,
176
+ NB=NB,
177
+ )
178
+ else:
179
+ l2norm_fwd_kernel1[(T,)](
180
+ x=x,
181
+ y=y,
182
+ rstd=rstd,
183
+ eps=eps,
184
+ D=D,
185
+ BD=BD,
186
+ )
187
+ return y.view(x_shape_og), rstd.view(x_shape_og[:-1])
188
+
189
+
190
+ def l2norm_bwd(
191
+ y: torch.Tensor,
192
+ rstd: torch.Tensor,
193
+ dy: torch.Tensor,
194
+ eps: float = 1e-6,
195
+ ):
196
+ y_shape_og = y.shape
197
+ y = y.view(-1, dy.shape[-1])
198
+ dy = dy.view(-1, dy.shape[-1])
199
+ assert dy.shape == y.shape
200
+ # allocate output
201
+ dx = torch.empty_like(y)
202
+ T, D = y.shape[0], y.shape[-1]
203
+ # Less than 64KB per feature: enqueue fused kernel
204
+ MAX_FUSED_SIZE = 65536 // y.element_size()
205
+ BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
206
+ if D > BD:
207
+ raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
208
+
209
+ if D <= 512:
210
+ # NOTE(tylerr): Avoid excessive recompilation and autotuning by tolerating a larger range
211
+ # of T before recompiling the kernel.
212
+ # NB = triton.cdiv(T, 2048)
213
+ NB = triton.cdiv(T, 2048 * 32)
214
+
215
+ def grid(meta):
216
+ return (triton.cdiv(T, meta["BT"]),)
217
+
218
+ l2norm_bwd_kernel[grid](
219
+ y=y,
220
+ rstd=rstd,
221
+ dy=dy,
222
+ dx=dx,
223
+ eps=eps,
224
+ T=T,
225
+ D=D,
226
+ BD=BD,
227
+ NB=NB,
228
+ )
229
+ else:
230
+ l2norm_bwd_kernel1[(T,)](
231
+ y=y,
232
+ rstd=rstd,
233
+ dy=dy,
234
+ dx=dx,
235
+ eps=eps,
236
+ D=D,
237
+ BD=BD,
238
+ )
239
+
240
+ return dx.view(y_shape_og)
241
+
242
+
243
+ class L2NormFunction(torch.autograd.Function):
244
+ @staticmethod
245
+ @input_guard
246
+ def forward(
247
+ ctx,
248
+ x,
249
+ eps=1e-6,
250
+ output_dtype=None,
251
+ ):
252
+ y, rstd = l2norm_fwd(x, eps, output_dtype)
253
+ ctx.eps = eps
254
+ ctx.x_dtype = x.dtype
255
+ ctx.save_for_backward(y, rstd)
256
+ return y
257
+
258
+ @staticmethod
259
+ @input_guard
260
+ def backward(ctx, dy):
261
+ y, rstd = ctx.saved_tensors
262
+ dx = l2norm_bwd(y, rstd, dy, ctx.eps)
263
+ return dx, None, None
264
+
265
+
266
+ def l2norm(
267
+ x: torch.Tensor,
268
+ eps: float = 1e-6,
269
+ output_dtype: torch.dtype | None = None,
270
+ ) -> torch.Tensor:
271
+ return L2NormFunction.apply(x, eps, output_dtype)
272
+
273
+
274
+ l2_norm = l2norm
275
+
276
+
277
+ class L2Norm(nn.Module):
278
+ def __init__(
279
+ self,
280
+ eps: float = 1e-6,
281
+ output_dtype: torch.dtype | None = None,
282
+ ):
283
+ super().__init__()
284
+ self.eps = eps
285
+ self.output_dtype = output_dtype
286
+
287
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
288
+ return l2norm(x, self.eps, self.output_dtype)
build/torch-cuda/modules/l2warp.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
10
+
11
+ class L2Wrap(torch.autograd.Function):
12
+ r"""
13
+ This class of penalty prevents the model from becoming overconfident,
14
+ thereby mitigating precision loss in BF16.
15
+
16
+ This version is memory-optimized by not storing the full logits tensor.
17
+ """
18
+ @staticmethod
19
+ def forward(
20
+ ctx,
21
+ loss: torch.Tensor,
22
+ logits: torch.Tensor,
23
+ l2_penalty_factor: float = 1e-4,
24
+ ) -> torch.Tensor:
25
+ """
26
+ Args:
27
+ loss (torch.Tensor):
28
+ The already-reduced (scalar) loss to wrap.
29
+ logits (torch.Tensor):
30
+ The logits of shape `[B, T, V]`.
31
+ l2_penalty_factor (float, Optional):
32
+ The strength of the L2 penalty on the max logit. Default: 1e-4.
33
+ """
34
+ maxx, ids = torch.max(logits, dim=-1, keepdim=True)
35
+ ctx.logits_shape = logits.shape
36
+ factor = l2_penalty_factor / (logits.shape[0] * logits.shape[1])
37
+ maxx = maxx * factor
38
+ ctx.save_for_backward(maxx, ids)
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
build/torch-cuda/modules/layernorm.py ADDED
@@ -0,0 +1,1472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # Copyright (c) 2023, Tri Dao
9
+ # https://github.com/state-spaces/mamba/blob/fb7b5310fa865dbd62aa059b1e26f2b431363e2a/mamba_ssm/ops/triton/layernorm.py
10
+ # Implement residual + layer_norm / rms_norm.
11
+
12
+ # Based on the Triton LayerNorm tutorial: https://triton-lang.org/main/getting-started/tutorials/05-layer-norm.html
13
+ # For the backward pass, we keep weight_grad and bias_grad in registers and accumulate.
14
+ # This is faster for dimensions up to 8k, but after that it's much slower due to register spilling.
15
+ # The models we train have hidden dim up to 8k anyway (e.g. Llama 70B), so this is fine.
16
+
17
+ from __future__ import annotations
18
+
19
+ from functools import partial
20
+
21
+ import torch
22
+ import torch.nn as nn
23
+ import torch.nn.functional as F
24
+ import triton
25
+ import triton.language as tl
26
+ from einops import rearrange
27
+ from torch.distributed import DeviceMesh
28
+ from torch.distributed.tensor import Replicate, Shard, distribute_module
29
+ from torch.distributed.tensor.parallel import ParallelStyle
30
+
31
+ from ..modules.backends import dispatch
32
+ from ..utils import autotune_cache_kwargs, get_multiprocessor_count, input_guard
33
+
34
+ try:
35
+ from torch.distributed.tensor import DTensor
36
+ except (ImportError, AttributeError):
37
+ DTensor = None
38
+
39
+
40
+ def layer_norm_ref(
41
+ x: torch.Tensor,
42
+ weight: torch.Tensor,
43
+ bias: torch.Tensor,
44
+ residual: torch.Tensor = None,
45
+ eps: float = 1e-5,
46
+ prenorm: bool = False,
47
+ upcast: bool = False,
48
+ ):
49
+ dtype = x.dtype
50
+ if upcast:
51
+ weight = weight.float()
52
+ bias = bias.float() if bias is not None else None
53
+ if upcast:
54
+ x = x.float()
55
+ residual = residual.float() if residual is not None else residual
56
+ if residual is not None:
57
+ x = (x + residual).to(x.dtype)
58
+ out = F.layer_norm(x.to(weight.dtype), x.shape[-1:], weight=weight, bias=bias, eps=eps).to(
59
+ dtype,
60
+ )
61
+ return out if not prenorm else (out, x)
62
+
63
+
64
+ def rms_norm_ref(
65
+ x: torch.Tensor,
66
+ weight: torch.Tensor,
67
+ bias: torch.Tensor,
68
+ residual: torch.Tensor = None,
69
+ eps: float = 1e-5,
70
+ prenorm: bool = False,
71
+ upcast: bool = False,
72
+ ):
73
+ dtype = x.dtype
74
+ if upcast:
75
+ weight = weight.float()
76
+ bias = bias.float() if bias is not None else None
77
+ if upcast:
78
+ x = x.float()
79
+ residual = residual.float() if residual is not None else residual
80
+ if residual is not None:
81
+ x = (x + residual).to(x.dtype)
82
+ rstd = 1 / torch.sqrt((x.square()).mean(dim=-1, keepdim=True) + eps)
83
+ out = (x * rstd * weight) + bias if bias is not None else (x * rstd * weight)
84
+ out = out.to(dtype)
85
+ return out if not prenorm else (out, x)
86
+
87
+
88
+ def group_norm_ref(
89
+ x: torch.Tensor,
90
+ weight: torch.Tensor,
91
+ bias: torch.Tensor,
92
+ num_groups: int,
93
+ residual: torch.Tensor = None,
94
+ eps: float = 1e-5,
95
+ is_rms_norm: bool = False,
96
+ prenorm: bool = False,
97
+ upcast: bool = False,
98
+ ):
99
+ dtype = x.dtype
100
+ if upcast:
101
+ weight = weight.float()
102
+ bias = bias.float() if bias is not None else None
103
+ if upcast:
104
+ x = x.float()
105
+ residual = residual.float() if residual is not None else residual
106
+ if residual is not None:
107
+ x = (x + residual).to(x.dtype)
108
+ residual = x
109
+ x, weight = [
110
+ rearrange(data, "... (g d) -> ... g d", g=num_groups) for data in (x, weight)
111
+ ]
112
+ if bias is not None:
113
+ bias = rearrange(bias, '... (g d) -> ... g d', g=num_groups)
114
+ if not is_rms_norm:
115
+ mean = x.mean(dim=-1, keepdim=True)
116
+ x = x - mean
117
+ rstd = 1 / torch.sqrt((x.square()).mean(dim=-1, keepdim=True) + eps)
118
+ out = (x * rstd * weight) + bias if bias is not None else (x * rstd * weight)
119
+ out = rearrange(out, "... g d -> ... (g d)")
120
+ out = out.to(dtype)
121
+ return out if not prenorm else (out, residual)
122
+
123
+
124
+ class GroupNormRef(nn.Module):
125
+
126
+ def __init__(
127
+ self,
128
+ num_groups: int,
129
+ hidden_size: int,
130
+ elementwise_affine: bool = True,
131
+ bias: bool = False,
132
+ eps: float = 1e-5,
133
+ is_rms_norm: bool = False,
134
+ ) -> GroupNormRef:
135
+ super().__init__()
136
+
137
+ if hidden_size % num_groups != 0:
138
+ raise ValueError('num_channels must be divisible by num_groups')
139
+
140
+ self.num_groups = num_groups
141
+ self.hidden_size = hidden_size
142
+ self.elementwise_affine = elementwise_affine
143
+ self.eps = eps
144
+ self.is_rms_norm = is_rms_norm
145
+
146
+ self.register_parameter("weight", None)
147
+ self.register_parameter("bias", None)
148
+ if elementwise_affine:
149
+ self.weight = nn.Parameter(torch.empty(hidden_size))
150
+ if bias:
151
+ self.bias = nn.Parameter(torch.empty(hidden_size))
152
+
153
+ self.reset_parameters()
154
+
155
+ def reset_parameters(self):
156
+ if self.elementwise_affine:
157
+ nn.init.ones_(self.weight)
158
+ if self.bias is not None:
159
+ nn.init.zeros_(self.bias)
160
+
161
+ def __repr__(self) -> str:
162
+ s = f"{self.__class__.__name__}({self.num_groups}, {self.hidden_size}"
163
+ if not self.elementwise_affine:
164
+ s += f", elementwise_affine={self.elementwise_affine}"
165
+ if self.is_rms_norm:
166
+ s += f", is_rms_norm={self.is_rms_norm}"
167
+ s += f", eps={self.eps}"
168
+ s += ")"
169
+ return s
170
+
171
+ def forward(self, x, residual=None, prenorm=False):
172
+ return group_norm_ref(
173
+ x,
174
+ self.weight,
175
+ self.bias,
176
+ num_groups=self.num_groups,
177
+ residual=residual,
178
+ eps=self.eps,
179
+ is_rms_norm=self.is_rms_norm,
180
+ prenorm=prenorm,
181
+ upcast=True,
182
+ )
183
+
184
+
185
+ @triton.autotune(
186
+ configs=[
187
+ triton.Config({'BT': BT}, num_warps=num_warps)
188
+ for BT in [32, 64, 128]
189
+ for num_warps in [2, 4, 8]
190
+ ],
191
+ key=['D', 'NB', 'HAS_RESIDUAL', 'STORE_RESIDUAL_OUT', 'IS_RMS_NORM'],
192
+ **autotune_cache_kwargs,
193
+ )
194
+ @triton.jit
195
+ def layer_norm_fwd_kernel(
196
+ x, # pointer to the input
197
+ y, # pointer to the output
198
+ w, # pointer to the weights
199
+ b, # pointer to the biases
200
+ res, # pointer to the res
201
+ res_out, # pointer to the res
202
+ mean, # pointer to the mean
203
+ rstd, # pointer to the 1/std
204
+ eps, # epsilon to avoid division by zero
205
+ T,
206
+ G: tl.constexpr,
207
+ D: tl.constexpr,
208
+ BT: tl.constexpr,
209
+ BD: tl.constexpr,
210
+ NB: tl.constexpr,
211
+ IS_RMS_NORM: tl.constexpr,
212
+ HAS_RESIDUAL: tl.constexpr,
213
+ STORE_RESIDUAL_OUT: tl.constexpr,
214
+ HAS_WEIGHT: tl.constexpr,
215
+ HAS_BIAS: tl.constexpr,
216
+ ):
217
+ i_t = tl.program_id(0)
218
+
219
+ o_t = i_t * BT + tl.arange(0, BT)
220
+ o_g = o_t % G
221
+ o_d = tl.arange(0, BD)
222
+ m_d = o_d < D
223
+
224
+ p_x = tl.make_block_ptr(x, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
225
+ b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
226
+ if HAS_RESIDUAL:
227
+ p_res = tl.make_block_ptr(res, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
228
+ b_x += tl.load(p_res, boundary_check=(0, 1)).to(tl.float32)
229
+ if STORE_RESIDUAL_OUT:
230
+ p_res_out = tl.make_block_ptr(res_out, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
231
+ tl.store(p_res_out, b_x.to(p_res_out.dtype.element_ty), boundary_check=(0, 1))
232
+ if not IS_RMS_NORM:
233
+ b_mean = tl.sum(b_x, axis=1) / D
234
+ p_mean = tl.make_block_ptr(mean, (T,), (1,), (i_t * BT,), (BT,), (0,))
235
+ tl.store(p_mean, b_mean.to(p_mean.dtype.element_ty), boundary_check=(0,))
236
+ b_xbar = tl.where(m_d[None, :], b_x - b_mean[:, None], 0.0)
237
+ b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
238
+ else:
239
+ b_xbar = tl.where(m_d[None, :], b_x, 0.0)
240
+ b_var = tl.sum(b_xbar * b_xbar, axis=1) / D
241
+ b_rstd = 1 / tl.sqrt(b_var + eps)
242
+
243
+ p_rstd = tl.make_block_ptr(rstd, (T,), (1,), (i_t * BT,), (BT,), (0,))
244
+ tl.store(p_rstd, b_rstd.to(p_rstd.dtype.element_ty), boundary_check=(0,))
245
+
246
+ if HAS_WEIGHT:
247
+ b_w = tl.load(w + o_g[:, None] * D + o_d[None, :], mask=m_d[None, :]).to(tl.float32)
248
+ if HAS_BIAS:
249
+ b_b = tl.load(b + o_g[:, None] * D + o_d[None, :], mask=m_d[None, :]).to(tl.float32)
250
+ b_x_hat = (b_x - b_mean[:, None]) * b_rstd[:, None] if not IS_RMS_NORM else b_x * b_rstd[:, None]
251
+ b_y = b_x_hat * b_w if HAS_WEIGHT else b_x_hat
252
+ if HAS_BIAS:
253
+ b_y = b_y + b_b
254
+
255
+ # Write output
256
+ p_y = tl.make_block_ptr(y, (T, D), (D, 1), (i_t * BT, 0), (BT, BD), (1, 0))
257
+ tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
258
+
259
+
260
+ @triton.autotune(
261
+ configs=[
262
+ triton.Config({}, num_warps=num_warps)
263
+ for num_warps in [2, 4, 8, 16]
264
+ ],
265
+ key=['D', 'HAS_RESIDUAL', 'STORE_RESIDUAL_OUT', 'IS_RMS_NORM'],
266
+ **autotune_cache_kwargs,
267
+ )
268
+ @triton.jit
269
+ def layer_norm_fwd_kernel1(
270
+ x, # pointer to the input
271
+ y, # pointer to the output
272
+ w, # pointer to the weights
273
+ b, # pointer to the biases
274
+ res, # pointer to the res
275
+ res_out, # pointer to the res
276
+ mean, # pointer to the mean
277
+ rstd, # pointer to the 1/std
278
+ eps, # epsilon to avoid division by zero
279
+ G: tl.constexpr,
280
+ D: tl.constexpr,
281
+ BD: tl.constexpr,
282
+ IS_RMS_NORM: tl.constexpr,
283
+ HAS_RESIDUAL: tl.constexpr,
284
+ STORE_RESIDUAL_OUT: tl.constexpr,
285
+ HAS_WEIGHT: tl.constexpr,
286
+ HAS_BIAS: tl.constexpr,
287
+ ):
288
+ i_t = tl.program_id(0)
289
+ i_g = i_t % G
290
+
291
+ x += i_t * D
292
+ y += i_t * D
293
+ if HAS_RESIDUAL:
294
+ res += i_t * D
295
+ if STORE_RESIDUAL_OUT:
296
+ res_out += i_t * D
297
+
298
+ o_d = tl.arange(0, BD)
299
+ m_d = o_d < D
300
+ b_x = tl.load(x + o_d, mask=m_d, other=0.0).to(tl.float32)
301
+ if HAS_RESIDUAL:
302
+ b_x += tl.load(res + o_d, mask=m_d, other=0.0).to(tl.float32)
303
+ if STORE_RESIDUAL_OUT:
304
+ tl.store(res_out + o_d, b_x, mask=m_d)
305
+ if not IS_RMS_NORM:
306
+ b_mean = tl.sum(b_x, axis=0) / D
307
+ tl.store(mean + i_t, b_mean)
308
+ b_xbar = tl.where(m_d, b_x - b_mean, 0.0)
309
+ b_var = tl.sum(b_xbar * b_xbar, axis=0) / D
310
+ else:
311
+ b_xbar = tl.where(m_d, b_x, 0.0)
312
+ b_var = tl.sum(b_xbar * b_xbar, axis=0) / D
313
+ b_rstd = 1 / tl.sqrt(b_var + eps)
314
+ tl.store(rstd + i_t, b_rstd)
315
+
316
+ if HAS_WEIGHT:
317
+ b_w = tl.load(w + i_g * D + o_d, mask=m_d).to(tl.float32)
318
+ if HAS_BIAS:
319
+ b_b = tl.load(b + i_g * D + o_d, mask=m_d).to(tl.float32)
320
+ b_x_hat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
321
+ b_y = b_x_hat * b_w if HAS_WEIGHT else b_x_hat
322
+ if HAS_BIAS:
323
+ b_y = b_y + b_b
324
+
325
+ # Write output
326
+ tl.store(y + o_d, b_y, mask=m_d)
327
+
328
+
329
+ @triton.heuristics({
330
+ 'RECOMPUTE_OUTPUT': lambda args: args['y'] is not None,
331
+ })
332
+ @triton.autotune(
333
+ configs=[
334
+ triton.Config({'BT': BT}, num_warps=num_warps)
335
+ for BT in [32, 64]
336
+ for num_warps in [2, 4, 8]
337
+ ],
338
+ key=['D', 'NB', 'HAS_DRESIDUAL', 'STORE_DRESIDUAL', 'IS_RMS_NORM'],
339
+ **autotune_cache_kwargs,
340
+ )
341
+ @triton.jit
342
+ def layer_norm_bwd_kernel(
343
+ x, # pointer to the input
344
+ w, # pointer to the weights
345
+ b, # pointer to the biases
346
+ y, # pointer to the output to be recomputed
347
+ dy, # pointer to the output gradient
348
+ dx, # pointer to the input gradient
349
+ dw, # pointer to the partial sum of weights gradient
350
+ db, # pointer to the partial sum of biases gradient
351
+ dres,
352
+ dres_in,
353
+ mean,
354
+ rstd,
355
+ T,
356
+ G: tl.constexpr,
357
+ D: tl.constexpr,
358
+ BS: tl.constexpr,
359
+ BT: tl.constexpr,
360
+ BD: tl.constexpr,
361
+ NB: tl.constexpr,
362
+ GS: tl.constexpr,
363
+ IS_RMS_NORM: tl.constexpr,
364
+ HAS_DRESIDUAL: tl.constexpr,
365
+ STORE_DRESIDUAL: tl.constexpr,
366
+ HAS_WEIGHT: tl.constexpr,
367
+ HAS_BIAS: tl.constexpr,
368
+ RECOMPUTE_OUTPUT: tl.constexpr,
369
+ ):
370
+ i_s = tl.program_id(0)
371
+ i_g, i_sg = i_s // GS, i_s % GS
372
+
373
+ o_d = tl.arange(0, BD)
374
+ m_d = o_d < D
375
+ if HAS_WEIGHT:
376
+ b_w = tl.load(w + i_g * D + o_d, mask=m_d).to(tl.float32)
377
+ b_dw = tl.zeros((BT, BD), dtype=tl.float32)
378
+ if HAS_BIAS:
379
+ b_b = tl.load(b + i_g * D + o_d, mask=m_d, other=0.0).to(tl.float32)
380
+ b_db = tl.zeros((BT, BD), dtype=tl.float32)
381
+
382
+ # Tg: number of tokens per group, used as the logical shape for make_block_ptr.
383
+ # for mean/rstd with shape (T,) and stride (G,), the strided view has Tg elements per group.
384
+ # the caller guarantees NS capped so every program has work.
385
+ # the last program's range may slightly exceed Tg (since BS = cdiv(T, NS));
386
+ # boundary_check handles the partial tail tile, m_t < Tg masks dw/db accumulation.
387
+ Tg = T // G
388
+ for i_t in range(i_sg * BS, i_sg * BS + BS, BT):
389
+ p_x = tl.make_block_ptr(x + i_g * D, (Tg, D), (G*D, 1), (i_t, 0), (BT, BD), (1, 0))
390
+ p_dy = tl.make_block_ptr(dy + i_g * D, (Tg, D), (G*D, 1), (i_t, 0), (BT, BD), (1, 0))
391
+ p_dx = tl.make_block_ptr(dx + i_g * D, (Tg, D), (G*D, 1), (i_t, 0), (BT, BD), (1, 0))
392
+ # [BT, BD]
393
+ b_x = tl.load(p_x, boundary_check=(0, 1)).to(tl.float32)
394
+ b_dy = tl.load(p_dy, boundary_check=(0, 1)).to(tl.float32)
395
+
396
+ if not IS_RMS_NORM:
397
+ p_mean = tl.make_block_ptr(mean + i_g, (Tg,), (G,), (i_t,), (BT,), (0,))
398
+ b_mean = tl.load(p_mean, boundary_check=(0,))
399
+ p_rstd = tl.make_block_ptr(rstd + i_g, (Tg,), (G,), (i_t,), (BT,), (0,))
400
+ b_rstd = tl.load(p_rstd, boundary_check=(0,))
401
+ # Compute dx
402
+ b_xhat = (b_x - b_mean[:, None]) * b_rstd[:, None] if not IS_RMS_NORM else b_x * b_rstd[:, None]
403
+ b_xhat = tl.where(m_d[None, :], b_xhat, 0.0)
404
+
405
+ b_y = b_xhat * b_w[None, :] if HAS_WEIGHT else b_xhat
406
+ if HAS_BIAS:
407
+ b_y = b_y + b_b[None, :]
408
+ if RECOMPUTE_OUTPUT:
409
+ p_y = tl.make_block_ptr(y + i_g * D, (Tg, D), (G*D, 1), (i_t, 0), (BT, BD), (1, 0))
410
+ tl.store(p_y, b_y.to(p_y.dtype.element_ty), boundary_check=(0, 1))
411
+
412
+ b_wdy = b_dy
413
+
414
+ if HAS_WEIGHT or HAS_BIAS:
415
+ # when BT > BS, a tile may span into the next program's range;
416
+ # mask to this program's upper bound to avoid double-counting dw/db.
417
+ m_t = (i_t + tl.arange(0, BT)) < min(i_sg * BS + BS, Tg)
418
+ if HAS_WEIGHT:
419
+ b_wdy = b_dy * b_w
420
+ b_dw += tl.where(m_t[:, None], b_dy * b_xhat, 0.0)
421
+ if HAS_BIAS:
422
+ b_db += tl.where(m_t[:, None], b_dy, 0.0)
423
+ if not IS_RMS_NORM:
424
+ b_c1 = tl.sum(b_xhat * b_wdy, axis=1) / D
425
+ b_c2 = tl.sum(b_wdy, axis=1) / D
426
+ b_dx = (b_wdy - (b_xhat * b_c1[:, None] + b_c2[:, None])) * b_rstd[:, None]
427
+ else:
428
+ b_c1 = tl.sum(b_xhat * b_wdy, axis=1) / D
429
+ b_dx = (b_wdy - b_xhat * b_c1[:, None]) * b_rstd[:, None]
430
+ if HAS_DRESIDUAL:
431
+ p_dres = tl.make_block_ptr(dres + i_g * D, (Tg, D), (G*D, 1), (i_t, 0), (BT, BD), (1, 0))
432
+ b_dres = tl.load(p_dres, boundary_check=(0, 1)).to(tl.float32)
433
+ b_dx += b_dres
434
+ # Write dx
435
+ if STORE_DRESIDUAL:
436
+ p_dres_in = tl.make_block_ptr(dres_in + i_g * D, (Tg, D), (G*D, 1), (i_t, 0), (BT, BD), (1, 0))
437
+ tl.store(p_dres_in, b_dx.to(p_dres_in.dtype.element_ty), boundary_check=(0, 1))
438
+
439
+ tl.store(p_dx, b_dx.to(p_dx.dtype.element_ty), boundary_check=(0, 1))
440
+
441
+ if HAS_WEIGHT:
442
+ tl.store(dw + i_s * D + o_d, tl.sum(b_dw, axis=0), mask=m_d)
443
+ if HAS_BIAS:
444
+ tl.store(db + i_s * D + o_d, tl.sum(b_db, axis=0), mask=m_d)
445
+
446
+
447
+ @triton.heuristics({
448
+ 'RECOMPUTE_OUTPUT': lambda args: args['y'] is not None,
449
+ })
450
+ @triton.autotune(
451
+ configs=[
452
+ triton.Config({}, num_warps=num_warps)
453
+ for num_warps in [2, 4, 8]
454
+ ],
455
+ key=['D', 'HAS_DRESIDUAL', 'STORE_DRESIDUAL', 'IS_RMS_NORM'],
456
+ **autotune_cache_kwargs,
457
+ )
458
+ @triton.jit
459
+ def layer_norm_bwd_kernel1(
460
+ x, # pointer to the input
461
+ w, # pointer to the weights
462
+ b, # pointer to the biases
463
+ y, # pointer to the output to be recomputed
464
+ dy, # pointer to the output gradient
465
+ dx, # pointer to the input gradient
466
+ dw, # pointer to the partial sum of weights gradient
467
+ db, # pointer to the partial sum of biases gradient
468
+ dres,
469
+ dres_in,
470
+ mean,
471
+ rstd,
472
+ T,
473
+ G: tl.constexpr,
474
+ D: tl.constexpr,
475
+ BS: tl.constexpr,
476
+ BD: tl.constexpr,
477
+ GS: tl.constexpr,
478
+ IS_RMS_NORM: tl.constexpr,
479
+ HAS_DRESIDUAL: tl.constexpr,
480
+ STORE_DRESIDUAL: tl.constexpr,
481
+ HAS_WEIGHT: tl.constexpr,
482
+ HAS_BIAS: tl.constexpr,
483
+ RECOMPUTE_OUTPUT: tl.constexpr,
484
+ ):
485
+ i_s = tl.program_id(0)
486
+ i_g, i_sg = i_s // GS, i_s % GS
487
+
488
+ o_d = tl.arange(0, BD)
489
+ mask = o_d < D
490
+
491
+ if HAS_WEIGHT:
492
+ b_w = tl.load(w + i_g * D + o_d, mask=mask).to(tl.float32)
493
+ b_dw = tl.zeros((BD,), dtype=tl.float32)
494
+ if RECOMPUTE_OUTPUT and HAS_BIAS:
495
+ b_b = tl.load(b + i_g * D + o_d, mask=mask, other=0.0).to(tl.float32)
496
+ if HAS_BIAS:
497
+ b_db = tl.zeros((BD,), dtype=tl.float32)
498
+
499
+ for i_t in range(i_sg * BS * G + i_g, min((i_sg * BS + BS) * G + i_g, T), G):
500
+ b_x = tl.load(x + i_t * D + o_d, mask=mask, other=0).to(tl.float32)
501
+ b_dy = tl.load(dy + i_t * D + o_d, mask=mask, other=0).to(tl.float32)
502
+
503
+ if not IS_RMS_NORM:
504
+ b_mean = tl.load(mean + i_t)
505
+ b_rstd = tl.load(rstd + i_t)
506
+ # Compute dx
507
+ b_xhat = (b_x - b_mean) * b_rstd if not IS_RMS_NORM else b_x * b_rstd
508
+ b_xhat = tl.where(mask, b_xhat, 0.0)
509
+ if RECOMPUTE_OUTPUT:
510
+ b_y = b_xhat * b_w if HAS_WEIGHT else b_xhat
511
+ if HAS_BIAS:
512
+ b_y = b_y + b_b
513
+ tl.store(y + i_t * D + o_d, b_y, mask=mask)
514
+ b_wdy = b_dy
515
+ if HAS_WEIGHT:
516
+ b_wdy = b_dy * b_w
517
+ b_dw += b_dy * b_xhat
518
+ if HAS_BIAS:
519
+ b_db += b_dy
520
+ if not IS_RMS_NORM:
521
+ b_c1 = tl.sum(b_xhat * b_wdy, axis=0) / D
522
+ b_c2 = tl.sum(b_wdy, axis=0) / D
523
+ b_dx = (b_wdy - (b_xhat * b_c1 + b_c2)) * b_rstd
524
+ else:
525
+ b_c1 = tl.sum(b_xhat * b_wdy, axis=0) / D
526
+ b_dx = (b_wdy - b_xhat * b_c1) * b_rstd
527
+ if HAS_DRESIDUAL:
528
+ b_dres = tl.load(dres + i_t * D + o_d, mask=mask, other=0).to(tl.float32)
529
+ b_dx += b_dres
530
+ # Write dx
531
+ b_dx = tl.cast(b_dx, dtype=dx.dtype.element_ty, fp_downcast_rounding='rtne')
532
+ if STORE_DRESIDUAL:
533
+ tl.store(dres_in + i_t * D + o_d, b_dx, mask=mask)
534
+ tl.store(dx + i_t * D + o_d, b_dx, mask=mask)
535
+
536
+ if HAS_WEIGHT:
537
+ tl.store(dw + i_s * D + o_d, b_dw, mask=mask)
538
+ if HAS_BIAS:
539
+ tl.store(db + i_s * D + o_d, b_db, mask=mask)
540
+
541
+
542
+ @dispatch('modules')
543
+ def layer_norm_fwd(
544
+ x: torch.Tensor,
545
+ weight: torch.Tensor,
546
+ bias: torch.Tensor,
547
+ eps: float = 1e-5,
548
+ residual: torch.Tensor = None,
549
+ out_dtype: torch.dtype = None,
550
+ residual_dtype: torch.dtype = None,
551
+ is_rms_norm: bool = False,
552
+ num_groups: int = 1,
553
+ ):
554
+ if residual is not None:
555
+ residual_dtype = residual.dtype
556
+ T, D, G = *x.shape, num_groups
557
+ if residual is not None:
558
+ assert residual.shape == (T, D)
559
+ if weight is not None:
560
+ assert weight.shape == (G * D,)
561
+ if bias is not None:
562
+ assert bias.shape == (G * D,)
563
+ # allocate output
564
+ y = torch.empty_like(x, dtype=x.dtype if out_dtype is None else out_dtype)
565
+ if residual is not None or (residual_dtype is not None and residual_dtype != x.dtype):
566
+ res_out = torch.empty(T, D, device=x.device, dtype=residual_dtype)
567
+ else:
568
+ res_out = None
569
+ mean = torch.empty((T,), dtype=torch.float, device=x.device) if not is_rms_norm else None
570
+ rstd = torch.empty((T,), dtype=torch.float, device=x.device)
571
+ # Less than 64KB per feature: enqueue fused kernel
572
+ MAX_FUSED_SIZE = 65536 // x.element_size()
573
+ BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
574
+ if D > BD:
575
+ raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
576
+ # heuristics for number of warps
577
+
578
+ if D <= 512:
579
+ NB = triton.cdiv(T, 2048)
580
+ def grid(meta): return (triton.cdiv(T, meta['BT']), )
581
+ layer_norm_fwd_kernel[grid](
582
+ x,
583
+ y,
584
+ weight,
585
+ bias,
586
+ residual,
587
+ res_out,
588
+ mean,
589
+ rstd,
590
+ eps,
591
+ T=T,
592
+ G=G,
593
+ D=D,
594
+ BD=BD,
595
+ NB=NB,
596
+ IS_RMS_NORM=is_rms_norm,
597
+ HAS_RESIDUAL=residual is not None,
598
+ STORE_RESIDUAL_OUT=res_out is not None,
599
+ HAS_WEIGHT=weight is not None,
600
+ HAS_BIAS=bias is not None,
601
+ )
602
+ else:
603
+ layer_norm_fwd_kernel1[(T,)](
604
+ x,
605
+ y,
606
+ weight,
607
+ bias,
608
+ residual,
609
+ res_out,
610
+ mean,
611
+ rstd,
612
+ eps,
613
+ G=G,
614
+ D=D,
615
+ BD=BD,
616
+ IS_RMS_NORM=is_rms_norm,
617
+ HAS_RESIDUAL=residual is not None,
618
+ STORE_RESIDUAL_OUT=res_out is not None,
619
+ HAS_WEIGHT=weight is not None,
620
+ HAS_BIAS=bias is not None,
621
+ )
622
+ # res_out is None if residual is None and residual_dtype == input_dtype
623
+ return y, mean, rstd, res_out if res_out is not None else x
624
+
625
+
626
+ @dispatch('modules')
627
+ def layer_norm_bwd(
628
+ dy: torch.Tensor,
629
+ x: torch.Tensor,
630
+ weight: torch.Tensor,
631
+ bias: torch.Tensor,
632
+ mean: torch.Tensor = None,
633
+ rstd: torch.Tensor = None,
634
+ dres: torch.Tensor = None,
635
+ has_residual: bool = False,
636
+ is_rms_norm: bool = False,
637
+ x_dtype: torch.dtype = None,
638
+ recompute_output: bool = False,
639
+ num_groups: int = 1,
640
+ ):
641
+ T, D, G = *x.shape, num_groups
642
+ assert dy.shape == (T, D)
643
+ if dres is not None:
644
+ assert dres.shape == (T, D)
645
+ if weight is not None:
646
+ assert weight.shape == (G * D,)
647
+ if bias is not None:
648
+ assert bias.shape == (G * D,)
649
+ # allocate output
650
+ dx = torch.empty_like(x) if x_dtype is None else torch.empty(T, D, dtype=x_dtype, device=x.device)
651
+ dres_in = torch.empty_like(x) if has_residual and dx.dtype != x.dtype else None
652
+ y = torch.empty(T, D, dtype=dy.dtype, device=dy.device) if recompute_output else None
653
+
654
+ # Less than 64KB per feature: enqueue fused kernel
655
+ MAX_FUSED_SIZE = 65536 // x.element_size()
656
+ BD = min(MAX_FUSED_SIZE, triton.next_power_of_2(D))
657
+ if D > BD:
658
+ raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
659
+ # each program handles one group only.
660
+ # cap per-group program count to T // G so no program is completely idle.
661
+ # without this, high-SM GPUs (e.g. B200, 160 SMs) with small T would
662
+ # launch idle programs whose make_block_ptr offsets exceed the tensor shape.
663
+ NS = min(triton.cdiv(get_multiprocessor_count(x.device.index), G), T // G) * G
664
+ BS = triton.cdiv(T, NS)
665
+ GS = NS // G
666
+
667
+ dw = torch.empty((NS, D), dtype=torch.float, device=weight.device) if weight is not None else None
668
+ db = torch.empty((NS, D), dtype=torch.float, device=bias.device) if bias is not None else None
669
+ grid = (NS,)
670
+
671
+ if D <= 512:
672
+ NB = triton.cdiv(T, 2048)
673
+ layer_norm_bwd_kernel[grid](
674
+ x,
675
+ weight,
676
+ bias,
677
+ y,
678
+ dy,
679
+ dx,
680
+ dw,
681
+ db,
682
+ dres,
683
+ dres_in,
684
+ mean,
685
+ rstd,
686
+ T=T,
687
+ G=G,
688
+ D=D,
689
+ BS=BS,
690
+ BD=BD,
691
+ NB=NB,
692
+ GS=GS,
693
+ IS_RMS_NORM=is_rms_norm,
694
+ HAS_DRESIDUAL=dres is not None,
695
+ STORE_DRESIDUAL=dres_in is not None,
696
+ HAS_WEIGHT=weight is not None,
697
+ HAS_BIAS=bias is not None,
698
+ )
699
+ else:
700
+ layer_norm_bwd_kernel1[grid](
701
+ x,
702
+ weight,
703
+ bias,
704
+ y,
705
+ dy,
706
+ dx,
707
+ dw,
708
+ db,
709
+ dres,
710
+ dres_in,
711
+ mean,
712
+ rstd,
713
+ T=T,
714
+ G=G,
715
+ D=D,
716
+ BS=BS,
717
+ BD=BD,
718
+ GS=GS,
719
+ IS_RMS_NORM=is_rms_norm,
720
+ HAS_DRESIDUAL=dres is not None,
721
+ STORE_DRESIDUAL=dres_in is not None,
722
+ HAS_WEIGHT=weight is not None,
723
+ HAS_BIAS=bias is not None,
724
+ )
725
+ dw = dw.view(G, -1, D).sum(1).to(weight).view_as(weight) if weight is not None else None
726
+ db = db.view(G, -1, D).sum(1).to(bias).view_as(bias) if bias is not None else None
727
+ # Don't need to compute dres_in separately in this case
728
+ if has_residual and dx.dtype == x.dtype:
729
+ dres_in = dx
730
+ return (dx, dw, db, dres_in) if not recompute_output else (dx, dw, db, dres_in, y)
731
+
732
+
733
+ class LayerNormFunction(torch.autograd.Function):
734
+
735
+ @staticmethod
736
+ @input_guard
737
+ def forward(
738
+ ctx,
739
+ x,
740
+ weight,
741
+ bias,
742
+ residual: torch.Tensor = None,
743
+ eps: float = 1e-5,
744
+ prenorm: bool = False,
745
+ residual_in_fp32: bool = False,
746
+ is_rms_norm: bool = False,
747
+ num_groups: int = 1,
748
+ ):
749
+ x_shape_og = x.shape
750
+
751
+ if x.shape[-1] % num_groups != 0:
752
+ raise ValueError('num_channels must be divisible by num_groups')
753
+ # reshape input data into 2D tensor
754
+ x = x.reshape(-1, (x.shape[-1] // num_groups))
755
+ if residual is not None:
756
+ assert residual.shape == x_shape_og
757
+ residual = residual.reshape_as(x)
758
+ residual_dtype = (
759
+ residual.dtype
760
+ if residual is not None
761
+ else (torch.float32 if residual_in_fp32 else None)
762
+ )
763
+ y, mean, rstd, res_out = layer_norm_fwd(
764
+ x,
765
+ weight,
766
+ bias,
767
+ eps,
768
+ residual,
769
+ residual_dtype=residual_dtype,
770
+ is_rms_norm=is_rms_norm,
771
+ num_groups=num_groups,
772
+ )
773
+ ctx.save_for_backward(res_out, weight, bias, mean, rstd)
774
+ ctx.x_shape_og = x_shape_og
775
+ ctx.eps = eps
776
+ ctx.is_rms_norm = is_rms_norm
777
+ ctx.num_groups = num_groups
778
+ ctx.has_residual = residual is not None
779
+ ctx.prenorm = prenorm
780
+ ctx.x_dtype = x.dtype
781
+ y = y.reshape(x_shape_og)
782
+ return y if not prenorm else (y, res_out.reshape(x_shape_og))
783
+
784
+ @staticmethod
785
+ @input_guard
786
+ def backward(ctx, dy, *args):
787
+ x, weight, bias, mean, rstd = ctx.saved_tensors
788
+ dy = dy.reshape(-1, (dy.shape[-1] // ctx.num_groups))
789
+ assert dy.shape == x.shape
790
+ if ctx.prenorm:
791
+ dresidual = args[0]
792
+ dresidual = dresidual.reshape(-1, x.shape[-1])
793
+ assert dresidual.shape == x.shape
794
+ else:
795
+ dresidual = None
796
+ dx, dw, db, dresidual_in = layer_norm_bwd(
797
+ dy,
798
+ x,
799
+ weight,
800
+ bias,
801
+ mean,
802
+ rstd,
803
+ dresidual,
804
+ ctx.has_residual,
805
+ ctx.is_rms_norm,
806
+ x_dtype=ctx.x_dtype,
807
+ num_groups=ctx.num_groups,
808
+ )
809
+ return (
810
+ dx.reshape(ctx.x_shape_og),
811
+ dw,
812
+ db,
813
+ dresidual_in.reshape(ctx.x_shape_og) if ctx.has_residual else None,
814
+ None,
815
+ None,
816
+ None,
817
+ None,
818
+ None,
819
+ )
820
+
821
+
822
+ def layer_norm(
823
+ x: torch.Tensor,
824
+ weight: torch.Tensor,
825
+ bias: torch.Tensor,
826
+ residual: torch.Tensor = None,
827
+ eps: float = 1e-5,
828
+ prenorm: bool = False,
829
+ residual_in_fp32: bool = False,
830
+ is_rms_norm: bool = False,
831
+ ):
832
+ return LayerNormFunction.apply(
833
+ x,
834
+ weight,
835
+ bias,
836
+ residual,
837
+ eps,
838
+ prenorm,
839
+ residual_in_fp32,
840
+ is_rms_norm,
841
+ )
842
+
843
+
844
+ def group_norm(
845
+ x: torch.Tensor,
846
+ weight: torch.Tensor,
847
+ bias: torch.Tensor,
848
+ residual: torch.Tensor = None,
849
+ eps: float = 1e-5,
850
+ prenorm: bool = False,
851
+ residual_in_fp32: bool = False,
852
+ is_rms_norm: bool = False,
853
+ num_groups: int = 1,
854
+ ):
855
+ return LayerNormFunction.apply(
856
+ x,
857
+ weight,
858
+ bias,
859
+ residual,
860
+ eps,
861
+ prenorm,
862
+ residual_in_fp32,
863
+ is_rms_norm,
864
+ num_groups,
865
+ )
866
+
867
+
868
+ def rms_norm(
869
+ x: torch.Tensor,
870
+ weight: torch.Tensor,
871
+ bias: torch.Tensor,
872
+ residual: torch.Tensor = None,
873
+ eps: float = 1e-5,
874
+ prenorm: bool = False,
875
+ residual_in_fp32: bool = False,
876
+ ):
877
+ return LayerNormFunction.apply(
878
+ x,
879
+ weight,
880
+ bias,
881
+ residual,
882
+ eps,
883
+ prenorm,
884
+ residual_in_fp32,
885
+ True,
886
+ )
887
+
888
+
889
+ def layer_norm_linear(
890
+ x: torch.Tensor,
891
+ norm_weight: torch.Tensor,
892
+ norm_bias: torch.Tensor,
893
+ linear_weight: torch.Tensor,
894
+ linear_bias: torch.Tensor,
895
+ residual: torch.Tensor = None,
896
+ eps: float = 1e-5,
897
+ prenorm: bool = False,
898
+ residual_in_fp32: bool = False,
899
+ is_rms_norm: bool = False,
900
+ num_groups: int = 1,
901
+ ):
902
+ return LayerNormLinearFunction.apply(
903
+ x,
904
+ norm_weight,
905
+ norm_bias,
906
+ linear_weight,
907
+ linear_bias,
908
+ residual,
909
+ eps,
910
+ prenorm,
911
+ residual_in_fp32,
912
+ is_rms_norm,
913
+ num_groups,
914
+ )
915
+
916
+
917
+ def rms_norm_linear(
918
+ x: torch.Tensor,
919
+ norm_weight: torch.Tensor,
920
+ norm_bias: torch.Tensor,
921
+ linear_weight: torch.Tensor,
922
+ linear_bias: torch.Tensor,
923
+ residual: torch.Tensor = None,
924
+ eps: float = 1e-5,
925
+ prenorm: bool = False,
926
+ residual_in_fp32: bool = False,
927
+ ):
928
+ return layer_norm_linear(
929
+ x=x,
930
+ norm_weight=norm_weight,
931
+ norm_bias=norm_bias,
932
+ linear_weight=linear_weight,
933
+ linear_bias=linear_bias,
934
+ residual=residual,
935
+ eps=eps,
936
+ prenorm=prenorm,
937
+ residual_in_fp32=residual_in_fp32,
938
+ is_rms_norm=True,
939
+ )
940
+
941
+
942
+ def group_norm_linear(
943
+ x: torch.Tensor,
944
+ norm_weight: torch.Tensor,
945
+ norm_bias: torch.Tensor,
946
+ linear_weight: torch.Tensor,
947
+ linear_bias: torch.Tensor,
948
+ residual: torch.Tensor = None,
949
+ eps: float = 1e-5,
950
+ prenorm: bool = False,
951
+ residual_in_fp32: bool = False,
952
+ is_rms_norm: bool = False,
953
+ num_groups: int = 1,
954
+ ):
955
+ return layer_norm_linear(
956
+ x=x,
957
+ norm_weight=norm_weight,
958
+ norm_bias=norm_bias,
959
+ linear_weight=linear_weight,
960
+ linear_bias=linear_bias,
961
+ residual=residual,
962
+ eps=eps,
963
+ prenorm=prenorm,
964
+ residual_in_fp32=residual_in_fp32,
965
+ is_rms_norm=is_rms_norm,
966
+ num_groups=num_groups,
967
+ )
968
+
969
+
970
+ class LayerNorm(nn.Module):
971
+
972
+ def __init__(
973
+ self,
974
+ hidden_size: int,
975
+ elementwise_affine: bool = True,
976
+ bias: bool = False,
977
+ eps: float = 1e-5,
978
+ device: torch.device | None = None,
979
+ dtype: torch.dtype | None = None,
980
+ ) -> LayerNorm:
981
+ factory_kwargs = {"device": device, "dtype": dtype}
982
+ super().__init__()
983
+
984
+ self.hidden_size = hidden_size
985
+ self.elementwise_affine = elementwise_affine
986
+ self.eps = eps
987
+
988
+ self.register_parameter("weight", None)
989
+ self.register_parameter("bias", None)
990
+ if elementwise_affine:
991
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
992
+ if bias:
993
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
994
+
995
+ self.reset_parameters()
996
+
997
+ def reset_parameters(self):
998
+ if self.elementwise_affine:
999
+ nn.init.ones_(self.weight)
1000
+ if self.bias is not None:
1001
+ nn.init.zeros_(self.bias)
1002
+
1003
+ def __repr__(self) -> str:
1004
+ s = f"{self.__class__.__name__}({self.hidden_size}"
1005
+ if not self.elementwise_affine:
1006
+ s += f", elementwise_affine={self.elementwise_affine}"
1007
+ s += f", eps={self.eps}"
1008
+ s += ")"
1009
+ return s
1010
+
1011
+ def forward(self, x, residual=None, prenorm=False, residual_in_fp32=False):
1012
+ return layer_norm(
1013
+ x,
1014
+ self.weight,
1015
+ self.bias,
1016
+ residual=residual,
1017
+ eps=self.eps,
1018
+ prenorm=prenorm,
1019
+ residual_in_fp32=residual_in_fp32,
1020
+ )
1021
+
1022
+
1023
+ class GroupNorm(nn.Module):
1024
+
1025
+ def __init__(
1026
+ self,
1027
+ num_groups: int,
1028
+ hidden_size: int,
1029
+ elementwise_affine: bool = True,
1030
+ bias: bool = False,
1031
+ eps: float = 1e-5,
1032
+ is_rms_norm: bool = False,
1033
+ device: torch.device | None = None,
1034
+ dtype: torch.dtype | None = None,
1035
+ ) -> GroupNorm:
1036
+ factory_kwargs = {"device": device, "dtype": dtype}
1037
+ super().__init__()
1038
+
1039
+ if hidden_size % num_groups != 0:
1040
+ raise ValueError('num_channels must be divisible by num_groups')
1041
+
1042
+ self.num_groups = num_groups
1043
+ self.hidden_size = hidden_size
1044
+ self.elementwise_affine = elementwise_affine
1045
+ self.eps = eps
1046
+ self.is_rms_norm = is_rms_norm
1047
+
1048
+ self.register_parameter("weight", None)
1049
+ self.register_parameter("bias", None)
1050
+ if elementwise_affine:
1051
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1052
+ if bias:
1053
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1054
+
1055
+ self.reset_parameters()
1056
+
1057
+ def reset_parameters(self):
1058
+ if self.elementwise_affine:
1059
+ nn.init.ones_(self.weight)
1060
+ if self.bias is not None:
1061
+ nn.init.zeros_(self.bias)
1062
+
1063
+ def __repr__(self) -> str:
1064
+ s = f"{self.__class__.__name__}({self.num_groups}, {self.hidden_size}"
1065
+ if not self.elementwise_affine:
1066
+ s += f", elementwise_affine={self.elementwise_affine}"
1067
+ if self.is_rms_norm:
1068
+ s += f", is_rms_norm={self.is_rms_norm}"
1069
+ s += f", eps={self.eps}"
1070
+ s += ")"
1071
+ return s
1072
+
1073
+ def forward(self, x, residual=None, prenorm=False, residual_in_fp32=False):
1074
+ return group_norm(
1075
+ x,
1076
+ self.weight,
1077
+ self.bias,
1078
+ residual=residual,
1079
+ eps=self.eps,
1080
+ prenorm=prenorm,
1081
+ residual_in_fp32=residual_in_fp32,
1082
+ is_rms_norm=self.is_rms_norm,
1083
+ num_groups=self.num_groups,
1084
+ )
1085
+
1086
+
1087
+ class RMSNorm(nn.Module):
1088
+
1089
+ def __init__(
1090
+ self,
1091
+ hidden_size: int,
1092
+ elementwise_affine: bool = True,
1093
+ bias: bool = False,
1094
+ eps: float = 1e-5,
1095
+ device: torch.device | None = None,
1096
+ dtype: torch.dtype | None = None,
1097
+ ) -> RMSNorm:
1098
+ factory_kwargs = {"device": device, "dtype": dtype}
1099
+ super().__init__()
1100
+
1101
+ self.hidden_size = hidden_size
1102
+ self.elementwise_affine = elementwise_affine
1103
+ self.eps = eps
1104
+
1105
+ self.register_parameter("weight", None)
1106
+ self.register_parameter("bias", None)
1107
+ if elementwise_affine:
1108
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1109
+ if bias:
1110
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1111
+
1112
+ self.reset_parameters()
1113
+
1114
+ def reset_parameters(self):
1115
+ if self.elementwise_affine:
1116
+ nn.init.ones_(self.weight)
1117
+ if self.bias is not None:
1118
+ nn.init.zeros_(self.bias)
1119
+
1120
+ def __repr__(self) -> str:
1121
+ s = f"{self.__class__.__name__}({self.hidden_size}"
1122
+ if not self.elementwise_affine:
1123
+ s += f", elementwise_affine={self.elementwise_affine}"
1124
+ s += f", eps={self.eps}"
1125
+ s += ")"
1126
+ return s
1127
+
1128
+ def forward(self, x, residual=None, prenorm=False, residual_in_fp32=False):
1129
+ return rms_norm(
1130
+ x,
1131
+ self.weight,
1132
+ self.bias,
1133
+ residual=residual,
1134
+ eps=self.eps,
1135
+ prenorm=prenorm,
1136
+ residual_in_fp32=residual_in_fp32,
1137
+ )
1138
+
1139
+
1140
+ class LayerNormLinearFunction(torch.autograd.Function):
1141
+
1142
+ @staticmethod
1143
+ @input_guard
1144
+ def forward(
1145
+ ctx,
1146
+ x,
1147
+ norm_weight,
1148
+ norm_bias,
1149
+ linear_weight,
1150
+ linear_bias,
1151
+ residual=None,
1152
+ eps=1e-5,
1153
+ prenorm=False,
1154
+ residual_in_fp32=False,
1155
+ is_rms_norm=False,
1156
+ num_groups=1,
1157
+ ):
1158
+ x_shape_og = x.shape
1159
+
1160
+ if x.shape[-1] % num_groups != 0:
1161
+ raise ValueError('num_channels must be divisible by num_groups')
1162
+ # reshape input data into 2D tensor
1163
+ x = x.reshape(-1, (x.shape[-1] // num_groups))
1164
+ if residual is not None:
1165
+ assert residual.shape == x_shape_og
1166
+ residual = residual.reshape_as(x)
1167
+ residual_dtype = (
1168
+ residual.dtype
1169
+ if residual is not None
1170
+ else (torch.float32 if residual_in_fp32 else None)
1171
+ )
1172
+ y, mean, rstd, res_out = layer_norm_fwd(
1173
+ x,
1174
+ norm_weight,
1175
+ norm_bias,
1176
+ eps,
1177
+ residual,
1178
+ out_dtype=None if not torch.is_autocast_enabled() else torch.get_autocast_gpu_dtype(),
1179
+ residual_dtype=residual_dtype,
1180
+ is_rms_norm=is_rms_norm,
1181
+ num_groups=num_groups,
1182
+ )
1183
+ y = y.reshape(x_shape_og)
1184
+ dtype = torch.get_autocast_gpu_dtype() if torch.is_autocast_enabled() else y.dtype
1185
+ linear_weight = linear_weight.to(dtype)
1186
+ linear_bias = linear_bias.to(dtype) if linear_bias is not None else None
1187
+ out = F.linear(y.to(linear_weight.dtype), linear_weight, linear_bias)
1188
+ # We don't store y, will be recomputed in the backward pass to save memory
1189
+ ctx.save_for_backward(res_out, norm_weight, norm_bias, linear_weight, mean, rstd)
1190
+ ctx.x_shape_og = x_shape_og
1191
+ ctx.eps = eps
1192
+ ctx.is_rms_norm = is_rms_norm
1193
+ ctx.num_groups = num_groups
1194
+ ctx.has_residual = residual is not None
1195
+ ctx.prenorm = prenorm
1196
+ ctx.x_dtype = x.dtype
1197
+ ctx.linear_bias_is_none = linear_bias is None
1198
+ return out if not prenorm else (out, res_out.reshape(x_shape_og))
1199
+
1200
+ @staticmethod
1201
+ @input_guard
1202
+ def backward(ctx, dout, *args):
1203
+ x, norm_weight, norm_bias, linear_weight, mean, rstd = ctx.saved_tensors
1204
+ dout = dout.reshape(-1, dout.shape[-1])
1205
+ dy = F.linear(dout, linear_weight.t())
1206
+ dy = dy.reshape(-1, (dy.shape[-1] // ctx.num_groups))
1207
+ dlinear_bias = None if ctx.linear_bias_is_none else dout.sum(0)
1208
+ assert dy.shape == x.shape
1209
+ if ctx.prenorm:
1210
+ dresidual = args[0]
1211
+ dresidual = dresidual.reshape(-1, x.shape[-1])
1212
+ assert dresidual.shape == x.shape
1213
+ else:
1214
+ dresidual = None
1215
+ dx, dnorm_weight, dnorm_bias, dresidual_in, y = layer_norm_bwd(
1216
+ dy,
1217
+ x,
1218
+ norm_weight,
1219
+ norm_bias,
1220
+ mean,
1221
+ rstd,
1222
+ dresidual,
1223
+ ctx.has_residual,
1224
+ ctx.is_rms_norm,
1225
+ x_dtype=ctx.x_dtype,
1226
+ recompute_output=True,
1227
+ num_groups=ctx.num_groups,
1228
+ )
1229
+ dlinear_weight = torch.einsum("bo,bi->oi", dout, y.view(-1, linear_weight.shape[-1]))
1230
+ return (
1231
+ dx.reshape(ctx.x_shape_og),
1232
+ dnorm_weight,
1233
+ dnorm_bias,
1234
+ dlinear_weight,
1235
+ dlinear_bias,
1236
+ dresidual_in.reshape(ctx.x_shape_og) if ctx.has_residual else None,
1237
+ None,
1238
+ None,
1239
+ None,
1240
+ None,
1241
+ None,
1242
+ )
1243
+
1244
+
1245
+ class LayerNormLinear(nn.Module):
1246
+
1247
+ def __init__(
1248
+ self,
1249
+ hidden_size,
1250
+ elementwise_affine: bool = True,
1251
+ bias: bool = False,
1252
+ eps: float = 1e-5,
1253
+ device: torch.device | None = None,
1254
+ dtype: torch.dtype | None = None,
1255
+ ) -> LayerNormLinear:
1256
+ factory_kwargs = {"device": device, "dtype": dtype}
1257
+ super().__init__()
1258
+
1259
+ self.hidden_size = hidden_size
1260
+ self.elementwise_affine = elementwise_affine
1261
+ self.eps = eps
1262
+
1263
+ self.register_parameter("weight", None)
1264
+ self.register_parameter("bias", None)
1265
+ if elementwise_affine:
1266
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1267
+ if bias:
1268
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1269
+
1270
+ self.reset_parameters()
1271
+
1272
+ def reset_parameters(self):
1273
+ if self.elementwise_affine:
1274
+ nn.init.ones_(self.weight)
1275
+ if self.bias is not None:
1276
+ nn.init.zeros_(self.bias)
1277
+
1278
+ def __repr__(self) -> str:
1279
+ s = f"{self.__class__.__name__}({self.hidden_size}"
1280
+ if not self.elementwise_affine:
1281
+ s += f", elementwise_affine={self.elementwise_affine}"
1282
+ s += f", eps={self.eps}"
1283
+ s += ")"
1284
+ return s
1285
+
1286
+ def forward(self, x, weight, bias, residual=None, prenorm=False, residual_in_fp32=False):
1287
+ return layer_norm_linear(
1288
+ x=x,
1289
+ norm_weight=self.weight,
1290
+ norm_bias=self.bias,
1291
+ linear_weight=weight,
1292
+ linear_bias=bias,
1293
+ residual=residual,
1294
+ eps=self.eps,
1295
+ prenorm=prenorm,
1296
+ residual_in_fp32=residual_in_fp32,
1297
+ is_rms_norm=False,
1298
+ )
1299
+
1300
+
1301
+ class GroupNormLinear(nn.Module):
1302
+
1303
+ def __init__(
1304
+ self,
1305
+ num_groups: int,
1306
+ hidden_size: int,
1307
+ elementwise_affine: bool = True,
1308
+ bias: bool = False,
1309
+ eps: float = 1e-5,
1310
+ is_rms_norm: bool = False,
1311
+ device: torch.device | None = None,
1312
+ dtype: torch.dtype | None = None,
1313
+ ) -> GroupNormLinear:
1314
+ factory_kwargs = {"device": device, "dtype": dtype}
1315
+ super().__init__()
1316
+
1317
+ if hidden_size % num_groups != 0:
1318
+ raise ValueError('num_channels must be divisible by num_groups')
1319
+
1320
+ self.num_groups = num_groups
1321
+ self.hidden_size = hidden_size
1322
+ self.elementwise_affine = elementwise_affine
1323
+ self.eps = eps
1324
+ self.is_rms_norm = is_rms_norm
1325
+
1326
+ self.register_parameter("weight", None)
1327
+ self.register_parameter("bias", None)
1328
+ if elementwise_affine:
1329
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1330
+ if bias:
1331
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1332
+
1333
+ self.reset_parameters()
1334
+
1335
+ def reset_parameters(self):
1336
+ if self.elementwise_affine:
1337
+ nn.init.ones_(self.weight)
1338
+ if self.bias is not None:
1339
+ nn.init.zeros_(self.bias)
1340
+
1341
+ def __repr__(self) -> str:
1342
+ s = f"{self.__class__.__name__}({self.num_groups}, {self.hidden_size}"
1343
+ if not self.elementwise_affine:
1344
+ s += f", elementwise_affine={self.elementwise_affine}"
1345
+ if self.is_rms_norm:
1346
+ s += f", is_rms_norm={self.is_rms_norm}"
1347
+ s += f", eps={self.eps}"
1348
+ s += ")"
1349
+ return s
1350
+
1351
+ def forward(self, x, weight, bias, residual=None, prenorm=False, residual_in_fp32=False):
1352
+ return layer_norm_linear(
1353
+ x=x,
1354
+ norm_weight=self.weight,
1355
+ norm_bias=self.bias,
1356
+ linear_weight=weight,
1357
+ linear_bias=bias,
1358
+ residual=residual,
1359
+ eps=self.eps,
1360
+ prenorm=prenorm,
1361
+ residual_in_fp32=residual_in_fp32,
1362
+ is_rms_norm=self.is_rms_norm,
1363
+ num_groups=self.num_groups,
1364
+ )
1365
+
1366
+
1367
+ class RMSNormLinear(nn.Module):
1368
+
1369
+ def __init__(
1370
+ self,
1371
+ hidden_size,
1372
+ elementwise_affine: bool = True,
1373
+ bias: bool = False,
1374
+ eps: float = 1e-5,
1375
+ device: torch.device | None = None,
1376
+ dtype: torch.dtype | None = None,
1377
+ ) -> RMSNormLinear:
1378
+ factory_kwargs = {"device": device, "dtype": dtype}
1379
+ super().__init__()
1380
+
1381
+ self.hidden_size = hidden_size
1382
+ self.elementwise_affine = elementwise_affine
1383
+ self.eps = eps
1384
+
1385
+ self.register_parameter("weight", None)
1386
+ self.register_parameter("bias", None)
1387
+ if elementwise_affine:
1388
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1389
+ if bias:
1390
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
1391
+
1392
+ self.reset_parameters()
1393
+
1394
+ def reset_parameters(self):
1395
+ if self.elementwise_affine:
1396
+ nn.init.ones_(self.weight)
1397
+ if self.bias is not None:
1398
+ nn.init.zeros_(self.bias)
1399
+
1400
+ def __repr__(self) -> str:
1401
+ s = f"{self.__class__.__name__}({self.hidden_size}"
1402
+ if not self.elementwise_affine:
1403
+ s += f", elementwise_affine={self.elementwise_affine}"
1404
+ s += f", eps={self.eps}"
1405
+ s += ")"
1406
+ return s
1407
+
1408
+ def forward(self, x, weight, bias, residual=None, prenorm=False, residual_in_fp32=False):
1409
+ return layer_norm_linear(
1410
+ x=x,
1411
+ norm_weight=self.weight,
1412
+ norm_bias=self.bias,
1413
+ linear_weight=weight,
1414
+ linear_bias=bias,
1415
+ residual=residual,
1416
+ eps=self.eps,
1417
+ prenorm=prenorm,
1418
+ residual_in_fp32=residual_in_fp32,
1419
+ is_rms_norm=True,
1420
+ )
1421
+
1422
+
1423
+ class NormParallel(ParallelStyle):
1424
+
1425
+ def __init__(self, *, sequence_dim: int = 1, use_local_output: bool = False):
1426
+ super().__init__()
1427
+ self.sequence_sharding = (Shard(sequence_dim),)
1428
+ self.use_local_output = use_local_output
1429
+
1430
+ def _replicate_module_fn(
1431
+ self, name: str, module: nn.Module, device_mesh: DeviceMesh,
1432
+ ):
1433
+ for p_name, param in module.named_parameters():
1434
+ # simple replication with fixed ones_ init from LayerNorm/RMSNorm, which allow
1435
+ # us to simply just use from_local
1436
+ replicated_param = torch.nn.Parameter(
1437
+ DTensor.from_local(param, device_mesh, [Replicate()], run_check=False),
1438
+ )
1439
+ module.register_parameter(p_name, replicated_param)
1440
+
1441
+ @staticmethod
1442
+ def _prepare_input_fn(sequence_sharding, mod, inputs, device_mesh):
1443
+ input_tensor = inputs[0]
1444
+ if isinstance(input_tensor, DTensor):
1445
+ # if the passed in input DTensor is not sharded on the sequence dim, we need to redistribute it
1446
+ if input_tensor.placements != sequence_sharding:
1447
+ input_tensor = input_tensor.redistribute(
1448
+ placements=sequence_sharding, async_op=True,
1449
+ )
1450
+ return input_tensor
1451
+ elif isinstance(input_tensor, torch.Tensor):
1452
+ # assume the input passed in already sharded on the sequence dim and create the DTensor
1453
+ return DTensor.from_local(
1454
+ input_tensor, device_mesh, sequence_sharding, run_check=False,
1455
+ )
1456
+ else:
1457
+ raise ValueError(
1458
+ f"expecting input of {mod} to be a torch.Tensor or DTensor, but got {input_tensor}",
1459
+ )
1460
+
1461
+ @staticmethod
1462
+ def _prepare_output_fn(use_local_output, mod, outputs, device_mesh):
1463
+ return outputs.to_local() if use_local_output else outputs
1464
+
1465
+ def _apply(self, module: nn.Module, device_mesh: DeviceMesh) -> nn.Module:
1466
+ return distribute_module(
1467
+ module,
1468
+ device_mesh,
1469
+ self._replicate_module_fn,
1470
+ partial(self._prepare_input_fn, self.sequence_sharding),
1471
+ partial(self._prepare_output_fn, self.use_local_output),
1472
+ )
build/torch-cuda/modules/layernorm_gated.py ADDED
@@ -0,0 +1,535 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # Copyright (c) 2024, Tri Dao.
9
+ #
10
+ # Based on the Triton LayerNorm tutorial: https://triton-lang.org/main/getting-started/tutorials/05-layer-norm.html
11
+ # For the backward pass, we keep weight_grad and bias_grad in registers and accumulate.
12
+ # This backward pass is faster for dimensions up to 8k, but after that it's much slower due to register spilling.
13
+ # The models we train have hidden dim up to 8k anyway (e.g. Llama 70B), so this is fine.
14
+
15
+ import math
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+ import torch.nn.functional as F
20
+ import triton
21
+ import triton.language as tl
22
+ from einops import rearrange
23
+
24
+ from ..utils import get_multiprocessor_count, input_guard
25
+
26
+
27
+ def rms_norm_ref(x, weight, bias, z=None, eps=1e-6, group_size=None, norm_before_gate=True, upcast=True):
28
+ dtype = x.dtype
29
+ weight = weight.float()
30
+ bias = bias.float() if bias is not None else None
31
+ if upcast:
32
+ x = x.float()
33
+ z = z.float() if z is not None else z
34
+ if z is not None and not norm_before_gate:
35
+ x = x * F.silu(z)
36
+ if group_size is None:
37
+ rstd = 1 / torch.sqrt((x.square()).mean(dim=-1, keepdim=True) + eps)
38
+ out = (x * rstd * weight) + bias if bias is not None else (x * rstd * weight)
39
+ else:
40
+ x_group = rearrange(x, "... (g d) -> ... g d", d=group_size)
41
+ rstd = 1 / torch.sqrt((x_group.square()).mean(dim=-1, keepdim=True) + eps)
42
+ out = rearrange(x_group * rstd, "... g d -> ... (g d)") * weight
43
+ if bias is not None:
44
+ out = out + bias
45
+ if z is not None and norm_before_gate:
46
+ out *= F.silu(z)
47
+ return out.to(dtype)
48
+
49
+
50
+ @triton.heuristics({
51
+ "HAS_BIAS": lambda args: args["B"] is not None,
52
+ "HAS_Z": lambda args: args["Z"] is not None,
53
+ })
54
+ @triton.jit
55
+ def layer_norm_fwd_kernel(
56
+ X, # pointer to the input
57
+ Y, # pointer to the output
58
+ W, # pointer to the weights
59
+ B, # pointer to the biases
60
+ Z, # pointer to the other branch
61
+ Mean, # pointer to the mean
62
+ Rstd, # pointer to the 1/std
63
+ stride_x_row, # how much to increase the pointer when moving by 1 row
64
+ stride_y_row,
65
+ stride_z_row,
66
+ M, # number of rows in X
67
+ N, # number of columns in X
68
+ eps, # epsilon to avoid division by zero
69
+ BLOCK_N: tl.constexpr,
70
+ HAS_BIAS: tl.constexpr,
71
+ HAS_Z: tl.constexpr,
72
+ NORM_BEFORE_GATE: tl.constexpr,
73
+ IS_RMS_NORM: tl.constexpr,
74
+ ):
75
+ # Map the program id to the row of X and Y it should compute.
76
+ row = tl.program_id(0)
77
+ group = tl.program_id(1)
78
+ X += row * stride_x_row + group * N
79
+ Y += row * stride_y_row + group * N
80
+ if HAS_Z:
81
+ Z += row * stride_z_row + group * N
82
+ if not IS_RMS_NORM:
83
+ Mean += group * M
84
+ Rstd += group * M
85
+ W += group * N
86
+ if HAS_BIAS:
87
+ B += group * N
88
+ # Compute mean and variance
89
+ cols = tl.arange(0, BLOCK_N)
90
+ x = tl.load(X + cols, mask=cols < N, other=0.).to(tl.float32)
91
+ if HAS_Z and not NORM_BEFORE_GATE:
92
+ z = tl.load(Z + cols, mask=cols < N).to(tl.float32)
93
+ x *= z * tl.sigmoid(z)
94
+ if not IS_RMS_NORM:
95
+ mean = tl.sum(x, axis=0) / N
96
+ tl.store(Mean + row, mean)
97
+ xbar = tl.where(cols < N, x - mean, 0.)
98
+ var = tl.sum(xbar * xbar, axis=0) / N
99
+ else:
100
+ xbar = tl.where(cols < N, x, 0.)
101
+ var = tl.sum(xbar * xbar, axis=0) / N
102
+ rstd = 1 / tl.sqrt(var + eps)
103
+ tl.store(Rstd + row, rstd)
104
+ # Normalize and apply linear transformation
105
+ mask = cols < N
106
+ w = tl.load(W + cols, mask=mask).to(tl.float32)
107
+ if HAS_BIAS:
108
+ b = tl.load(B + cols, mask=mask).to(tl.float32)
109
+ x_hat = (x - mean) * rstd if not IS_RMS_NORM else x * rstd
110
+ y = x_hat * w + b if HAS_BIAS else x_hat * w
111
+ if HAS_Z and NORM_BEFORE_GATE:
112
+ z = tl.load(Z + cols, mask=mask).to(tl.float32)
113
+ y *= z * tl.sigmoid(z)
114
+ # Write output
115
+ tl.store(Y + cols, y, mask=mask)
116
+
117
+
118
+ def layer_norm_fwd(
119
+ x: torch.Tensor,
120
+ weight: torch.Tensor,
121
+ bias: torch.Tensor,
122
+ eps: float,
123
+ z: torch.Tensor = None,
124
+ out: torch.Tensor = None,
125
+ group_size: int = None,
126
+ norm_before_gate: bool = True,
127
+ is_rms_norm: bool = False,
128
+ ):
129
+ M, N = x.shape
130
+ if group_size is None:
131
+ group_size = N
132
+ assert N % group_size == 0
133
+ ngroups = N // group_size
134
+ assert x.stride(-1) == 1
135
+ if z is not None:
136
+ assert z.stride(-1) == 1
137
+ assert z.shape == (M, N)
138
+ assert weight.shape == (N,)
139
+ assert weight.stride(-1) == 1
140
+ if bias is not None:
141
+ assert bias.stride(-1) == 1
142
+ assert bias.shape == (N,)
143
+ # allocate output
144
+ if out is not None:
145
+ assert out.shape == x.shape
146
+ else:
147
+ out = torch.empty_like(x)
148
+ assert out.stride(-1) == 1
149
+ mean = torch.empty((ngroups * M, ), dtype=torch.float32, device=x.device) if not is_rms_norm else None
150
+ rstd = torch.empty((ngroups * M, ), dtype=torch.float32, device=x.device)
151
+ # Less than 64KB per feature: enqueue fused kernel
152
+ MAX_FUSED_SIZE = 65536 // x.element_size()
153
+ BLOCK_N = min(MAX_FUSED_SIZE, triton.next_power_of_2(group_size))
154
+ if group_size > BLOCK_N:
155
+ raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
156
+ # heuristics for number of warps
157
+ num_warps = min(max(BLOCK_N // 256, 1), 8)
158
+ grid = (M, ngroups)
159
+ layer_norm_fwd_kernel[grid](
160
+ x,
161
+ out,
162
+ weight,
163
+ bias,
164
+ z,
165
+ mean,
166
+ rstd,
167
+ x.stride(0),
168
+ out.stride(0),
169
+ z.stride(0) if z is not None else 0,
170
+ M,
171
+ group_size,
172
+ eps,
173
+ BLOCK_N=BLOCK_N,
174
+ NORM_BEFORE_GATE=norm_before_gate,
175
+ IS_RMS_NORM=is_rms_norm,
176
+ num_warps=num_warps,
177
+ )
178
+ return out, mean, rstd
179
+
180
+
181
+ @triton.heuristics({
182
+ "HAS_BIAS": lambda args: args["B"] is not None,
183
+ "HAS_Z": lambda args: args["Z"] is not None,
184
+ "RECOMPUTE_OUTPUT": lambda args: args["Y"] is not None,
185
+ })
186
+ @triton.jit
187
+ def layer_norm_bwd_kernel(
188
+ X, # pointer to the input
189
+ W, # pointer to the weights
190
+ B, # pointer to the biases
191
+ Z, # pointer to the other branch
192
+ Y, # pointer to the output to be recomputed
193
+ DY, # pointer to the output gradient
194
+ DX, # pointer to the input gradient
195
+ DW, # pointer to the partial sum of weights gradient
196
+ DB, # pointer to the partial sum of biases gradient
197
+ DZ, # pointer to the other branch
198
+ Mean, # pointer to the mean
199
+ Rstd, # pointer to the 1/std
200
+ stride_x_row, # how much to increase the pointer when moving by 1 row
201
+ stride_z_row,
202
+ stride_y_row,
203
+ stride_dy_row,
204
+ stride_dx_row,
205
+ stride_dz_row,
206
+ stride_dw_row,
207
+ stride_db_row,
208
+ M, # number of rows in X
209
+ N, # number of columns in X
210
+ eps, # epsilon to avoid division by zero
211
+ rows_per_program,
212
+ NORM_BEFORE_GATE: tl.constexpr,
213
+ IS_RMS_NORM: tl.constexpr,
214
+ HAS_BIAS: tl.constexpr,
215
+ HAS_Z: tl.constexpr,
216
+ RECOMPUTE_OUTPUT: tl.constexpr,
217
+ BLOCK_N: tl.constexpr,
218
+ ):
219
+ # Map the program id to the elements of X, DX, and DY it should compute.
220
+ row_block_id = tl.program_id(0)
221
+ group = tl.program_id(1)
222
+ row_start = row_block_id * rows_per_program
223
+ cols = tl.arange(0, BLOCK_N)
224
+ mask = cols < N
225
+ X += row_start * stride_x_row + group * N
226
+ if HAS_Z:
227
+ Z += row_start * stride_z_row + group * N
228
+ DZ += row_start * stride_dz_row + group * N
229
+ DY += row_start * stride_dy_row + group * N
230
+ DX += row_start * stride_dx_row + group * N
231
+ if RECOMPUTE_OUTPUT:
232
+ Y += row_start * stride_y_row + group * N
233
+ if not IS_RMS_NORM:
234
+ Mean += group * M
235
+ Rstd += group * M
236
+ W += group * N
237
+ w = tl.load(W + cols, mask=mask).to(tl.float32)
238
+ if (RECOMPUTE_OUTPUT or HAS_Z) and HAS_BIAS:
239
+ B += group * N
240
+ b = tl.load(B + cols, mask=mask, other=0.).to(tl.float32)
241
+ dw = tl.zeros((BLOCK_N,), dtype=tl.float32)
242
+ if HAS_BIAS:
243
+ db = tl.zeros((BLOCK_N,), dtype=tl.float32)
244
+ row_end = min((row_block_id + 1) * rows_per_program, M)
245
+ for row in range(row_start, row_end):
246
+ # Load data to SRAM
247
+ x = tl.load(X + cols, mask=mask, other=0).to(tl.float32)
248
+ dy = tl.load(DY + cols, mask=mask, other=0).to(tl.float32)
249
+ if not IS_RMS_NORM:
250
+ mean = tl.load(Mean + row)
251
+ if HAS_Z and not NORM_BEFORE_GATE:
252
+ z = tl.load(Z + cols, mask=mask, other=0.).to(tl.float32)
253
+ x_og = x
254
+ x = x_og * z * tl.sigmoid(z)
255
+ rstd = tl.load(Rstd + row)
256
+ # Compute dx
257
+ xhat = (x - mean) * rstd if not IS_RMS_NORM else x * rstd
258
+ xhat = tl.where(mask, xhat, 0.)
259
+ if HAS_Z and NORM_BEFORE_GATE:
260
+ z = tl.load(Z + cols, mask=mask, other=0.).to(tl.float32)
261
+ z_sigmoid = tl.sigmoid(z)
262
+ y = xhat * w + b if HAS_BIAS else xhat * w
263
+ if RECOMPUTE_OUTPUT:
264
+ tl.store(Y + cols, y * z * z_sigmoid, mask=mask)
265
+ dz = dy * y * z_sigmoid * (1 + z * (1 - z_sigmoid))
266
+ tl.store(DZ + cols, dz, mask=mask)
267
+ dy *= z * z_sigmoid
268
+ else:
269
+ if RECOMPUTE_OUTPUT:
270
+ y = xhat * w + b if HAS_BIAS else xhat * w
271
+ tl.store(Y + cols, y, mask=mask)
272
+ wdy = w * dy
273
+ c1 = tl.sum(xhat * wdy, axis=0) / N
274
+ if not IS_RMS_NORM:
275
+ c2 = tl.sum(wdy, axis=0) / N
276
+ dx = (wdy - (xhat * c1 + c2)) * rstd
277
+ else:
278
+ dx = (wdy - xhat * c1) * rstd
279
+ dw += dy * xhat
280
+ if HAS_BIAS:
281
+ db += dy
282
+ if HAS_Z and not NORM_BEFORE_GATE:
283
+ z_sigmoid = tl.sigmoid(z)
284
+ dz = dx * x_og * z_sigmoid * (1 + z * (1 - z_sigmoid))
285
+ tl.store(DZ + cols, dz, mask=mask)
286
+ dx *= z * z_sigmoid
287
+ # Write dx
288
+ tl.store(DX + cols, dx, mask=mask)
289
+
290
+ X += stride_x_row
291
+ if HAS_Z:
292
+ Z += stride_z_row
293
+ DZ += stride_dz_row
294
+ if RECOMPUTE_OUTPUT:
295
+ Y += stride_y_row
296
+ DY += stride_dy_row
297
+ DX += stride_dx_row
298
+ tl.store(DW + row_block_id * stride_dw_row + group * N + cols, dw, mask=mask)
299
+ if HAS_BIAS:
300
+ tl.store(DB + row_block_id * stride_db_row + group * N + cols, db, mask=mask)
301
+
302
+
303
+ def layer_norm_bwd(
304
+ dy: torch.Tensor,
305
+ x: torch.Tensor,
306
+ weight: torch.Tensor,
307
+ bias: torch.Tensor,
308
+ eps: float,
309
+ mean: torch.Tensor,
310
+ rstd: torch.Tensor,
311
+ z: torch.Tensor = None,
312
+ group_size: int = None,
313
+ norm_before_gate: bool = True,
314
+ is_rms_norm: bool = False,
315
+ recompute_output: bool = False,
316
+ dz: torch.Tensor = None,
317
+ out: torch.Tensor = None,
318
+ ):
319
+ M, N = x.shape
320
+ if group_size is None:
321
+ group_size = N
322
+ assert N % group_size == 0
323
+ ngroups = N // group_size
324
+ assert x.stride(-1) == 1
325
+ assert dy.stride(-1) == 1
326
+ assert dy.shape == (M, N)
327
+ if z is not None:
328
+ assert z.stride(-1) == 1
329
+ assert z.shape == (M, N)
330
+ assert weight.shape == (N,)
331
+ assert weight.stride(-1) == 1
332
+ if bias is not None:
333
+ assert bias.stride(-1) == 1
334
+ assert bias.shape == (N,)
335
+ # allocate output
336
+ dx = torch.empty_like(x)
337
+ if dz is not None:
338
+ assert z is not None
339
+ assert dz.shape == z.shape
340
+ assert dz.stride(-1) == 1
341
+ else:
342
+ dz = torch.empty_like(z) if z is not None else None
343
+ if recompute_output:
344
+ if out is None:
345
+ out = torch.empty_like(x)
346
+ assert out.shape == x.shape
347
+
348
+ # Less than 64KB per feature: enqueue fused kernel
349
+ MAX_FUSED_SIZE = 65536 // x.element_size()
350
+ BLOCK_N = min(MAX_FUSED_SIZE, triton.next_power_of_2(group_size))
351
+ if group_size > BLOCK_N:
352
+ raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
353
+ # heuristics for number of warps
354
+ num_warps = min(max(BLOCK_N // 256, 1), 8)
355
+ sm_count = get_multiprocessor_count(x.device.index)
356
+ # If group size is small (e.g., 64), we're only using 1 warp. So having just 108 programs
357
+ # would limit the occupancy.
358
+ nrow_groups = math.ceil(sm_count * math.ceil(4 / num_warps) / ngroups)
359
+ _dw = torch.empty((nrow_groups, N), dtype=torch.float32, device=weight.device)
360
+ _db = torch.empty((nrow_groups, N), dtype=torch.float32, device=bias.device) if bias is not None else None
361
+ rows_per_program = math.ceil(M / nrow_groups)
362
+ grid = (nrow_groups, ngroups)
363
+ layer_norm_bwd_kernel[grid](
364
+ x,
365
+ weight,
366
+ bias,
367
+ z,
368
+ out if recompute_output else None,
369
+ dy,
370
+ dx,
371
+ _dw,
372
+ _db,
373
+ dz,
374
+ mean,
375
+ rstd,
376
+ x.stride(0),
377
+ z.stride(0) if z is not None else 0,
378
+ 0 if not recompute_output else out.stride(0),
379
+ dy.stride(0),
380
+ dx.stride(0),
381
+ dz.stride(0) if dz is not None else 0,
382
+ _dw.stride(0),
383
+ _db.stride(0) if _db is not None else 0,
384
+ M, group_size, eps,
385
+ rows_per_program,
386
+ BLOCK_N=BLOCK_N,
387
+ NORM_BEFORE_GATE=norm_before_gate,
388
+ IS_RMS_NORM=is_rms_norm,
389
+ num_warps=num_warps,
390
+ )
391
+ dw = _dw.sum(0).to(weight.dtype)
392
+ db = _db.sum(0).to(bias.dtype) if bias is not None else None
393
+ return (dx, dw, db, dz) if not recompute_output else (dx, dw, db, dz, out)
394
+
395
+
396
+ class LayerNormFn(torch.autograd.Function):
397
+
398
+ @input_guard
399
+ @staticmethod
400
+ def forward(ctx, x, weight, bias, z=None, eps=1e-6, group_size=None, norm_before_gate=True,
401
+ is_rms_norm=False):
402
+ """If z is not None, we do norm(x) * silu(z) if norm_before_gate, else norm(x * silu(z))
403
+ """
404
+
405
+ x_shape_og = x.shape
406
+ # reshape input data into 2D tensor
407
+ x = x.reshape(-1, x.shape[-1])
408
+ if x.stride(-1) != 1:
409
+ x = x.contiguous()
410
+ if z is not None:
411
+ assert z.shape == x_shape_og
412
+ z = z.reshape(-1, z.shape[-1])
413
+ if z.stride(-1) != 1:
414
+ z = z.contiguous()
415
+ weight = weight.contiguous()
416
+ if bias is not None:
417
+ bias = bias.contiguous()
418
+ y, mean, rstd = layer_norm_fwd(
419
+ x,
420
+ weight,
421
+ bias,
422
+ eps,
423
+ z=z,
424
+ group_size=group_size,
425
+ norm_before_gate=norm_before_gate,
426
+ is_rms_norm=is_rms_norm,
427
+ )
428
+ ctx.save_for_backward(x, weight, bias, mean, rstd, z)
429
+ ctx.x_shape_og = x_shape_og
430
+ ctx.eps = eps
431
+ ctx.group_size = group_size
432
+ ctx.norm_before_gate = norm_before_gate
433
+ ctx.is_rms_norm = is_rms_norm
434
+ return y.reshape(x_shape_og)
435
+
436
+ @input_guard
437
+ @staticmethod
438
+ def backward(ctx, dy):
439
+ x, weight, bias, mean, rstd, z = ctx.saved_tensors
440
+ dy = dy.reshape(-1, dy.shape[-1])
441
+ if dy.stride(-1) != 1:
442
+ dy = dy.contiguous()
443
+ assert dy.shape == x.shape
444
+ dx, dw, db, dz = layer_norm_bwd(
445
+ dy,
446
+ x,
447
+ weight,
448
+ bias,
449
+ ctx.eps,
450
+ mean,
451
+ rstd,
452
+ z,
453
+ ctx.group_size,
454
+ ctx.norm_before_gate,
455
+ ctx.is_rms_norm,
456
+ )
457
+ dx = dx.reshape(ctx.x_shape_og)
458
+ dz = dz.reshape(ctx.x_shape_og) if dz is not None else None
459
+ return dx, dw, db, dz, None, None, None, None
460
+
461
+
462
+ def layernorm_fn(x, weight, bias, z=None, eps=1e-6, group_size=None, norm_before_gate=True, is_rms_norm=False):
463
+ return LayerNormFn.apply(x, weight, bias, z, eps, group_size, norm_before_gate, is_rms_norm)
464
+
465
+
466
+ def rmsnorm_fn(x, weight, bias, z=None, eps=1e-6, group_size=None, norm_before_gate=True):
467
+ return LayerNormFn.apply(x, weight, bias, z, eps, group_size, norm_before_gate, True)
468
+
469
+
470
+ class LayerNormGated(nn.Module):
471
+
472
+ def __init__(
473
+ self,
474
+ hidden_size,
475
+ eps: float = 1e-5,
476
+ group_size: int | None = None,
477
+ norm_before_gate: bool = True,
478
+ device: torch.device | None = None,
479
+ dtype: torch.dtype | None = None,
480
+ ):
481
+ """If group_size is not None, we do GroupNorm with each group having group_size elements.
482
+ group_size=None is equivalent to group_size=hidden_size (i.e. there's only 1 group).
483
+ """
484
+
485
+ factory_kwargs = {"device": device, "dtype": dtype}
486
+ super().__init__()
487
+ self.eps = eps
488
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
489
+ self.bias = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
490
+ self.group_size = group_size
491
+ self.norm_before_gate = norm_before_gate
492
+ self.reset_parameters()
493
+
494
+ def reset_parameters(self):
495
+ torch.nn.init.ones_(self.weight)
496
+ torch.nn.init.zeros_(self.bias)
497
+
498
+ def forward(self, x, z=None):
499
+ """If z is not None, we do norm(x) * silu(z) if norm_before_gate, else norm(x * silu(z))
500
+ """
501
+ return layernorm_fn(x, self.weight, self.bias, z=z, group_size=self.group_size, eps=self.eps,
502
+ norm_before_gate=self.norm_before_gate)
503
+
504
+
505
+ class RMSNormGated(nn.Module):
506
+
507
+ def __init__(
508
+ self,
509
+ hidden_size,
510
+ eps: float = 1e-5,
511
+ group_size: int | None = None,
512
+ norm_before_gate: bool = False,
513
+ device: torch.device | None = None,
514
+ dtype: torch.dtype | None = None,
515
+ ):
516
+ """If group_size is not None, we do GroupNorm with each group having group_size elements.
517
+ group_size=None is equivalent to group_size=hidden_size (i.e. there's only 1 group).
518
+ """
519
+ factory_kwargs = {"device": device, "dtype": dtype}
520
+ super().__init__()
521
+ self.eps = eps
522
+ self.weight = nn.Parameter(torch.empty(hidden_size, **factory_kwargs))
523
+ self.register_parameter("bias", None)
524
+ self.group_size = group_size
525
+ self.norm_before_gate = norm_before_gate
526
+ self.reset_parameters()
527
+
528
+ def reset_parameters(self):
529
+ torch.nn.init.ones_(self.weight)
530
+
531
+ def forward(self, x, z=None):
532
+ """If z is not None, we do norm(x) * silu(z) if norm_before_gate, else norm(x * silu(z))
533
+ """
534
+ return rmsnorm_fn(x, self.weight, self.bias, z=z, eps=self.eps, group_size=self.group_size,
535
+ norm_before_gate=self.norm_before_gate)
build/torch-cuda/modules/mlp.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 functools import partial
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ import torch
14
+ import torch.nn as nn
15
+ from torch.distributed import DeviceMesh
16
+ from torch.distributed.tensor import Placement, Replicate, Shard, distribute_module
17
+ from torch.distributed.tensor.parallel import ParallelStyle
18
+
19
+ from ..modules.activations import powglu, powglu_linear, swiglu, swiglu_linear
20
+
21
+ try:
22
+ from torch.distributed.tensor import DTensor
23
+ except (ImportError, AttributeError):
24
+ DTensor = None
25
+
26
+ if TYPE_CHECKING:
27
+ from transformers.processing_utils import Unpack
28
+
29
+
30
+ class GatedMLP(nn.Module):
31
+
32
+ def __init__(
33
+ self,
34
+ hidden_size: int,
35
+ hidden_ratio: int | None = None,
36
+ intermediate_size: int | None = None,
37
+ hidden_act: str = 'swish',
38
+ fuse_swiglu: bool = True,
39
+ powglu_power: float = 3.0,
40
+ ) -> GatedMLP:
41
+ super().__init__()
42
+
43
+ self.hidden_size = hidden_size
44
+ # the final number of params is `hidden_ratio * hidden_size^2`
45
+ # `intermediate_size` is chosen to be a multiple of 256 closest to `2/3 * hidden_size * hidden_ratio`
46
+ if hidden_ratio is None:
47
+ hidden_ratio = 4
48
+ if intermediate_size is None:
49
+ intermediate_size = int(hidden_size * hidden_ratio * 2 / 3)
50
+ intermediate_size = 256 * ((intermediate_size + 256 - 1) // 256)
51
+ self.hidden_ratio = hidden_ratio
52
+ self.intermediate_size = intermediate_size
53
+ self.hidden_act = hidden_act
54
+ self.fuse_swiglu = fuse_swiglu
55
+ self.powglu_power = powglu_power
56
+
57
+ if hidden_act not in ('swish', 'powlu'):
58
+ raise ValueError(f'Unsupported hidden_act: {hidden_act}')
59
+
60
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
61
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
62
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
63
+ if self.fuse_swiglu and hidden_act == 'swish':
64
+ self.swiglu_linear = SwiGLULinear()
65
+
66
+ def forward(
67
+ self,
68
+ x: torch.Tensor,
69
+ **kwargs: Unpack[Any],
70
+ ) -> torch.Tensor:
71
+ gate, y = self.gate_proj(x), self.up_proj(x)
72
+ if self.hidden_act == 'powlu':
73
+ if self.fuse_swiglu:
74
+ return powglu_linear(gate, y, self.down_proj.weight, self.down_proj.bias, self.powglu_power)
75
+ return self.down_proj(powglu(gate, y, self.powglu_power))
76
+ if self.fuse_swiglu:
77
+ return self.swiglu_linear(gate, y, self.down_proj.weight, self.down_proj.bias)
78
+ return self.down_proj(swiglu(gate, y))
79
+
80
+
81
+ class SwiGLULinear(nn.Module):
82
+
83
+ def forward(self, x, y, weight, bias):
84
+ return swiglu_linear(x, y, weight, bias)
85
+
86
+
87
+ class SwiGLULinearParallel(ParallelStyle):
88
+ def __init__(
89
+ self,
90
+ *,
91
+ input_layouts: Placement | None = None,
92
+ output_layouts: Placement | None = None,
93
+ use_local_output: bool = True,
94
+ ):
95
+ super().__init__()
96
+ self.input_layouts = (input_layouts or Shard(-1),)
97
+ self.output_layouts = (output_layouts or Replicate(),)
98
+ self.desired_input_layouts = (Shard(-1),)
99
+ self.use_local_output = use_local_output
100
+
101
+ @staticmethod
102
+ def _prepare_input_fn(
103
+ input_layouts, desired_input_layouts, mod, inputs, device_mesh,
104
+ ):
105
+ x, y, weight, bias = inputs
106
+ if not isinstance(x, DTensor):
107
+ x = DTensor.from_local(x, device_mesh, input_layouts, run_check=False)
108
+ if x.placements != desired_input_layouts:
109
+ x = x.redistribute(placements=desired_input_layouts, async_op=True)
110
+
111
+ if not isinstance(y, DTensor):
112
+ y = DTensor.from_local(y, device_mesh, input_layouts, run_check=False)
113
+ if y.placements != desired_input_layouts:
114
+ y = y.redistribute(placements=desired_input_layouts, async_op=True)
115
+
116
+ if not isinstance(weight, DTensor):
117
+ weight = DTensor.from_local(weight, device_mesh, (Shard(1),))
118
+
119
+ if bias is not None and not isinstance(bias, DTensor):
120
+ bias = DTensor.from_local(bias, device_mesh, (Replicate(),))
121
+
122
+ return x, y, weight, bias
123
+
124
+ @staticmethod
125
+ def _prepare_output_fn(output_layouts, use_local_output, mod, outputs, device_mesh):
126
+ # Rowwise sharding produces partial output, depending on output layouts:
127
+ # 1. to replicate -> allreduce
128
+ # 2. to shard -> reduce_scatter
129
+ if outputs.placements != output_layouts:
130
+ outputs = outputs.redistribute(placements=output_layouts, async_op=True)
131
+ # back to local tensor if use_local_output is True
132
+ return outputs.to_local() if use_local_output else outputs
133
+
134
+ def _apply(self, module: nn.Module, device_mesh: DeviceMesh) -> nn.Module:
135
+ return distribute_module(
136
+ module,
137
+ device_mesh,
138
+ partition_fn=None,
139
+ input_fn=partial(self._prepare_input_fn, self.input_layouts, self.desired_input_layouts),
140
+ output_fn=partial(self._prepare_output_fn, self.output_layouts, self.use_local_output),
141
+ )
build/torch-cuda/modules/parallel.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.nn as nn
9
+ from torch.distributed import DeviceMesh
10
+ from torch.distributed.tensor import distribute_module
11
+ from torch.distributed.tensor.parallel import ParallelStyle
12
+ from torch.distributed.tensor.placement_types import Placement
13
+
14
+ try:
15
+ from torch.distributed.tensor import DTensor
16
+ except (ImportError, AttributeError):
17
+ DTensor = None
18
+
19
+
20
+ class PrepareModuleWeight(ParallelStyle):
21
+ def __init__(self, *, layouts: Placement | None = None):
22
+ super().__init__()
23
+ self.layouts = layouts
24
+
25
+ def _replicate_module_fn(
26
+ self,
27
+ name: str,
28
+ module: nn.Module,
29
+ device_mesh: DeviceMesh,
30
+ ):
31
+ for p_name, param in module.named_parameters():
32
+ replicated_param = nn.Parameter(
33
+ DTensor.from_local(param, device_mesh, [self.layouts], run_check=False),
34
+ )
35
+ module.register_parameter(p_name, replicated_param)
36
+
37
+ def _apply(self, module: nn.Module, device_mesh: DeviceMesh) -> nn.Module:
38
+ return distribute_module(
39
+ module,
40
+ device_mesh,
41
+ partition_fn=self._replicate_module_fn,
42
+ input_fn=None,
43
+ output_fn=None,
44
+ )
build/torch-cuda/modules/rotary.py ADDED
@@ -0,0 +1,519 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 torch.nn as nn
10
+ import triton
11
+ import triton.language as tl
12
+ from einops import rearrange, repeat
13
+
14
+ from ..modules.backends import dispatch
15
+ from ..ops.utils import prepare_chunk_indices
16
+ from ..utils import IS_AMD, autotune_cache_kwargs, get_multiprocessor_count, input_guard
17
+
18
+ NUM_WARPS_AUTOTUNE = [2, 4, 8, 16] if IS_AMD else [2, 4, 8, 16, 32]
19
+
20
+
21
+ def rotate_half(x, interleaved=False):
22
+ if not interleaved:
23
+ x1, x2 = x.chunk(2, dim=-1)
24
+ return torch.cat((-x2, x1), dim=-1)
25
+ else:
26
+ x1, x2 = x[..., ::2], x[..., 1::2]
27
+ return rearrange(torch.stack((-x2, x1), dim=-1), '... d two -> ... (d two)', two=2)
28
+
29
+
30
+ def rotary_embedding_ref(x, cos, sin, interleaved=False):
31
+ ro_dim = cos.shape[-1] * 2
32
+ assert ro_dim <= x.shape[-1]
33
+ cos = repeat(cos, '... d -> ... 1 (2 d)' if not interleaved else '... d -> ... 1 (d 2)')
34
+ sin = repeat(sin, '... d -> ... 1 (2 d)' if not interleaved else '... d -> ... 1 (d 2)')
35
+ return torch.cat([x[..., :ro_dim] * cos + rotate_half(x[..., :ro_dim], interleaved) * sin, x[..., ro_dim:]], -1)
36
+
37
+
38
+ @triton.autotune(
39
+ configs=[
40
+ triton.Config({}, num_warps=num_warps, num_stages=num_stages)
41
+ for num_warps in NUM_WARPS_AUTOTUNE
42
+ for num_stages in [2, 3, 4]
43
+ ],
44
+ key=['B', 'H', 'D', 'INTERLEAVED'],
45
+ **autotune_cache_kwargs,
46
+ )
47
+ @triton.jit(do_not_specialize=['T'])
48
+ def rotary_embedding_kernel(
49
+ x,
50
+ cos,
51
+ sin,
52
+ y,
53
+ cu_seqlens,
54
+ chunk_indices,
55
+ seq_offsets,
56
+ T,
57
+ B: tl.constexpr,
58
+ H: tl.constexpr,
59
+ D: tl.constexpr,
60
+ R: tl.constexpr,
61
+ TR: tl.constexpr,
62
+ BT: tl.constexpr,
63
+ BD: tl.constexpr,
64
+ IS_SEQLEN_OFFSETS_TENSOR: tl.constexpr,
65
+ IS_VARLEN: tl.constexpr,
66
+ INTERLEAVED: tl.constexpr,
67
+ CONJUGATE: tl.constexpr,
68
+ ):
69
+ i_t, i_b, i_h = tl.program_id(0), tl.program_id(1), tl.program_id(2)
70
+
71
+ if IS_VARLEN:
72
+ 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)
73
+ bos, eos = tl.load(cu_seqlens + i_n), tl.load(cu_seqlens + i_n + 1)
74
+ T = eos - bos
75
+ x = x + bos * H*D + i_h * D
76
+ y = y + bos * H*D + i_h * D
77
+ else:
78
+ i_n = i_b
79
+ x = x + i_n * T*H*D + i_h * D
80
+ y = y + i_n * T*H*D + i_h * D
81
+
82
+ if i_t * BT >= T:
83
+ return
84
+
85
+ o_t = i_t * BT + tl.arange(0, BT)
86
+ if not IS_SEQLEN_OFFSETS_TENSOR:
87
+ o_cs = o_t + seq_offsets
88
+ else:
89
+ o_cs = o_t + tl.load(seq_offsets + i_n)
90
+ m_t = (o_t >= 0) & (o_t < T) & (o_cs >= 0) & (o_cs < TR)
91
+
92
+ if not INTERLEAVED:
93
+ # Load the 1st and 2nd halves of x, do calculation, then store to 1st and 2nd halves of out
94
+ o_r = tl.arange(0, BD // 2)
95
+ p_x = x + o_t[:, None] * H*D + o_r[None, :]
96
+ p_cos = cos + (o_cs[:, None] * R + o_r[None, :])
97
+ p_sin = sin + (o_cs[:, None] * R + o_r[None, :])
98
+ mask = m_t[:, None] & (o_r < R)[None, :]
99
+
100
+ b_cos = tl.load(p_cos, mask=mask, other=1.0).to(tl.float32)
101
+ b_sin = tl.load(p_sin, mask=mask, other=0.0).to(tl.float32)
102
+ b_x0 = tl.load(p_x, mask=mask, other=0.0).to(tl.float32)
103
+ b_x1 = tl.load(p_x + R, mask=mask, other=0.0).to(tl.float32)
104
+ if CONJUGATE:
105
+ b_sin = -b_sin
106
+ b_o0 = b_x0 * b_cos - b_x1 * b_sin
107
+ b_o1 = b_x0 * b_sin + b_x1 * b_cos
108
+ # write back result
109
+ p_y = y + (o_t[:, None] * H*D + o_r[None, :])
110
+ tl.store(p_y, b_o0, mask=mask)
111
+ tl.store(p_y + R, b_o1, mask=mask)
112
+ else:
113
+ # We don't want to load x[0, 2, 4, ...] and x[1, 3, 5, ...] separately since both are slow.
114
+ # Instead, we load x0 = x[0, 1, 2, 3, ...] and x1 = x[1, 0, 3, 2, ...].
115
+ # Loading x0 will be fast but x1 will be slow.
116
+ # Then we load cos = cos[0, 0, 1, 1, ...] and sin = sin[0, 0, 1, 1, ...].
117
+ # Then we do the calculation and use tl.where to pick put the right outputs for the even
118
+ # and for the odd indices.
119
+ o_d = tl.arange(0, BD)
120
+ o_d_swap = o_d + ((o_d + 1) % 2) * 2 - 1 # 1, 0, 3, 2, 5, 4, ...
121
+ o_d_repeat = tl.arange(0, BD) // 2
122
+ p_x0 = x + o_t[:, None] * H*D + o_d[None, :]
123
+ p_x1 = x + o_t[:, None] * H*D + o_d_swap[None, :]
124
+ p_cos = cos + (o_cs[:, None] * R + o_d_repeat[None, :])
125
+ p_sin = sin + (o_cs[:, None] * R + o_d_repeat[None, :])
126
+ mask = m_t[:, None] & (o_d_repeat < R)[None, :]
127
+
128
+ b_cos = tl.load(p_cos, mask=mask, other=1.0).to(tl.float32)
129
+ b_sin = tl.load(p_sin, mask=mask, other=0.0).to(tl.float32)
130
+ b_x0 = tl.load(p_x0, mask=mask, other=0.0).to(tl.float32)
131
+ b_x1 = tl.load(p_x1, mask=mask, other=0.0).to(tl.float32)
132
+ if CONJUGATE:
133
+ b_sin = -b_sin
134
+ b_o0 = b_x0 * b_cos
135
+ b_o1 = b_x1 * b_sin
136
+ b_y = tl.where(o_d[None, :] % 2 == 0, b_o0 - b_o1, b_o0 + b_o1)
137
+ p_y = y + (o_t[:, None] * H*D + o_d[None, :])
138
+ tl.store(p_y, b_y, mask=mask)
139
+
140
+
141
+ @dispatch('modules')
142
+ def rotary_embedding_fwdbwd(
143
+ x: torch.Tensor,
144
+ cos: torch.Tensor,
145
+ sin: torch.Tensor,
146
+ seqlen_offsets: int | torch.Tensor = 0,
147
+ cu_seqlens: torch.Tensor | None = None,
148
+ interleaved: bool = False,
149
+ inplace: bool = False,
150
+ conjugate: bool = False,
151
+ chunk_indices: torch.LongTensor | None = None,
152
+ ) -> torch.Tensor:
153
+ """
154
+ Args:
155
+ x: [B, T, H, D].
156
+ cos: [TR, R / 2]
157
+ sin: [TR, R / 2]
158
+ seqlen_offsets: integer or integer tensor of size [N]
159
+ cu_seqlens: [N + 1,] or None
160
+
161
+ Returns:
162
+ y: [B, T, H, D]
163
+ """
164
+ is_varlen = cu_seqlens is not None
165
+
166
+ B, T, H, D = x.shape
167
+ N = B if not is_varlen else cu_seqlens.shape[0] - 1
168
+ TR, R = cos.shape
169
+ R2 = R * 2
170
+
171
+ assert D <= 256, "Only support D <= 256"
172
+ assert TR >= T, f"TR must be >= T, got {TR} and {T}"
173
+
174
+ assert cos.dtype == sin.dtype, f"cos and sin must have the same dtype, got {cos.dtype} and {sin.dtype}"
175
+ assert x.dtype == cos.dtype, f"Input and cos/sin must have the same dtype, got {x.dtype} and {cos.dtype}"
176
+
177
+ if isinstance(seqlen_offsets, torch.Tensor):
178
+ assert seqlen_offsets.shape == (N,)
179
+ assert seqlen_offsets.dtype in [torch.int32, torch.int64]
180
+ else:
181
+ assert seqlen_offsets + T <= TR
182
+
183
+ # zeros_like: rows the kernel skips (negative o_cs under left-padding) must be defined, not uninitialized.
184
+ y = torch.zeros_like(x) if not inplace else x
185
+ if R2 < D and not inplace:
186
+ y[..., R2:].copy_(x[..., R2:])
187
+
188
+ BD = triton.next_power_of_2(R2)
189
+ BT = min(128, triton.next_power_of_2(triton.cdiv(T, get_multiprocessor_count(x.device.index))))
190
+ if chunk_indices is None and is_varlen:
191
+ chunk_indices = prepare_chunk_indices(cu_seqlens, BT)
192
+ NT = len(chunk_indices) if is_varlen else triton.cdiv(T, BT)
193
+
194
+ grid = (NT, B, H)
195
+ rotary_embedding_kernel[grid](
196
+ x,
197
+ cos,
198
+ sin,
199
+ y,
200
+ cu_seqlens,
201
+ chunk_indices,
202
+ seqlen_offsets,
203
+ B=B,
204
+ T=T,
205
+ H=H,
206
+ D=D,
207
+ R=R,
208
+ TR=TR,
209
+ BT=BT,
210
+ BD=BD,
211
+ IS_SEQLEN_OFFSETS_TENSOR=isinstance(seqlen_offsets, torch.Tensor),
212
+ IS_VARLEN=is_varlen,
213
+ INTERLEAVED=interleaved,
214
+ CONJUGATE=conjugate,
215
+ )
216
+ return y
217
+
218
+
219
+ class RotaryEmbeddingFunction(torch.autograd.Function):
220
+
221
+ @staticmethod
222
+ @input_guard
223
+ def forward(
224
+ ctx,
225
+ x,
226
+ cos,
227
+ sin,
228
+ interleaved=False,
229
+ inplace=False,
230
+ seqlen_offsets: int | torch.Tensor = 0,
231
+ cu_seqlens: torch.Tensor | None = None,
232
+ chunk_indices: torch.LongTensor | None = None,
233
+ ):
234
+ y = rotary_embedding_fwdbwd(
235
+ x,
236
+ cos,
237
+ sin,
238
+ seqlen_offsets=seqlen_offsets,
239
+ cu_seqlens=cu_seqlens,
240
+ interleaved=interleaved,
241
+ inplace=inplace,
242
+ chunk_indices=chunk_indices,
243
+ )
244
+ if isinstance(seqlen_offsets, int):
245
+ # Can't save int with save_for_backward
246
+ ctx.save_for_backward(cos, sin, cu_seqlens)
247
+ ctx.seqlen_offsets = seqlen_offsets
248
+ else:
249
+ ctx.save_for_backward(cos, sin, cu_seqlens, seqlen_offsets)
250
+ ctx.seqlen_offsets = None
251
+ ctx.interleaved = interleaved
252
+ ctx.inplace = inplace
253
+ ctx.chunk_indices = chunk_indices
254
+ return y if not inplace else x
255
+
256
+ @staticmethod
257
+ @input_guard
258
+ def backward(ctx, do):
259
+ seqlen_offsets = ctx.seqlen_offsets
260
+ if seqlen_offsets is None:
261
+ cos, sin, cu_seqlens, seqlen_offsets = ctx.saved_tensors
262
+ else:
263
+ cos, sin, cu_seqlens = ctx.saved_tensors
264
+ # TD [2023-09-02]: For some reason Triton (2.0.0.post1) errors with
265
+ # "[CUDA]: invalid device context", and cloning makes it work. Idk why. Triton 2.1.0 works.
266
+ if not ctx.interleaved and not ctx.inplace:
267
+ do = do.clone()
268
+ dx = rotary_embedding_fwdbwd(
269
+ do,
270
+ cos,
271
+ sin,
272
+ seqlen_offsets=seqlen_offsets,
273
+ cu_seqlens=cu_seqlens,
274
+ interleaved=ctx.interleaved,
275
+ inplace=ctx.inplace,
276
+ conjugate=True,
277
+ chunk_indices=ctx.chunk_indices,
278
+ )
279
+ return dx, None, None, None, None, None, None, None
280
+
281
+
282
+ def rotary_embedding(
283
+ x,
284
+ cos,
285
+ sin,
286
+ interleaved=False,
287
+ inplace=False,
288
+ seqlen_offsets: int | torch.Tensor = 0,
289
+ cu_seqlens: torch.Tensor | None = None,
290
+ chunk_indices: torch.LongTensor | None = None,
291
+ ):
292
+ """
293
+ Args:
294
+ x: [B, T, H, D]
295
+ cos, sin: [TR, R//2]
296
+ interleaved:
297
+ If True, rotate pairs of even and odd dimensions (GPT-J style) instead of 1st half and 2nd half (GPT-NeoX style).
298
+ inplace:
299
+ If True, apply rotary embedding in-place.
300
+ seqlen_offsets: [N,] or int.
301
+ Each sequence in x is shifted by this amount.
302
+ Most commonly used in inference when we have KV cache.
303
+ cu_seqlens: [N + 1,] or None
304
+
305
+ Returns:
306
+ out: [B, T, H, D]
307
+ """
308
+ return RotaryEmbeddingFunction.apply(
309
+ x,
310
+ cos,
311
+ sin,
312
+ interleaved,
313
+ inplace,
314
+ seqlen_offsets,
315
+ cu_seqlens,
316
+ chunk_indices,
317
+ )
318
+
319
+
320
+ class RotaryEmbedding(nn.Module):
321
+ """
322
+ The rotary position embeddings from RoFormer_ (Su et. al).
323
+ A crucial insight from the method is that the query and keys are
324
+ transformed by rotation matrices which depend on the relative positions.
325
+
326
+ Other implementations are available in the Rotary Transformer repo_ and in
327
+ GPT-NeoX_, GPT-NeoX was an inspiration
328
+
329
+ .. _RoFormer: https://arxiv.org/abs/2104.09864
330
+ .. _repo: https://github.com/ZhuiyiTechnology/roformer
331
+ .. _GPT-NeoX: https://github.com/EleutherAI/gpt-neox
332
+
333
+ If scale_base is not None, this implements XPos (Sun et al., https://arxiv.org/abs/2212.10554).
334
+ A recommended value for scale_base is 512: https://github.com/HazyResearch/flash-attention/issues/96
335
+ Reference: https://github.com/sunyt32/torchscale/blob/main/torchscale/component/xpos_relative_position.py
336
+ """
337
+
338
+ def __init__(
339
+ self,
340
+ dim: int,
341
+ base: float = 10000.0,
342
+ scale_base: float | None = None,
343
+ interleaved: bool = False,
344
+ pos_idx_in_fp32: bool = True,
345
+ device: torch.device | None = None,
346
+ ):
347
+ """
348
+ interleaved:
349
+ If True, rotate pairs of even and odd dimensions (GPT-J style) instead of 1st half and 2nd half (GPT-NeoX style).
350
+ pos_idx_in_fp32:
351
+ If True, the position indices [0.0, ..., seqlen - 1] are in fp32, otherwise they might be in lower precision.
352
+ This option was added because previously (before 2023-07-02), when we construct
353
+ the position indices, we use the dtype of self.inv_freq.
354
+ In most cases this would be fp32, but if the model is trained in pure bf16 (not mixed precision), then
355
+ self.inv_freq would be bf16, and the position indices are also in bf16.
356
+ Because of the limited precision of bf16 (e.g. 1995.0 is rounded to 2000.0), the
357
+ embeddings for some positions will coincide.
358
+ To maintain compatibility with models previously trained in pure bf16, we add this option.
359
+ """
360
+ super().__init__()
361
+
362
+ self.dim = dim
363
+ self.base = float(base)
364
+ self.scale_base = scale_base
365
+ self.interleaved = interleaved
366
+ self.pos_idx_in_fp32 = pos_idx_in_fp32
367
+ self.device = device
368
+
369
+ # Generate and save the inverse frequency buffer (non trainable)
370
+ self.register_buffer("inv_freq", torch.empty(-(dim // -2), dtype=torch.float32, device=device), persistent=False)
371
+
372
+ scale = None
373
+ if scale_base is not None:
374
+ scale = torch.empty(-(dim // -2), dtype=torch.float32, device=device)
375
+ self.register_buffer("scale", scale, persistent=False)
376
+
377
+ self._seq_len_cached = 0
378
+ self._cos_cached = None
379
+ self._sin_cached = None
380
+ self._cos_k_cached = None
381
+ self._sin_k_cached = None
382
+
383
+ self.reset_parameters()
384
+
385
+ def reset_parameters(self):
386
+ with torch.no_grad():
387
+ self.inv_freq.copy_(self._compute_inv_freq(device=self.inv_freq.device))
388
+ if self.scale_base is not None:
389
+ self.scale.copy_(self._compute_scale(device=self.scale.device))
390
+
391
+ def __repr__(self):
392
+ s = f"{self.__class__.__name__}("
393
+ s += f"dim={self.dim}, "
394
+ s += f"base={self.base}, "
395
+ s += f"interleaved={self.interleaved}, "
396
+ if self.scale_base is not None:
397
+ s += f"scale_base={self.scale_base}, "
398
+ s += f"pos_idx_in_fp32={self.pos_idx_in_fp32})"
399
+ return s
400
+
401
+ def _compute_inv_freq(self, device=None):
402
+ return 1.0 / (
403
+ self.base
404
+ ** (torch.arange(0, self.dim, 2, device=device, dtype=torch.float32) / self.dim)
405
+ )
406
+
407
+ def _compute_scale(self, device=None):
408
+ return (torch.arange(0, self.dim, 2, device=device, dtype=torch.float32) + 0.4 * self.dim) / (1.4 * self.dim)
409
+
410
+ def _update_cos_sin_cache(self, seqlen, device=None, dtype=None):
411
+ # Reset the tables if the sequence length has changed,
412
+ # if we're on a new device (possibly due to tracing for instance),
413
+ # or if we're switching from inference mode to training
414
+ if (
415
+ seqlen > self._seq_len_cached
416
+ or self._cos_cached is None
417
+ or self._cos_cached.device != device
418
+ or self._cos_cached.dtype != dtype
419
+ or (self.training and self._cos_cached.is_inference())
420
+ ):
421
+ self._seq_len_cached = seqlen
422
+ # We want fp32 here, not self.inv_freq.dtype, since the model could be loaded in bf16
423
+ # And the output of arange can be quite large, so bf16 would lose a lot of precision.
424
+ # However, for compatibility reason, we add an option to use the dtype of self.inv_freq.
425
+ if self.pos_idx_in_fp32:
426
+ t = torch.arange(seqlen, device=device, dtype=torch.float32)
427
+ # We want fp32 here as well since inv_freq will be multiplied with t, and the output
428
+ # will be large. Having it in bf16 will lose a lot of precision and cause the
429
+ # cos & sin output to change significantly.
430
+ # We want to recompute self.inv_freq if it was not loaded in fp32
431
+ if self.inv_freq.dtype != torch.float32:
432
+ inv_freq = self._compute_inv_freq(device=device)
433
+ else:
434
+ inv_freq = self.inv_freq
435
+ else:
436
+ t = torch.arange(seqlen, device=device, dtype=self.inv_freq.dtype)
437
+ inv_freq = self.inv_freq
438
+ # Don't do einsum, it converts fp32 to fp16 under AMP
439
+ # freqs = torch.einsum("i,j->ij", t, self.inv_freq)
440
+ freqs = torch.outer(t, inv_freq)
441
+ if self.scale is None:
442
+ self._cos_cached = torch.cos(freqs).to(dtype)
443
+ self._sin_cached = torch.sin(freqs).to(dtype)
444
+ else:
445
+ power = (
446
+ torch.arange(seqlen, dtype=self.scale.dtype, device=self.scale.device)
447
+ - seqlen // 2
448
+ ) / self.scale_base
449
+ scale = self.scale.to(device=power.device) ** rearrange(power, "s -> s 1")
450
+ # We want the multiplication by scale to happen in fp32
451
+ self._cos_cached = (torch.cos(freqs) * scale).to(dtype)
452
+ self._sin_cached = (torch.sin(freqs) * scale).to(dtype)
453
+ self._cos_k_cached = (torch.cos(freqs) / scale).to(dtype)
454
+ self._sin_k_cached = (torch.sin(freqs) / scale).to(dtype)
455
+
456
+ def forward(
457
+ self,
458
+ q: torch.Tensor,
459
+ k: torch.Tensor,
460
+ seqlen_offset: int | torch.Tensor = 0,
461
+ cu_seqlens: torch.Tensor | None = None,
462
+ max_seqlen: int | None = None,
463
+ chunk_indices: torch.LongTensor | None = None,
464
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
465
+ """
466
+ q: [B, T, H, D]
467
+ k: [B, T, H, D]
468
+ seqlen_offset:
469
+ [N] or int.
470
+ Each sequence in x is shifted by this amount.
471
+ Most commonly used in inference when we have KV cache.
472
+ cu_seqlens: [N + 1] or None
473
+ max_seqlen: int
474
+ """
475
+ if max_seqlen is not None:
476
+ self._update_cos_sin_cache(max_seqlen, device=q.device, dtype=q.dtype)
477
+ elif isinstance(seqlen_offset, int):
478
+ self._update_cos_sin_cache(q.shape[1] + seqlen_offset, device=q.device, dtype=q.dtype)
479
+ if self.scale is None:
480
+ q = rotary_embedding(
481
+ q,
482
+ self._cos_cached,
483
+ self._sin_cached,
484
+ interleaved=self.interleaved,
485
+ seqlen_offsets=seqlen_offset,
486
+ cu_seqlens=cu_seqlens,
487
+ chunk_indices=chunk_indices,
488
+ )
489
+ k = rotary_embedding(
490
+ k,
491
+ self._cos_cached,
492
+ self._sin_cached,
493
+ interleaved=self.interleaved,
494
+ seqlen_offsets=seqlen_offset,
495
+ cu_seqlens=cu_seqlens,
496
+ chunk_indices=chunk_indices,
497
+ )
498
+
499
+ else:
500
+ q = rotary_embedding(
501
+ q,
502
+ self._cos_cached,
503
+ self._sin_cached,
504
+ interleaved=self.interleaved,
505
+ seqlen_offsets=seqlen_offset,
506
+ cu_seqlens=cu_seqlens,
507
+ chunk_indices=chunk_indices,
508
+ )
509
+ k = rotary_embedding(
510
+ k,
511
+ self._cos_k_cached,
512
+ self._sin_k_cached,
513
+ interleaved=self.interleaved,
514
+ seqlen_offsets=seqlen_offset,
515
+ cu_seqlens=cu_seqlens,
516
+ chunk_indices=chunk_indices,
517
+ )
518
+
519
+ return q, k
build/torch-cuda/modules/token_shift.py ADDED
@@ -0,0 +1,573 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import triton.language as tl
11
+
12
+ from ..ops.utils import prepare_chunk_indices
13
+ from ..utils import IS_AMD, IS_NPU, autotune_cache_kwargs, get_multiprocessor_count, input_guard, tensor_cache
14
+
15
+ NUM_WARPS_AUTOTUNE = [2, 4, 8, 16] if IS_AMD else [2, 4, 8, 16, 32]
16
+ # Ascend Triton rejects 2-D grids whose product exceeds 65535 unless
17
+ # TRITON_ALL_BLOCKS_PARALLEL=1. Fall back to the long kernel instead.
18
+ _NPU_MAX_TRITON_GRID = 65535
19
+
20
+
21
+ def token_shift_ref(
22
+ x: torch.Tensor,
23
+ cu_seqlens: torch.Tensor | None = None,
24
+ ) -> torch.Tensor:
25
+ if cu_seqlens is not None:
26
+ # Variable length mode with cu_seqlens
27
+ assert x.dim() == 3, "Input must be [B, T, D]"
28
+ B, T, D = x.shape
29
+ assert B == 1, "Batch size must be 1 when using cu_seqlens"
30
+
31
+ result = torch.zeros_like(x)
32
+ N = cu_seqlens.shape[0] - 1
33
+
34
+ for i in range(N):
35
+ start = cu_seqlens[i].item()
36
+ end = cu_seqlens[i+1].item()
37
+ seq_len = end - start
38
+
39
+ if seq_len <= 1:
40
+ # For sequences of length 1 or 0, delta is simply -x
41
+ result[0, start:end] = -x[0, start:end]
42
+ else:
43
+ # For longer sequences, handle padding manually
44
+ shifted = torch.zeros_like(x[0, start:end])
45
+ shifted[1:] = x[0, start:end-1]
46
+ delta = shifted - x[0, start:end]
47
+ result[0, start:end] = delta
48
+
49
+ return result
50
+ else:
51
+ time_shift = torch.nn.ZeroPad2d((0, 0, 1, -1))
52
+ shifted = time_shift(x)
53
+ delta = shifted - x
54
+ return delta
55
+
56
+
57
+ @triton.heuristics({
58
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
59
+ 'USE_INITIAL_STATE': lambda args: args['cache'] is not None,
60
+ })
61
+ @triton.autotune(
62
+ configs=[
63
+ triton.Config({}, num_warps=num_warps, num_stages=num_stages)
64
+ for num_warps in NUM_WARPS_AUTOTUNE
65
+ for num_stages in [1, 2, 3]
66
+ ],
67
+ key=['BD'],
68
+ **autotune_cache_kwargs,
69
+ )
70
+ @triton.jit
71
+ def token_shift_fwd_kernel_short(
72
+ x,
73
+ y,
74
+ cu_seqlens,
75
+ cache,
76
+ cache_out,
77
+ T,
78
+ D: tl.constexpr,
79
+ BD: tl.constexpr,
80
+ IS_VARLEN: tl.constexpr,
81
+ USE_INITIAL_STATE: tl.constexpr,
82
+ STORE_FINAL_STATE: tl.constexpr,
83
+ IS_DECODE: tl.constexpr,
84
+ ):
85
+ i_b, i_t = tl.program_id(0), tl.program_id(1)
86
+
87
+ if IS_VARLEN:
88
+ i_n = i_b
89
+ bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(cu_seqlens + i_n + 1).to(tl.int32)
90
+ g_t = i_t + bos
91
+
92
+ if g_t >= eos:
93
+ return
94
+
95
+ is_first_pos = (i_t == 0)
96
+ is_last_pos = (g_t == eos - 1)
97
+ else:
98
+ g_t = i_t
99
+ is_first_pos = (g_t == 0)
100
+ is_last_pos = (g_t == T - 1)
101
+
102
+ o_d = tl.arange(0, BD)
103
+ m_d = o_d < D
104
+
105
+ if IS_VARLEN:
106
+ base_offset = g_t * D + o_d
107
+ else:
108
+ base_offset = i_b * T*D + g_t * D + o_d
109
+
110
+ b_x = tl.load(x + base_offset, mask=m_d)
111
+ if IS_VARLEN:
112
+ cache_offset = i_n * D + o_d # i_n is seq index
113
+ else:
114
+ cache_offset = i_b * D + o_d # i_b is batch index
115
+
116
+ if IS_DECODE and USE_INITIAL_STATE:
117
+ b_cache = tl.load(cache + cache_offset, mask=m_d)
118
+ delta = b_cache - b_x
119
+ tl.store(y + base_offset, delta, mask=m_d)
120
+ if STORE_FINAL_STATE:
121
+ tl.store(cache_out + cache_offset, b_x, mask=m_d)
122
+ return
123
+
124
+ if is_first_pos:
125
+ # First position in sequence: delta = -hidden_states
126
+ if USE_INITIAL_STATE:
127
+ # cache shape: [N, D]
128
+ b_cache = tl.load(cache + cache_offset, mask=m_d)
129
+ delta = b_cache - b_x
130
+ tl.store(y + base_offset, delta, mask=m_d)
131
+ else:
132
+ tl.store(y + base_offset, -b_x, mask=m_d)
133
+ return
134
+
135
+ # Other positions: delta = prev - curr
136
+ if IS_VARLEN:
137
+ prev_offset = (g_t-1) * D + o_d
138
+ else:
139
+ prev_offset = i_b * T*D + (g_t-1) * D + o_d
140
+
141
+ prev_values = tl.load(x + prev_offset, mask=m_d)
142
+ delta = prev_values - b_x
143
+ tl.store(y + base_offset, delta, mask=m_d)
144
+ if STORE_FINAL_STATE:
145
+ if is_last_pos:
146
+ tl.store(cache_out + cache_offset, b_x, mask=m_d)
147
+
148
+
149
+ @triton.heuristics({
150
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
151
+ 'USE_INITIAL_STATE': lambda args: args['cache'] is not None,
152
+ })
153
+ @triton.autotune(
154
+ configs=[
155
+ triton.Config({}, num_warps=num_warps, num_stages=num_stages)
156
+ for num_warps in NUM_WARPS_AUTOTUNE
157
+ for num_stages in [1, 2, 3]
158
+ ],
159
+ key=['BD', 'NB'],
160
+ **autotune_cache_kwargs,
161
+ )
162
+ @triton.jit
163
+ def token_shift_fwd_kernel_long(
164
+ x,
165
+ y,
166
+ cu_seqlens,
167
+ chunk_indices,
168
+ cache,
169
+ cache_out,
170
+ T,
171
+ D: tl.constexpr,
172
+ BD: tl.constexpr,
173
+ BT: tl.constexpr,
174
+ NB: tl.constexpr,
175
+ ND: tl.constexpr,
176
+ IS_VARLEN: tl.constexpr,
177
+ USE_INITIAL_STATE: tl.constexpr,
178
+ STORE_FINAL_STATE: tl.constexpr,
179
+ ):
180
+ i_dt, i_b = tl.program_id(0), tl.program_id(1)
181
+ i_d, i_t = i_dt % ND, i_dt // ND
182
+
183
+ if IS_VARLEN:
184
+ i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), \
185
+ tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
186
+ bos, eos = tl.load(cu_seqlens + i_n), tl.load(cu_seqlens + i_n + 1)
187
+ t_start = i_t * BT
188
+ t_end = tl.minimum(t_start + BT, eos - bos)
189
+ else:
190
+ i_n = i_b
191
+ bos, eos = i_b * T, (i_b + 1) * T
192
+ t_start = i_t * BT
193
+ t_end = tl.minimum(t_start + BT, T)
194
+
195
+ o_d = i_d * BD + tl.arange(0, BD)
196
+ m_d = o_d < D
197
+
198
+ for t in range(t_start, t_end):
199
+ global_t = bos + t
200
+ offset = global_t * D + o_d
201
+ b_x = tl.load(x + offset, mask=m_d)
202
+ is_first = (global_t == bos)
203
+ if is_first:
204
+ if USE_INITIAL_STATE:
205
+ # cache shape: [N, D]
206
+ cache_off = i_n * D + o_d if IS_VARLEN else i_b * D + o_d
207
+ b_cache = tl.load(cache + cache_off, mask=m_d)
208
+ delta = b_cache - b_x
209
+ else:
210
+ delta = -b_x
211
+ else:
212
+ prev_off = offset - D
213
+ b_prev = tl.load(x + prev_off, mask=m_d)
214
+ delta = b_prev - b_x
215
+
216
+ tl.store(y + offset, delta, mask=m_d)
217
+
218
+ if STORE_FINAL_STATE:
219
+ if global_t == eos - 1:
220
+ cache_out_off = i_n * D + o_d if IS_VARLEN else i_b * D + o_d
221
+ tl.store(cache_out + cache_out_off, b_x, mask=m_d)
222
+
223
+
224
+ @triton.heuristics({
225
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
226
+ 'USE_INITIAL_STATE': lambda args: args['grad_cache_out'] is not None,
227
+ 'HAS_DCACHE': lambda args: args['grad_cache_in'] is not None,
228
+ })
229
+ @triton.autotune(
230
+ configs=[
231
+ triton.Config({}, num_warps=num_warps, num_stages=num_stages)
232
+ for num_warps in NUM_WARPS_AUTOTUNE
233
+ for num_stages in [1, 2, 3]
234
+ ],
235
+ key=['BD'],
236
+ **autotune_cache_kwargs,
237
+ )
238
+ @triton.jit
239
+ def token_shift_bwd_kernel_short(
240
+ dx,
241
+ dy,
242
+ cu_seqlens,
243
+ grad_cache_in,
244
+ grad_cache_out,
245
+ T,
246
+ D: tl.constexpr,
247
+ BD: tl.constexpr,
248
+ IS_VARLEN: tl.constexpr,
249
+ USE_INITIAL_STATE: tl.constexpr,
250
+ HAS_DCACHE: tl.constexpr,
251
+ ):
252
+ i_b, i_t = tl.program_id(0), tl.program_id(1)
253
+
254
+ if IS_VARLEN:
255
+ i_n = i_b
256
+ bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(cu_seqlens + i_n + 1).to(tl.int32)
257
+ g_t = i_t + bos
258
+ if g_t >= eos:
259
+ return
260
+ is_first_pos = (g_t == bos)
261
+ is_last_pos = (g_t == eos - 1)
262
+ else:
263
+ g_t = i_t
264
+ is_first_pos = (g_t == 0)
265
+ is_last_pos = (g_t == T - 1)
266
+
267
+ o_d = tl.arange(0, BD)
268
+ m_d = o_d < D
269
+
270
+ if IS_VARLEN:
271
+ base_offset = g_t * D + o_d
272
+ # This should not be used for varlen
273
+ cache_off = i_n * D + o_d
274
+ else:
275
+ base_offset = i_b * T * D + g_t * D + o_d
276
+ cache_off = i_b * D + o_d
277
+
278
+ b_dy = tl.load(dy + base_offset, mask=m_d)
279
+
280
+ if is_last_pos:
281
+ # grad = -grad_delta[t] + grad_cache_in(from next rank)
282
+ if HAS_DCACHE:
283
+ b_dy_cache = tl.load(grad_cache_in + cache_off, mask=m_d)
284
+ b_dx = -b_dy + b_dy_cache
285
+ else:
286
+ b_dx = -b_dy
287
+ else:
288
+ # grad = -grad_delta[t] + grad_delta[t+1]
289
+ if IS_VARLEN:
290
+ next_offset = (g_t + 1) * D + o_d
291
+ else:
292
+ next_offset = i_b * T * D + (g_t + 1) * D + o_d
293
+ b_dx = -b_dy + tl.load(dy + next_offset, mask=m_d)
294
+
295
+ tl.store(dx + base_offset, b_dx, mask=m_d)
296
+
297
+ if USE_INITIAL_STATE:
298
+ if is_first_pos:
299
+ tl.store(grad_cache_out + cache_off, b_dy, mask=m_d)
300
+
301
+
302
+ @triton.heuristics({
303
+ 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
304
+ 'USE_INITIAL_STATE': lambda args: args['grad_cache_out'] is not None,
305
+ 'HAS_DCACHE': lambda args: args['grad_cache_in'] is not None,
306
+ })
307
+ @triton.autotune(
308
+ configs=[
309
+ triton.Config({}, num_warps=num_warps, num_stages=num_stages)
310
+ for num_warps in NUM_WARPS_AUTOTUNE
311
+ for num_stages in [1, 2, 3]
312
+ ],
313
+ key=['BD', 'NB'],
314
+ **autotune_cache_kwargs,
315
+ )
316
+ @triton.jit
317
+ def token_shift_bwd_kernel_long(
318
+ dx,
319
+ dy,
320
+ cu_seqlens,
321
+ chunk_indices,
322
+ grad_cache_in,
323
+ grad_cache_out,
324
+ T,
325
+ D: tl.constexpr,
326
+ BD: tl.constexpr,
327
+ BT: tl.constexpr,
328
+ NB: tl.constexpr,
329
+ ND: tl.constexpr,
330
+ IS_VARLEN: tl.constexpr,
331
+ USE_INITIAL_STATE: tl.constexpr,
332
+ HAS_DCACHE: tl.constexpr,
333
+ ):
334
+ i_dt, i_b = tl.program_id(0), tl.program_id(1)
335
+ i_d, i_t_blk = i_dt % ND, i_dt // ND
336
+
337
+ if IS_VARLEN:
338
+ i_n, i_t_blk = tl.load(chunk_indices + i_t_blk * 2).to(tl.int32), \
339
+ tl.load(chunk_indices + i_t_blk * 2 + 1).to(tl.int32)
340
+ bos, eos = tl.load(cu_seqlens + i_n), tl.load(cu_seqlens + i_n + 1)
341
+ t_start = i_t_blk * BT
342
+ t_end = tl.minimum(t_start + BT, eos - bos)
343
+ else:
344
+ i_n = i_b
345
+ bos, eos = i_b * T, (i_b + 1) * T
346
+ t_start = i_t_blk * BT
347
+ t_end = tl.minimum(t_start + BT, T)
348
+
349
+ o_d = i_d * BD + tl.arange(0, BD)
350
+ m_d = o_d < D
351
+ cache_off = i_n * D + o_d if IS_VARLEN else i_b * D + o_d
352
+
353
+ for t in range(t_start, t_end):
354
+ global_t = bos + t
355
+ offset = global_t * D + o_d
356
+ b_dy = tl.load(dy + offset, mask=m_d)
357
+
358
+ if global_t == eos - 1:
359
+ if HAS_DCACHE:
360
+ b_dy_cache = tl.load(grad_cache_in + cache_off, mask=m_d)
361
+ b_dx = -b_dy + b_dy_cache
362
+ else:
363
+ b_dx = -b_dy
364
+ else:
365
+ next_off = offset + D
366
+ b_dx = -b_dy + tl.load(dy + next_off, mask=m_d)
367
+
368
+ tl.store(dx + offset, b_dx, mask=m_d)
369
+
370
+ if USE_INITIAL_STATE:
371
+ if global_t == bos:
372
+ tl.store(grad_cache_out + cache_off, b_dy, mask=m_d)
373
+
374
+
375
+ @tensor_cache
376
+ def prepare_maxlens(cu_seqlens: torch.LongTensor) -> int:
377
+ return torch.max(cu_seqlens.diff()).item()
378
+
379
+
380
+ def token_shift_fwd(
381
+ x: torch.Tensor,
382
+ cu_seqlens: torch.Tensor | None = None,
383
+ cache: torch.Tensor | None = None,
384
+ output_cache: bool = False,
385
+ chunk_indices: torch.LongTensor | None = None,
386
+ ) -> torch.Tensor:
387
+ B, T, D = x.shape
388
+ y = torch.empty_like(x)
389
+
390
+ if cu_seqlens is not None:
391
+ T = prepare_maxlens(cu_seqlens)
392
+ N = len(cu_seqlens) - 1
393
+ else:
394
+ N = B
395
+
396
+ use_short_kernel = T <= 4096
397
+ if IS_NPU and use_short_kernel and N * T > _NPU_MAX_TRITON_GRID:
398
+ use_short_kernel = False
399
+
400
+ if output_cache:
401
+ cache_out = torch.empty((N, D), device=x.device, dtype=x.dtype)
402
+ else:
403
+ cache_out = None
404
+
405
+ if use_short_kernel:
406
+ if cu_seqlens is not None:
407
+ N = len(cu_seqlens) - 1
408
+ else:
409
+ N = B
410
+ BD = triton.next_power_of_2(D)
411
+ grid = (N, T)
412
+ IS_DECODE = T == 1 or (B == 1 and T == N)
413
+ token_shift_fwd_kernel_short[grid](
414
+ x=x,
415
+ y=y,
416
+ cu_seqlens=cu_seqlens,
417
+ cache=cache,
418
+ cache_out=cache_out,
419
+ T=T,
420
+ D=D,
421
+ BD=BD,
422
+ STORE_FINAL_STATE=output_cache,
423
+ IS_DECODE=IS_DECODE,
424
+ )
425
+ else:
426
+ BT = min(64, triton.next_power_of_2(triton.cdiv(max(16, B*T), get_multiprocessor_count(x.device.index))))
427
+ if chunk_indices is None and cu_seqlens is not None:
428
+ chunk_indices = prepare_chunk_indices(cu_seqlens, BT)
429
+ NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
430
+
431
+ BD = triton.next_power_of_2(D)
432
+ ND = triton.cdiv(D, BD)
433
+ NB = triton.cdiv(B*T, 1024)
434
+
435
+ def grid(meta): return (ND * NT, 1 if cu_seqlens is not None else N)
436
+ token_shift_fwd_kernel_long[grid](
437
+ x,
438
+ y,
439
+ cu_seqlens,
440
+ chunk_indices,
441
+ cache,
442
+ cache_out,
443
+ T,
444
+ D=D,
445
+ BD=BD,
446
+ BT=BT,
447
+ NB=NB,
448
+ ND=ND,
449
+ STORE_FINAL_STATE=output_cache,
450
+ )
451
+
452
+ return y, N, T, use_short_kernel, cache_out
453
+
454
+
455
+ def token_shift_bwd(
456
+ dy: torch.Tensor,
457
+ N: int,
458
+ T: int,
459
+ dcache: torch.Tensor | None = None,
460
+ cu_seqlens: torch.Tensor | None = None,
461
+ use_short_kernel: bool = True,
462
+ has_init_cache: bool = False,
463
+ chunk_indices: torch.LongTensor | None = None,
464
+ ) -> torch.Tensor:
465
+ D = dy.shape[2]
466
+ BD = triton.next_power_of_2(D)
467
+ dx = torch.empty_like(dy)
468
+ if has_init_cache:
469
+ grad_cache_out = torch.empty((N, D), device=dy.device, dtype=dy.dtype)
470
+ else:
471
+ grad_cache_out = None
472
+ if use_short_kernel:
473
+ grid = (N, T)
474
+ token_shift_bwd_kernel_short[grid](
475
+ dy=dy,
476
+ dx=dx,
477
+ cu_seqlens=cu_seqlens,
478
+ grad_cache_in=dcache,
479
+ grad_cache_out=grad_cache_out,
480
+ T=T,
481
+ D=D,
482
+ BD=BD,
483
+ )
484
+ else:
485
+ BT = min(64, triton.next_power_of_2(triton.cdiv(max(16, dy.numel() // D),
486
+ get_multiprocessor_count(dy.device.index))))
487
+ if chunk_indices is None and cu_seqlens is not None:
488
+ chunk_indices = prepare_chunk_indices(cu_seqlens, BT)
489
+ NT = len(chunk_indices) if cu_seqlens is not None else triton.cdiv(T, BT)
490
+ NB = triton.cdiv(N * dy.shape[1], 1024)
491
+ BD = triton.next_power_of_2(D)
492
+ ND = triton.cdiv(D, BD)
493
+
494
+ def grid(meta): return (ND * NT, 1 if cu_seqlens is not None else N)
495
+ token_shift_bwd_kernel_long[grid](
496
+ dx,
497
+ dy,
498
+ cu_seqlens,
499
+ chunk_indices,
500
+ dcache,
501
+ grad_cache_out,
502
+ T,
503
+ D=D,
504
+ BD=BD,
505
+ BT=BT,
506
+ NB=NB,
507
+ ND=ND,
508
+ )
509
+ return dx, grad_cache_out
510
+
511
+
512
+ class TokenShift(torch.autograd.Function):
513
+
514
+ @staticmethod
515
+ @input_guard
516
+ def forward(ctx, x: torch.Tensor, cu_seqlens: torch.Tensor | None = None,
517
+ cache: torch.Tensor | None = None, output_cache: bool = False,
518
+ chunk_indices: torch.LongTensor | None = None):
519
+ output, N, T, use_short_kernel, cache_out = token_shift_fwd(x, cu_seqlens, cache, output_cache, chunk_indices)
520
+ ctx.cu_seqlens = cu_seqlens
521
+ ctx.chunk_indices = chunk_indices
522
+ ctx.N = N
523
+ ctx.T = T
524
+ ctx.use_short_kernel = use_short_kernel
525
+ ctx.has_cache = cache is not None
526
+ return output, cache_out
527
+
528
+ @staticmethod
529
+ @input_guard
530
+ def backward(ctx, dy: torch.Tensor, dcache: torch.Tensor | None = None):
531
+ dx, grad_cache = token_shift_bwd(dy, ctx.N, ctx.T, dcache, ctx.cu_seqlens,
532
+ ctx.use_short_kernel, ctx.has_cache, ctx.chunk_indices)
533
+ return dx, None, grad_cache, None, None
534
+
535
+
536
+ @torch.compiler.disable
537
+ def token_shift(
538
+ x: torch.Tensor,
539
+ cu_seqlens: torch.LongTensor | None = None,
540
+ cache: torch.Tensor | None = None,
541
+ output_cache: bool = False,
542
+ chunk_indices: torch.LongTensor | None = None,
543
+ ):
544
+ """
545
+ Token-shift operation implemented with Triton kernels.
546
+
547
+ Args:
548
+ x: Input tensor of shape [B, T, D] (or [1, T, D] when `cu_seqlens` is supplied).
549
+ cu_seqlens: Optional cumulative sequence lengths of shape [B + 1].
550
+ When supplied, `x.shape[0]` must be 1 and `x.dim()` must be 3.
551
+ cache: Optional cache tensor of shape [N, D] that holds the last token
552
+ from the previous call.
553
+ output_cache: Whether to return the updated cache alongside the output.
554
+ In previous versions this parameter did not exist and the
555
+ cache was always dropped; to preserve backward compatibility
556
+ the default is False.
557
+
558
+ Returns:
559
+ output: Tensor of shape [B, T, D] after applying the token-shift.
560
+
561
+ cache_out: Tensor of shape [B, 1, D] containing the last token that
562
+ should be fed as `cache` in the next call. Only returned
563
+ when `output_cache=True`.
564
+ """
565
+ if cu_seqlens is not None:
566
+ assert x.dim() == 3, "Input must be [B, T, D]"
567
+ assert x.shape[0] == 1, "Batch size must be 1 when using cu_seqlens"
568
+
569
+ output, cache_out = TokenShift.apply(x, cu_seqlens, cache, output_cache, chunk_indices)
570
+ if output_cache:
571
+ return output, cache_out
572
+ else:
573
+ return output
build/torch-cuda/modules/token_shift_cp.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ """
9
+ Context Parallel support for Token Shift.
10
+
11
+ Token shift has a 1-token dependency on previous tokens:
12
+ y[t] = x[t-1] - x[t] (for t > 0)
13
+ y[0] = cache - x[0] (cache is the last token from previous rank)
14
+
15
+ In CP mode, non-first ranks need the last token from the previous rank as cache.
16
+ Backward: non-last ranks need to send the last token's gradient to previous rank.
17
+ """
18
+
19
+ import torch
20
+ import torch.distributed as dist
21
+
22
+ from ..modules.token_shift import token_shift_bwd, token_shift_fwd
23
+ from ..ops.cp import FLACPContext, conv_cp_send_recv_bwd, conv_cp_send_recv_fwd
24
+
25
+
26
+ class TokenShiftCPFunction(torch.autograd.Function):
27
+ """
28
+ Context Parallel version of TokenShift.
29
+
30
+ Forward:
31
+ 1. Get last token from previous rank to construct cache
32
+ 2. Call token_shift_fwd with cache
33
+
34
+ Backward:
35
+ 1. Call token_shift_bwd to get dx
36
+ 2. Sync communication: add next rank's first token gradient to current rank's last token
37
+ """
38
+
39
+ @staticmethod
40
+ def _prepare_cache_for_cp(
41
+ x: torch.Tensor,
42
+ cu_seqlens: torch.Tensor | None,
43
+ context: FLACPContext,
44
+ group: dist.ProcessGroup | None,
45
+ ) -> tuple[torch.Tensor | None, int]:
46
+ """Prepare cache for CP forward pass by communicating with previous rank.
47
+
48
+ Args:
49
+ x: Input tensor of shape [1, T, D]
50
+ cu_seqlens: Cumulative sequence lengths
51
+ context: CP context
52
+ group: Process group for communication
53
+
54
+ Returns:
55
+ cache: Cache tensor of shape [N, D] or None
56
+ pre_num_tokens: Number of tokens from previous rank for the first sequence
57
+ """
58
+ if group is None:
59
+ return None, 0
60
+
61
+ D = x.shape[-1]
62
+ cache = None
63
+ pre_num_tokens = 0
64
+
65
+ if not context.is_first_rank:
66
+ # Non-first rank: need cache from previous rank
67
+ assert x.dim() == 3 and x.shape[0] == 1, f"CP requires [1, T, D], got {x.shape}"
68
+ x_2d = x.squeeze(0) # [T, D]
69
+ last_token = x_2d[-1:].contiguous() # [1, D]
70
+ prev_last_token = conv_cp_send_recv_fwd(last_token, group) # [1, D]
71
+
72
+ # For varlen: only the first sequence needs cache from prev rank
73
+ N = len(cu_seqlens) - 1 if cu_seqlens is not None else 1
74
+ cache = torch.zeros(N, D, device=x.device, dtype=x.dtype)
75
+
76
+ # pre_num_conv_tokens tells us how many tokens from prev rank
77
+ # belong to the first sequence on this rank
78
+ pre_num_tokens = getattr(context, 'pre_num_conv_tokens', 0)
79
+ if pre_num_tokens > 0:
80
+ # The prev rank's last token is used as cache for first sequence
81
+ cache[0] = prev_last_token[0]
82
+ else:
83
+ # First rank: participate in send but don't use received data
84
+ x_2d = x.squeeze(0)
85
+ last_token = x_2d[-1:].contiguous()
86
+ _ = conv_cp_send_recv_fwd(last_token, group)
87
+
88
+ return cache, pre_num_tokens
89
+
90
+ @staticmethod
91
+ def _correct_dx_for_cp(
92
+ dx: torch.Tensor,
93
+ grad_cache: torch.Tensor | None,
94
+ group: dist.ProcessGroup | None,
95
+ is_first_rank: bool,
96
+ pre_num_tokens: int = 0,
97
+ ) -> None:
98
+ """Correct dx gradients for CP backward pass.
99
+
100
+ Args:
101
+ dx: Gradient tensor to be corrected, shape [1, T, D]
102
+ grad_cache: Gradient w.r.t. cache, shape [N, D] or None
103
+ group: Process group
104
+ is_first_rank: Whether this is the first rank
105
+ pre_num_tokens: Number of tokens from previous rank for first sequence
106
+ """
107
+ if group is None:
108
+ return
109
+
110
+ D = dx.shape[-1]
111
+
112
+ # Prepare gradient to send to previous rank
113
+ if grad_cache is not None and pre_num_tokens > 0:
114
+ # Only first sequence's cache gradient is relevant
115
+ d_cache = grad_cache[0:1] # [1, D]
116
+ else:
117
+ d_cache = torch.zeros(1, D, device=dx.device, dtype=dx.dtype)
118
+
119
+ # Send to previous rank, receive from next rank
120
+ recv_grad = conv_cp_send_recv_bwd(d_cache, group) # [1, D]
121
+
122
+ # Add received gradient to current rank's last token
123
+ dx[0, -1, :].add_(recv_grad[0])
124
+
125
+ @staticmethod
126
+ def forward(
127
+ ctx,
128
+ x: torch.Tensor,
129
+ cu_seqlens: torch.Tensor | None,
130
+ chunk_indices: torch.Tensor | None,
131
+ cp_context: FLACPContext | None,
132
+ ):
133
+ if cp_context is None:
134
+ raise ValueError("cp_context must be provided for TokenShiftCPFunction")
135
+
136
+ cu_seqlens = cp_context.cu_seqlens
137
+ group = cp_context.group
138
+
139
+ # Prepare cache for CP
140
+ cache, pre_num_tokens = TokenShiftCPFunction._prepare_cache_for_cp(
141
+ x=x,
142
+ cu_seqlens=cu_seqlens,
143
+ context=cp_context,
144
+ group=group,
145
+ )
146
+
147
+ # Save for backward
148
+ ctx.cu_seqlens = cu_seqlens
149
+ ctx.chunk_indices = chunk_indices
150
+ ctx.group = group
151
+ ctx.has_cache = cache is not None
152
+ ctx.is_first_rank = cp_context.is_first_rank
153
+ ctx.pre_num_tokens = pre_num_tokens
154
+
155
+ # Call original forward
156
+ y, N, T, use_short_kernel, cache_out = token_shift_fwd(
157
+ x=x,
158
+ cu_seqlens=cu_seqlens,
159
+ cache=cache,
160
+ output_cache=True,
161
+ chunk_indices=chunk_indices,
162
+ )
163
+
164
+ ctx.N = N
165
+ ctx.T = T
166
+ ctx.use_short_kernel = use_short_kernel
167
+
168
+ return y
169
+
170
+ @staticmethod
171
+ def backward(ctx, dy: torch.Tensor):
172
+ group = ctx.group
173
+
174
+ # Prepare dcache for backward
175
+ # For CP: non-last rank needs to receive gradient from next rank
176
+ # This is handled in _correct_dx_for_cp after computing dx
177
+ dcache = None # Will be computed by token_shift_bwd
178
+
179
+ # Call original backward
180
+ dx, grad_cache = token_shift_bwd(
181
+ dy=dy,
182
+ N=ctx.N,
183
+ T=ctx.T,
184
+ dcache=dcache,
185
+ cu_seqlens=ctx.cu_seqlens,
186
+ use_short_kernel=ctx.use_short_kernel,
187
+ has_init_cache=ctx.has_cache,
188
+ chunk_indices=ctx.chunk_indices,
189
+ )
190
+
191
+ # Correct dx gradients for CP
192
+ TokenShiftCPFunction._correct_dx_for_cp(
193
+ dx=dx,
194
+ grad_cache=grad_cache,
195
+ group=group,
196
+ is_first_rank=ctx.is_first_rank,
197
+ pre_num_tokens=ctx.pre_num_tokens,
198
+ )
199
+
200
+ return dx, None, None, None
201
+
202
+
203
+ @torch.compiler.disable
204
+ def token_shift_cp(
205
+ x: torch.Tensor,
206
+ cp_context: FLACPContext,
207
+ cu_seqlens: torch.Tensor | None = None,
208
+ chunk_indices: torch.Tensor | None = None,
209
+ ):
210
+ """
211
+ Context Parallel version of token_shift.
212
+
213
+ Args:
214
+ x: Input tensor of shape [1, T, D]
215
+ cp_context: CP context (required for CP mode)
216
+ cu_seqlens: Cumulative sequence lengths
217
+ chunk_indices: Chunk indices for variable-length sequences
218
+
219
+ Returns:
220
+ output: Tensor of shape [1, T, D] after applying token-shift
221
+ """
222
+ if cp_context is None:
223
+ raise ValueError("cp_context must be provided for token_shift_cp")
224
+
225
+ assert cp_context.cu_seqlens is not None, "cu_seqlens must be provided for token_shift_cp"
226
+
227
+ return TokenShiftCPFunction.apply(
228
+ x, cu_seqlens, chunk_indices, cp_context
229
+ )
build/torch-cuda/ops/__init__.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .abc import chunk_abc
9
+ from .attn import parallel_attn
10
+ from .attnres import fused_attnres
11
+ from .based import fused_chunk_based, parallel_based
12
+ from .comba import chunk_comba, fused_recurrent_comba
13
+ from .delta_rule import chunk_delta_rule, fused_chunk_delta_rule, fused_recurrent_delta_rule
14
+ from .forgetting_attn import parallel_forgetting_attn
15
+ from .gated_delta_rule import chunk_gated_delta_rule, chunk_gdn, fused_recurrent_gated_delta_rule, fused_recurrent_gdn
16
+ from .generalized_delta_rule import (
17
+ chunk_dplr_delta_rule,
18
+ chunk_iplr_delta_rule,
19
+ fused_recurrent_dplr_delta_rule,
20
+ fused_recurrent_iplr_delta_rule,
21
+ )
22
+ from .gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
23
+ from .gsa import chunk_gsa, fused_recurrent_gsa
24
+ from .hgrn import fused_recurrent_hgrn
25
+ from .kda import chunk_kda, fused_recurrent_kda
26
+ from .lightning_attn import chunk_lightning_attn, fused_recurrent_lightning_attn
27
+ from .linear_attn import chunk_linear_attn, fused_chunk_linear_attn, fused_recurrent_linear_attn
28
+ from .log_linear_attn import chunk_log_linear_attn
29
+ from .mesa_net import chunk_mesa_net
30
+ from .nsa import parallel_nsa
31
+ from .parallax import parallel_parallax
32
+ from .path_attn import parallel_path_attn
33
+ from .retention import chunk_retention, fused_chunk_retention, fused_recurrent_retention, parallel_retention
34
+ from .rwkv6 import chunk_rwkv6, fused_recurrent_rwkv6
35
+ from .rwkv7 import chunk_rwkv7, fused_recurrent_rwkv7
36
+ from .simple_gla import chunk_simple_gla, fused_chunk_simple_gla, fused_recurrent_simple_gla, parallel_simple_gla
37
+ from .wall_attn import parallel_wall_attn, parallel_wall_attn_decode
38
+
39
+ __all__ = [
40
+ 'chunk_abc',
41
+ 'chunk_comba',
42
+ 'chunk_delta_rule',
43
+ 'chunk_dplr_delta_rule',
44
+ 'chunk_gated_delta_rule',
45
+ 'chunk_gdn',
46
+ 'chunk_gla',
47
+ 'chunk_gsa',
48
+ 'chunk_iplr_delta_rule',
49
+ 'chunk_kda',
50
+ 'chunk_lightning_attn',
51
+ 'chunk_linear_attn',
52
+ 'chunk_log_linear_attn',
53
+ 'chunk_mesa_net',
54
+ 'chunk_retention',
55
+ 'chunk_rwkv6',
56
+ 'chunk_rwkv7',
57
+ 'chunk_simple_gla',
58
+ 'fused_attnres',
59
+ 'fused_chunk_based',
60
+ 'fused_chunk_delta_rule',
61
+ 'fused_chunk_gla',
62
+ 'fused_chunk_linear_attn',
63
+ 'fused_chunk_retention',
64
+ 'fused_chunk_simple_gla',
65
+ 'fused_recurrent_comba',
66
+ 'fused_recurrent_delta_rule',
67
+ 'fused_recurrent_dplr_delta_rule',
68
+ 'fused_recurrent_gated_delta_rule',
69
+ 'fused_recurrent_gdn',
70
+ 'fused_recurrent_gla',
71
+ 'fused_recurrent_gsa',
72
+ 'fused_recurrent_hgrn',
73
+ 'fused_recurrent_iplr_delta_rule',
74
+ 'fused_recurrent_kda',
75
+ 'fused_recurrent_lightning_attn',
76
+ 'fused_recurrent_linear_attn',
77
+ 'fused_recurrent_retention',
78
+ 'fused_recurrent_rwkv6',
79
+ 'fused_recurrent_rwkv7',
80
+ 'fused_recurrent_simple_gla',
81
+ 'parallel_attn',
82
+ 'parallel_based',
83
+ 'parallel_forgetting_attn',
84
+ 'parallel_nsa',
85
+ 'parallel_parallax',
86
+ 'parallel_path_attn',
87
+ 'parallel_retention',
88
+ 'parallel_simple_gla',
89
+ 'parallel_wall_attn',
90
+ 'parallel_wall_attn_decode',
91
+ ]
build/torch-cuda/ops/abc/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .chunk import chunk_abc
9
+
10
+ __all__ = [
11
+ 'chunk_abc',
12
+ ]
build/torch-cuda/ops/abc/chunk.py ADDED
@@ -0,0 +1,1119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import triton.language as tl
11
+
12
+ from ...ops.utils import softmax_bwd, softmax_fwd
13
+ from ...ops.utils.logcumsumexp import logcumsumexp_fwd_kernel
14
+ from ...ops.utils.op import exp
15
+ from ...utils import input_guard
16
+
17
+
18
+ @triton.jit(do_not_specialize=['T'])
19
+ def chunk_abc_fwd_kernel_h(
20
+ k,
21
+ v,
22
+ z,
23
+ h,
24
+ h0,
25
+ ht,
26
+ T,
27
+ K: tl.constexpr,
28
+ V: tl.constexpr,
29
+ BT: tl.constexpr,
30
+ BK: tl.constexpr,
31
+ BV: tl.constexpr,
32
+ NT: tl.constexpr,
33
+ NORMK: tl.constexpr,
34
+ USE_INITIAL_STATE: tl.constexpr,
35
+ STORE_FINAL_STATE: tl.constexpr,
36
+ ):
37
+ i_v, i_k, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
38
+
39
+ b_h = tl.zeros([BK, BV], dtype=tl.float32)
40
+ if USE_INITIAL_STATE:
41
+ p_h = tl.make_block_ptr(h0 + i_bh * K * V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
42
+ b_h += tl.load(p_h, boundary_check=(0, 1)).to(tl.float32)
43
+ if NORMK:
44
+ p_z0 = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), (i_k * BK,), (BK,), (0,))
45
+ else:
46
+ p_z0 = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), (i_v * BV,), (BV,), (0,))
47
+ b_zp = tl.load(p_z0).to(tl.float32)
48
+ for i_t in range(NT):
49
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (K, T), (1, K), (i_k * BK, i_t * BT), (BK, BT), (0, 1))
50
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
51
+ p_h = tl.make_block_ptr(h + i_bh * NT*K*V + i_t * K * V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
52
+
53
+ tl.store(p_h, b_h.to(p_h.dtype.element_ty), boundary_check=(0, 1))
54
+ # [BK, BT]
55
+ b_k = tl.load(p_k, boundary_check=(0, 1))
56
+ # [BT, BV]
57
+ b_v = tl.load(p_v, boundary_check=(0, 1))
58
+ if NORMK:
59
+ p_zc = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), ((i_t * BT + BT - 1) * K + i_k * BK,), (BK,), (0,))
60
+ # [BK,]
61
+ b_zc = tl.load(p_zc, boundary_check=(0,))
62
+ b_r, b_zp = exp(b_zp - b_zc), b_zc
63
+ # [BK, BV]
64
+ b_h = b_h * b_r[:, None]
65
+ b_k = exp(b_k - b_zc[:, None]).to(b_k.dtype)
66
+ else:
67
+ p_zc = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), ((i_t * BT + BT - 1) * V + i_v * BV,), (BV,), (0,))
68
+ # [BV,]
69
+ b_zc = tl.load(p_zc, boundary_check=(0,))
70
+ b_r, b_zp = exp(b_zp - b_zc), b_zc
71
+ # [BK, BV]
72
+ b_h = b_h * b_r[None, :]
73
+ b_v = exp(b_v - b_zc[None, :]).to(b_v.dtype)
74
+ # [BK, BV]
75
+ b_h += tl.dot(b_k, b_v, allow_tf32=False)
76
+
77
+ if STORE_FINAL_STATE:
78
+ p_h = tl.make_block_ptr(ht + i_bh * K * V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
79
+ tl.store(p_h, b_h.to(p_h.dtype.element_ty), boundary_check=(0, 1))
80
+
81
+
82
+ @triton.jit(do_not_specialize=['T'])
83
+ def chunk_abc_fwd_kernel_intra_K(
84
+ v,
85
+ z,
86
+ o,
87
+ A,
88
+ T,
89
+ V: tl.constexpr,
90
+ BT: tl.constexpr,
91
+ BC: tl.constexpr,
92
+ BV: tl.constexpr,
93
+ NC: tl.constexpr,
94
+ ):
95
+ i_v, i_c, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
96
+ i_t, i_i = i_c // NC, i_c % NC
97
+
98
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
99
+ p_zn = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), ((i_t * BT + i_i * BC) * V + i_v * BV,), (BV,), (0,))
100
+ # [BV,]
101
+ b_zn = tl.load(p_zn, boundary_check=(0,))
102
+ # [BC, BV]
103
+ b_o = tl.zeros([BC, BV], dtype=tl.float32)
104
+ for i_j in range(0, i_i):
105
+ p_A = tl.make_block_ptr(A + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT + i_i * BC, i_j * BC), (BC, BC), (1, 0))
106
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_j * BC, i_v * BV), (BC, BV), (1, 0))
107
+ # [BC, BV]
108
+ b_v = tl.load(p_v, boundary_check=(0, 1))
109
+ # [BC, BC]
110
+ b_A = tl.load(p_A, boundary_check=(0, 1))
111
+ b_o += tl.dot(b_A, exp(b_v - b_zn[None, :]).to(b_v.dtype), allow_tf32=False)
112
+ b_z = tl.load(p_z, boundary_check=(0, 1))
113
+ b_o *= exp(b_zn[None, :] - b_z)
114
+
115
+ o_i = tl.arange(0, BC)
116
+ o_A = i_bh * T * BT + (i_t * BT + i_i * BC + tl.arange(0, BC)) * BT + i_i * BC
117
+ m_A = (i_t * BT + i_i * BC + tl.arange(0, BC)) < T
118
+ for j in range(0, BC):
119
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T * V,), (1,), ((i_t * BT + i_i * BC + j) * V + i_v * BV,), (BV,), (0,))
120
+ # [BC,]
121
+ b_A = tl.load(A + o_A + j, mask=m_A, other=0)
122
+ # [BV,]
123
+ b_v = tl.load(p_v, boundary_check=(0,)).to(tl.float32)
124
+ # [BC, BV]
125
+ # avoid 0 * inf = inf
126
+ m_i = o_i[:, None] >= j
127
+ b_o += tl.where(m_i, b_A[:, None] * exp(b_v[None, :] - b_z), 0)
128
+ p_o = tl.make_block_ptr(o + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
129
+ tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
130
+
131
+
132
+ @triton.jit(do_not_specialize=['T'])
133
+ def chunk_abc_fwd_kernel_K(
134
+ q,
135
+ k,
136
+ z,
137
+ h,
138
+ o,
139
+ A,
140
+ scale,
141
+ T,
142
+ K: tl.constexpr,
143
+ V: tl.constexpr,
144
+ BT: tl.constexpr,
145
+ BK: tl.constexpr,
146
+ BV: tl.constexpr,
147
+ NT: tl.constexpr,
148
+ ):
149
+ i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
150
+ i_p = tl.maximum(i_t * BT - 1, 0)
151
+
152
+ o_i = tl.arange(0, BT)
153
+ m_s = o_i[:, None] >= o_i[None, :]
154
+
155
+ b_o = tl.zeros([BT, BV], dtype=tl.float32)
156
+ b_A = tl.zeros([BT, BT], dtype=tl.float32)
157
+ for i_k in range(tl.cdiv(K, BK)):
158
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
159
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (K, T), (1, K), (i_k * BK, i_t * BT), (BK, BT), (0, 1))
160
+ p_h = tl.make_block_ptr(h + i_bh * NT*K*V + i_t * K * V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
161
+
162
+ # [BT, BK]
163
+ b_q = tl.load(p_q, boundary_check=(0, 1))
164
+ b_q = (b_q * scale).to(b_q.dtype)
165
+ # [BK, BT]
166
+ b_k = tl.load(p_k, boundary_check=(0, 1))
167
+ # [BK, BV]
168
+ b_h = tl.load(p_h, boundary_check=(0, 1))
169
+ # [BT, BV]
170
+ b_o += tl.dot(b_q, b_h, allow_tf32=False)
171
+ # [BT, BT]
172
+ b_A += tl.dot(b_q, b_k, allow_tf32=False)
173
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
174
+ p_o = tl.make_block_ptr(o + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
175
+ # [BT, BV]
176
+ b_z = tl.load(p_z, boundary_check=(0, 1))
177
+ # [BT, BV]
178
+ p_zp = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), (i_p * V + i_v * BV,), (BV,), (0,))
179
+ b_zp = tl.load(p_zp, boundary_check=(0,))
180
+ b_o = b_o * exp(b_zp[None, :] - b_z)
181
+ tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
182
+
183
+ p_A = tl.make_block_ptr(A + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
184
+ # [BT, BT]
185
+ b_A = tl.where(m_s, b_A, 0.)
186
+ if i_v == 0:
187
+ tl.store(p_A, b_A.to(p_A.dtype.element_ty), boundary_check=(0, 1))
188
+
189
+
190
+ @triton.jit(do_not_specialize=['T'])
191
+ def chunk_abc_fwd_kernel_intra_V(
192
+ q,
193
+ k,
194
+ z,
195
+ A,
196
+ scale,
197
+ T,
198
+ K: tl.constexpr,
199
+ BT: tl.constexpr,
200
+ BC: tl.constexpr,
201
+ BK: tl.constexpr,
202
+ NC: tl.constexpr,
203
+ ):
204
+ i_k, i_c, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
205
+ i_t, i_i, i_j = i_c // (NC * NC), (i_c % (NC * NC)) // NC, (i_c % (NC * NC)) % NC
206
+ n_bh = tl.num_programs(2)
207
+
208
+ if i_i > i_j:
209
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
210
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (K, T), (1, K), (i_k * BK, i_t * BT + i_j * BC), (BK, BC), (0, 1))
211
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
212
+ p_A = tl.make_block_ptr(A + (i_k*n_bh+i_bh)*T*BT, (T, BT), (BT, 1), (i_t * BT + i_i * BC, i_j * BC), (BC, BC), (1, 0))
213
+ p_zn = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), ((i_t * BT + i_i * BC) * K + i_k * BK,), (BK,), (0,))
214
+ # [BK,]
215
+ b_zn = tl.load(p_zn, boundary_check=(0,))
216
+ # [BC, BK]
217
+ b_q = tl.load(p_q, boundary_check=(0, 1))
218
+ b_z = tl.load(p_z, boundary_check=(0, 1))
219
+ b_q = (b_q * exp(b_zn[None, :] - b_z) * scale).to(b_q.dtype)
220
+ # [BK, BC]
221
+ b_k = tl.load(p_k, boundary_check=(0, 1))
222
+ b_k = exp(b_k - b_zn[:, None]).to(b_k.dtype)
223
+ # [BC, BC]
224
+ b_A = tl.dot(b_q, b_k, allow_tf32=False)
225
+ tl.store(p_A, b_A.to(A.dtype.element_ty), boundary_check=(0, 1))
226
+ elif i_i == i_j:
227
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
228
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (T * K,), (1,), ((i_t * BT + i_j * BC) * K + i_k * BK,), (BK,), (0,))
229
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
230
+ # [BC, BK]
231
+ b_q = tl.load(p_q, boundary_check=(0, 1))
232
+ b_z = tl.load(p_z, boundary_check=(0, 1))
233
+
234
+ o_i = tl.arange(0, BC)
235
+ o_A = (i_bh + i_k * n_bh) * T * BT + (i_t * BT + i_i * BC + tl.arange(0, BC)) * BT + i_j * BC
236
+ m_A = (i_t * BT + i_i * BC + tl.arange(0, BC)) < T
237
+ for j in range(0, BC):
238
+ # [BK,]
239
+ b_k = tl.load(p_k, boundary_check=(0,)).to(tl.float32)
240
+ # [BC,]
241
+ b_A = tl.sum(b_q * exp(b_k[None, :] - b_z) * scale, 1)
242
+ b_A = tl.where(o_i >= j, b_A, 0.)
243
+ tl.store(A + o_A + j, b_A.to(b_q.dtype), mask=m_A)
244
+
245
+ p_k = tl.advance(p_k, (K,))
246
+
247
+
248
+ @triton.jit(do_not_specialize=['T'])
249
+ def chunk_abc_fwd_kernel_V(
250
+ q,
251
+ v,
252
+ z,
253
+ h,
254
+ o,
255
+ A,
256
+ scale,
257
+ T,
258
+ K: tl.constexpr,
259
+ V: tl.constexpr,
260
+ BT: tl.constexpr,
261
+ BK: tl.constexpr,
262
+ BV: tl.constexpr,
263
+ NT: tl.constexpr,
264
+ ):
265
+ i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
266
+ i_p = tl.maximum(i_t * BT - 1, 0)
267
+
268
+ b_o = tl.zeros([BT, BV], dtype=tl.float32)
269
+ for i_k in range(tl.cdiv(K, BK)):
270
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
271
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
272
+ p_h = tl.make_block_ptr(h + i_bh * NT*K*V + i_t * K * V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
273
+ p_zp = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), (i_p * K + i_k * BK,), (BK,), (0,))
274
+
275
+ # [BT, BK]
276
+ b_q = tl.load(p_q, boundary_check=(0, 1))
277
+ b_q = (b_q * scale).to(b_q.dtype)
278
+ # [BT, BK]
279
+ b_z = tl.load(p_z, boundary_check=(0, 1))
280
+ # [BT, BK]
281
+ b_zp = tl.load(p_zp, boundary_check=(0,))
282
+ b_q = (b_q * exp(b_zp[None, :] - b_z)).to(b_q.dtype)
283
+ # [BK, BV]
284
+ b_h = tl.load(p_h, boundary_check=(0, 1))
285
+ # works but dkw, owing to divine benevolence
286
+ # [BT, BV]
287
+ if i_k >= 0:
288
+ b_o += tl.dot(b_q, b_h, allow_tf32=False)
289
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
290
+ p_o = tl.make_block_ptr(o + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
291
+ p_A = tl.make_block_ptr(A + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
292
+ # [BT, BV]
293
+ b_v = tl.load(p_v, boundary_check=(0, 1))
294
+ # [BT, BT]
295
+ b_A = tl.load(p_A, boundary_check=(0, 1))
296
+ b_o += tl.dot(b_A.to(b_v.dtype), b_v, allow_tf32=False)
297
+ tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
298
+
299
+
300
+ @triton.jit(do_not_specialize=['T'])
301
+ def chunk_abc_bwd_kernel_dh(
302
+ q,
303
+ z,
304
+ do,
305
+ dh,
306
+ scale,
307
+ T,
308
+ K: tl.constexpr,
309
+ V: tl.constexpr,
310
+ BT: tl.constexpr,
311
+ BK: tl.constexpr,
312
+ BV: tl.constexpr,
313
+ NT: tl.constexpr,
314
+ NORMK: tl.constexpr,
315
+ ):
316
+ i_k, i_v, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
317
+
318
+ b_dh = tl.zeros([BK, BV], dtype=tl.float32)
319
+ b_zp = tl.full([BK if NORMK else BV], float('inf'), dtype=tl.float32)
320
+ for i_t in range(NT - 1, -1, -1):
321
+ i_p = tl.maximum(i_t * BT - 1, 0)
322
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (K, T), (1, K), (i_k * BK, i_t * BT), (BK, BT), (0, 1))
323
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
324
+ p_dh = tl.make_block_ptr(dh + i_bh * NT*K*V + i_t * K*V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
325
+
326
+ # [BK, BT]
327
+ b_q = tl.load(p_q, boundary_check=(0, 1))
328
+ b_q = (b_q * scale).to(b_q.dtype)
329
+ # [BT, BV]
330
+ b_do = tl.load(p_do, boundary_check=(0, 1))
331
+
332
+ tl.store(p_dh, b_dh.to(p_dh.dtype.element_ty), boundary_check=(0, 1))
333
+ if NORMK:
334
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (K, T), (1, K), (i_k * BK, i_t * BT), (BK, BT), (0, 1))
335
+ p_zc = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), (i_p * K + i_k * BK,), (BK,), (0,))
336
+ # [BK,]
337
+ b_zc = tl.load(p_zc, boundary_check=(0,))
338
+ b_r, b_zp = exp(b_zc - b_zp), b_zc
339
+ # [BK, BT]
340
+ b_z = tl.load(p_z, boundary_check=(0, 1))
341
+ b_q = (b_q * exp(b_zc[:, None] - b_z)).to(b_q.dtype)
342
+ # [BK, BV]
343
+ b_dh = b_dh * b_r[:, None]
344
+ else:
345
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
346
+ p_zc = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), (i_p * V + i_v * BV,), (BV,), (0,))
347
+ # [BV,]
348
+ b_zc = tl.load(p_zc, boundary_check=(0,))
349
+ b_r, b_zp = exp(b_zc - b_zp), b_zc
350
+ # [BT, BV]
351
+ b_z = tl.load(p_z, boundary_check=(0,))
352
+ b_do = (b_do * exp(b_zc[None, :] - b_z)).to(b_do.dtype)
353
+ # [BK, BV]
354
+ b_dh = b_dh * b_r[None, :]
355
+ # [BK, BV]
356
+ b_dh += tl.dot(b_q, b_do, allow_tf32=False)
357
+
358
+
359
+ @triton.jit(do_not_specialize=['T'])
360
+ def chunk_abc_bwd_kernel_V(
361
+ k,
362
+ v,
363
+ z,
364
+ h,
365
+ A,
366
+ do,
367
+ dh,
368
+ dq,
369
+ dk,
370
+ dv,
371
+ dA,
372
+ scale,
373
+ T,
374
+ K: tl.constexpr,
375
+ V: tl.constexpr,
376
+ BT: tl.constexpr,
377
+ BK: tl.constexpr,
378
+ BV: tl.constexpr,
379
+ NT: tl.constexpr,
380
+ ):
381
+ i_k, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
382
+ i_p = tl.maximum(i_t * BT - 1, 0)
383
+ n_bh = tl.num_programs(2)
384
+
385
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
386
+ p_zc = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), ((i_t * BT + BT - 1) * K + i_k * BK,), (BK,), (0,))
387
+ p_A = tl.make_block_ptr(A + i_bh * T * BT, (BT, T), (1, BT), (0, i_t * BT), (BT, BT), (0, 1))
388
+
389
+ # [BK,]
390
+ b_zc = tl.load(p_zc, boundary_check=(0,))
391
+ # [BT, BK]
392
+ b_k = tl.load(p_k, boundary_check=(0, 1))
393
+ b_k = exp(b_k - b_zc[None, :]).to(b_k.dtype)
394
+ # [BT, BT]
395
+ b_A = tl.load(p_A, boundary_check=(0, 1))
396
+
397
+ b_dq = tl.zeros([BT, BK], dtype=tl.float32)
398
+ b_dk = tl.zeros([BT, BK], dtype=tl.float32)
399
+ b_dA = tl.zeros([BT, BT], dtype=tl.float32)
400
+ for i_v in range(tl.cdiv(V, BV)):
401
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
402
+ p_h = tl.make_block_ptr(h + i_bh * NT*K*V + i_t * V * K, (V, K), (1, V), (i_v * BV, i_k * BK), (BV, BK), (0, 1))
403
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
404
+ p_dh = tl.make_block_ptr(dh + i_bh * NT*K*V + i_t * K*V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
405
+ p_dv = tl.make_block_ptr(dv + (i_k*n_bh+i_bh) * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
406
+
407
+ # [BT, BV]
408
+ b_v = tl.load(p_v, boundary_check=(0, 1))
409
+ # [BV, BK]
410
+ b_h = tl.load(p_h, boundary_check=(0, 1))
411
+ # [BT, BV]
412
+ b_do = tl.load(p_do, boundary_check=(0, 1))
413
+ # [BK, BV]
414
+ b_dh = tl.load(p_dh, boundary_check=(0, 1))
415
+
416
+ # [BT, BV]
417
+ b_dv = tl.dot(b_k, b_dh, allow_tf32=False)
418
+ if i_k == 0:
419
+ b_dv += tl.dot(b_A.to(b_do.dtype), b_do, allow_tf32=False)
420
+ b_do = (b_do * scale).to(b_do.dtype)
421
+ tl.store(p_dv, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
422
+ # [BT, BT]
423
+ b_dA += tl.dot(b_do, tl.trans(b_v), allow_tf32=False)
424
+ # [BT, BK]
425
+ b_dq += tl.dot(b_do, b_h, allow_tf32=False)
426
+ # [BT, BK]
427
+ b_dk += tl.dot(b_v, tl.trans(b_dh), allow_tf32=False)
428
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
429
+ p_zp = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), (i_p * K + i_k * BK,), (BK,), (0,))
430
+ # [BK,]
431
+ b_zp = tl.load(p_zp, boundary_check=(0,))
432
+ # [BT, BK]
433
+ b_z = tl.load(p_z, boundary_check=(0, 1))
434
+ b_z = exp(b_zp[None, :] - b_z)
435
+ # [BT, BK]
436
+ b_dq = b_dq * b_z
437
+ b_dk = b_dk * b_k
438
+
439
+ p_dq = tl.make_block_ptr(dq + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
440
+ p_dk = tl.make_block_ptr(dk + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
441
+ p_dA = tl.make_block_ptr(dA + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
442
+ tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), boundary_check=(0, 1))
443
+ tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), boundary_check=(0, 1))
444
+
445
+ o_i = tl.arange(0, BT)
446
+ m_s = o_i[:, None] >= o_i[None, :]
447
+ # [BT, BT]
448
+ b_dA = tl.where(m_s, b_dA, 0.).to(b_k.dtype)
449
+ if i_k == 0:
450
+ tl.store(p_dA, b_dA.to(p_dA.dtype.element_ty), boundary_check=(0, 1))
451
+
452
+
453
+ @triton.jit(do_not_specialize=['T'])
454
+ def chunk_abc_bwd_kernel_intra_V(
455
+ q,
456
+ k,
457
+ z,
458
+ dA,
459
+ dq,
460
+ dk,
461
+ T,
462
+ K: tl.constexpr,
463
+ BT: tl.constexpr,
464
+ BC: tl.constexpr,
465
+ BK: tl.constexpr,
466
+ NC: tl.constexpr,
467
+ ):
468
+ i_k, i_c, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
469
+ i_t, i_i = i_c // NC, i_c % NC
470
+
471
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
472
+ p_zn = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), ((i_t * BT + i_i * BC) * K + i_k * BK,), (BK,), (0,))
473
+ # [BK,]
474
+ b_zn = tl.load(p_zn, boundary_check=(0,))
475
+ # [BC, BK]
476
+ b_z = tl.load(p_z, boundary_check=(0, 1))
477
+ b_zq = exp(b_zn[None, :] - b_z)
478
+ b_dq = tl.zeros([BC, BK], dtype=tl.float32)
479
+ for i_j in range(0, i_i):
480
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_j * BC, i_k * BK), (BC, BK), (1, 0))
481
+ p_dA = tl.make_block_ptr(dA + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT + i_i * BC, i_j * BC), (BC, BC), (1, 0))
482
+ # [BC, BK]
483
+ b_k = tl.load(p_k, boundary_check=(0, 1))
484
+ b_kz = exp(b_k - b_zn[None, :]).to(b_k.dtype)
485
+ # [BC, BC]
486
+ b_dA = tl.load(p_dA, boundary_check=(0, 1))
487
+ # [BC, BK]
488
+ b_dq += tl.dot(b_dA, b_kz, allow_tf32=False)
489
+ b_dq *= b_zq
490
+
491
+ o_i = tl.arange(0, BC)
492
+ o_dA = i_bh * T * BT + (i_t * BT + i_i * BC + tl.arange(0, BC)) * BT + i_i * BC
493
+ m_dA = (i_t * BT + i_i * BC + tl.arange(0, BC)) < T
494
+ for j in range(0, BC):
495
+ p_kj = tl.make_block_ptr(k + i_bh * T*K, (T * K,), (1,), ((i_t * BT + i_i*BC+j) * K + i_k * BK,), (BK,), (0,))
496
+ # [BC,]
497
+ b_dA = tl.load(dA + o_dA + j, mask=m_dA, other=0)
498
+ # [BK,]
499
+ b_kj = tl.load(p_kj, boundary_check=(0,)).to(tl.float32)
500
+ # [BC, BK]
501
+ m_i = o_i[:, None] >= j
502
+ # [BC, BK]
503
+ b_dq += tl.where(m_i, b_dA[:, None] * exp(b_kj[None, :] - b_z), 0.)
504
+ p_dq = tl.make_block_ptr(dq + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
505
+ tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), boundary_check=(0, 1))
506
+
507
+ tl.debug_barrier()
508
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
509
+ p_zn = tl.make_block_ptr(z + i_bh * T*K, (T*K,), (1,), ((i_t * BT + i_i * BC + BC - 1) * K + i_k * BK,), (BK,), (0,))
510
+ # [BK,]
511
+ b_zn = tl.load(p_zn, boundary_check=(0,))
512
+ # [BC, BK]
513
+ b_k = tl.load(p_k, boundary_check=(0, 1))
514
+ b_kz = exp(b_k - b_zn[None, :])
515
+ b_dk = tl.zeros([BC, BK], dtype=tl.float32)
516
+ for i_j in range(i_i + 1, NC):
517
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_j * BC, i_k * BK), (BC, BK), (1, 0))
518
+ p_z = tl.make_block_ptr(z + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_j * BC, i_k * BK), (BC, BK), (1, 0))
519
+ p_dA = tl.make_block_ptr(dA + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT + i_j * BC, i_i * BC), (BC, BC), (1, 0))
520
+ # [BC, BK]
521
+ b_q = tl.load(p_q, boundary_check=(0, 1))
522
+ b_z = tl.load(p_z, boundary_check=(0, 1))
523
+ b_qz = (b_q * exp(b_zn[None, :] - b_z)).to(b_q.dtype)
524
+ # [BC, BC]
525
+ b_dA = tl.load(p_dA, boundary_check=(0, 1))
526
+ # [BC, BK]
527
+ b_dk += tl.dot(tl.trans(b_dA), b_qz, allow_tf32=False)
528
+ b_dk *= b_kz
529
+
530
+ o_dA = i_bh * T * BT + (i_t * BT + i_i * BC) * BT + i_i * BC + tl.arange(0, BC)
531
+ for j in range(0, BC):
532
+ p_qj = tl.make_block_ptr(q + i_bh * T*K, (T * K,), (1,), ((i_t * BT + i_i * BC + j) * K + i_k * BK,), (BK,), (0,))
533
+ p_zj = tl.make_block_ptr(z + i_bh * T*K, (T * K,), (1,), ((i_t * BT + i_i * BC + j) * K + i_k * BK,), (BK,), (0,))
534
+ # [BC,]
535
+ b_dA = tl.load(dA + o_dA + j * BT, mask=(i_t * BT + i_i * BC + j < T), other=0)
536
+ # [BK,]
537
+ b_qj = tl.load(p_qj, boundary_check=(0,)).to(tl.float32)
538
+ b_zj = tl.load(p_zj, boundary_check=(0,)).to(tl.float32)
539
+ # [BC, BK]
540
+ m_i = o_i[:, None] <= j
541
+ b_dk += tl.where(m_i, b_dA[:, None] * b_qj[None, :] * exp(b_k - b_zj[None, :]), 0.)
542
+ p_dk = tl.make_block_ptr(dk + i_bh * T*K, (T, K), (K, 1), (i_t * BT + i_i * BC, i_k * BK), (BC, BK), (1, 0))
543
+ tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), boundary_check=(0, 1))
544
+
545
+
546
+ @triton.jit(do_not_specialize=['T'])
547
+ def chunk_abc_bwd_kernel_intra_K(
548
+ v,
549
+ z,
550
+ do,
551
+ dA,
552
+ scale,
553
+ T,
554
+ V: tl.constexpr,
555
+ BT: tl.constexpr,
556
+ BC: tl.constexpr,
557
+ BV: tl.constexpr,
558
+ NC: tl.constexpr,
559
+ ):
560
+ i_v, i_c, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
561
+ i_t, i_i, i_j = i_c // (NC * NC), (i_c % (NC * NC)) // NC, (i_c % (NC * NC)) % NC
562
+ n_bh = tl.num_programs(2)
563
+
564
+ if i_i > i_j:
565
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (V, T), (1, V), (i_v * BV, i_t * BT + i_j * BC), (BV, BC), (0, 1))
566
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
567
+ p_zn = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), ((i_t * BT + i_i * BC) * V + i_v * BV,), (BV,), (0,))
568
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
569
+ p_dA = tl.make_block_ptr(dA+(i_bh+i_v*n_bh)*T*BT, (T, BT), (BT, 1), (i_t * BT + i_i * BC, i_j * BC), (BC, BC), (1, 0))
570
+ # [BV,]
571
+ b_zn = tl.load(p_zn, boundary_check=(0,))
572
+ # [BC, BV]
573
+ b_z = tl.load(p_z, boundary_check=(0, 1))
574
+ b_do = tl.load(p_do, boundary_check=(0, 1))
575
+ b_do = (b_do * exp(b_zn[None, :] - b_z) * scale).to(b_do.dtype)
576
+ # [BV, BC]
577
+ b_v = tl.load(p_v, boundary_check=(0, 1))
578
+ b_v = exp(b_v - b_zn[:, None]).to(b_v.dtype)
579
+ # [BC, BC]
580
+ b_dA = tl.dot(b_do, b_v, allow_tf32=False)
581
+ tl.store(p_dA, b_dA.to(dA.dtype.element_ty), boundary_check=(0, 1))
582
+ elif i_i == i_j:
583
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T * V,), (1,), ((i_t * BT + i_j * BC) * V + i_v * BV,), (BV,), (0,))
584
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
585
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
586
+ # [BC, BV]
587
+ b_z = tl.load(p_z, boundary_check=(0, 1))
588
+ b_do = tl.load(p_do, boundary_check=(0, 1)) * scale
589
+
590
+ o_i = tl.arange(0, BC)
591
+ o_A = (i_bh + i_v * n_bh) * T * BT + (i_t * BT + i_i * BC + tl.arange(0, BC)) * BT + i_j * BC
592
+ m_A = (i_t * BT + i_i * BC + tl.arange(0, BC)) < T
593
+ for j in range(0, BC):
594
+ # [BV,]
595
+ b_v = tl.load(p_v, boundary_check=(0,)).to(tl.float32)
596
+ # [BC,]
597
+ b_dA = tl.sum(b_do * exp(b_v[None, :] - b_z), 1)
598
+ b_dA = tl.where(o_i >= j, b_dA, 0)
599
+ tl.store(dA + o_A + j, b_dA.to(b_do.dtype), mask=m_A)
600
+
601
+ p_v = tl.advance(p_v, (V,))
602
+
603
+
604
+ @triton.jit(do_not_specialize=['T'])
605
+ def chunk_abc_bwd_kernel_K(
606
+ q,
607
+ k,
608
+ v,
609
+ z,
610
+ h,
611
+ A,
612
+ do,
613
+ dh,
614
+ dq,
615
+ dk,
616
+ dv,
617
+ dA,
618
+ scale,
619
+ T,
620
+ K: tl.constexpr,
621
+ V: tl.constexpr,
622
+ BT: tl.constexpr,
623
+ BK: tl.constexpr,
624
+ BV: tl.constexpr,
625
+ NT: tl.constexpr,
626
+ ):
627
+ i_k, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
628
+ i_p = tl.maximum(i_t * BT - 1, 0)
629
+ n_bh = tl.num_programs(2)
630
+
631
+ o_i = tl.arange(0, BT)
632
+ m_s = o_i[:, None] >= o_i[None, :]
633
+
634
+ p_q = tl.make_block_ptr(q + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
635
+ p_k = tl.make_block_ptr(k + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
636
+ p_A = tl.make_block_ptr(A + (i_k*n_bh+i_bh) * T * BT, (T, BT), (BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
637
+
638
+ # [BT, BK]
639
+ b_q = tl.load(p_q, boundary_check=(0, 1))
640
+ b_k = tl.load(p_k, boundary_check=(0, 1))
641
+ # [BT, BT]
642
+ b_A = tl.dot((b_q * scale).to(b_q.dtype), tl.trans(b_k), allow_tf32=False)
643
+ b_A = tl.where(m_s, b_A, 0.)
644
+ tl.store(p_A, b_A.to(p_A.dtype.element_ty), boundary_check=(0, 1))
645
+
646
+ b_dq = tl.zeros([BT, BK], dtype=tl.float32)
647
+ b_dk = tl.zeros([BT, BK], dtype=tl.float32)
648
+ for i_v in range(tl.cdiv(V, BV)):
649
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
650
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
651
+ p_zp = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), (i_p * V + i_v * BV,), (BV,), (0,))
652
+ p_zc = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), ((i_t * BT + BT - 1) * V + i_v * BV,), (BV,), (0,))
653
+ p_h = tl.make_block_ptr(h + i_bh * NT*K*V + i_t * K*V, (V, K), (1, V), (i_v * BV, i_k * BK), (BV, BK), (0, 1))
654
+
655
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
656
+ p_dh = tl.make_block_ptr(dh + i_bh * NT*K*V + i_t * K*V, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0))
657
+ p_dv = tl.make_block_ptr(dv + (i_k*n_bh+i_bh) * T*V, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
658
+
659
+ # [BV,]
660
+ b_zp = tl.load(p_zp, boundary_check=(0,))
661
+ b_zc = tl.load(p_zc, boundary_check=(0,))
662
+ # [BT, BV]
663
+ b_v = tl.load(p_v, boundary_check=(0, 1))
664
+ b_v = exp(b_v - b_zc[None, :]).to(b_v.dtype)
665
+ b_z = tl.load(p_z, boundary_check=(0, 1))
666
+ b_z = exp(b_zp[None, :] - b_z)
667
+ # [BV, BK]
668
+ b_h = tl.load(p_h, boundary_check=(0, 1))
669
+ # [BT, BV]
670
+ b_do = tl.load(p_do, boundary_check=(0, 1))
671
+ b_do = (b_do * b_z * scale).to(b_do.dtype)
672
+ # [BK, BV]
673
+ b_dh = tl.load(p_dh, boundary_check=(0, 1))
674
+
675
+ # [BT, BK]
676
+ b_dq += tl.dot(b_do, b_h, allow_tf32=False)
677
+ b_dk += tl.dot(b_v, tl.trans(b_dh), allow_tf32=False)
678
+ # [BT, BV]
679
+ b_dv = b_v * tl.dot(b_k, b_dh, allow_tf32=False)
680
+ tl.store(p_dv, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
681
+ p_dA = tl.make_block_ptr(dA + i_bh * T * BT, (T, BT), (BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
682
+ # [BT, BT]
683
+ b_dA = tl.load(p_dA, boundary_check=(0, 1))
684
+ # [BT, BK]
685
+ b_dq += tl.dot(b_dA, b_k, allow_tf32=False)
686
+ b_dk += tl.dot(tl.trans(b_dA).to(b_k.dtype), b_q, allow_tf32=False)
687
+
688
+ p_dq = tl.make_block_ptr(dq + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
689
+ p_dk = tl.make_block_ptr(dk + i_bh * T*K, (T, K), (K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0))
690
+ tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), boundary_check=(0, 1))
691
+ tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), boundary_check=(0, 1))
692
+
693
+
694
+ @triton.jit(do_not_specialize=['T'])
695
+ def chunk_abc_bwd_kernel_intra_KV(
696
+ v,
697
+ z,
698
+ A,
699
+ do,
700
+ dv,
701
+ T,
702
+ V: tl.constexpr,
703
+ BT: tl.constexpr,
704
+ BC: tl.constexpr,
705
+ BV: tl.constexpr,
706
+ NC: tl.constexpr,
707
+ ):
708
+ i_v, i_c, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
709
+ i_t, i_i = i_c // NC, i_c % NC
710
+
711
+ p_v = tl.make_block_ptr(v + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
712
+ p_zn = tl.make_block_ptr(z + i_bh * T*V, (T*V,), (1,), ((i_t * BT + i_i * BC + BC - 1) * V + i_v * BV,), (BV,), (0,))
713
+ # [BV,]
714
+ b_zn = tl.load(p_zn, boundary_check=(0,))
715
+ # [BC, BV]
716
+ b_v = tl.load(p_v, boundary_check=(0, 1))
717
+ b_dv = tl.zeros([BC, BV], dtype=tl.float32)
718
+ for i_j in range(i_i + 1, NC):
719
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_j * BC, i_v * BV), (BC, BV), (1, 0))
720
+ p_A = tl.make_block_ptr(A + i_bh * T * BT, (BT, T), (1, BT), (i_i * BC, i_t * BT + i_j * BC), (BC, BC), (0, 1))
721
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_j * BC, i_v * BV), (BC, BV), (1, 0))
722
+ # [BC, BV]
723
+ b_z = tl.load(p_z, boundary_check=(0, 1))
724
+ b_do = tl.load(p_do, boundary_check=(0, 1))
725
+ b_do = (b_do * exp(b_zn[None, :] - b_z)).to(b_do.dtype)
726
+ # [BC, BC]
727
+ b_A = tl.load(p_A, boundary_check=(0, 1))
728
+ b_dv += tl.dot(b_A, b_do, allow_tf32=False)
729
+ b_dv *= exp(b_v - b_zn[None, :])
730
+
731
+ o_i = tl.arange(0, BC)
732
+ for j in range(0, BC):
733
+ p_z = tl.make_block_ptr(z + i_bh * T*V, (T * V,), (1,), ((i_t * BT + i_i * BC + j) * V + i_v * BV,), (BV,), (0,))
734
+ p_A = tl.make_block_ptr(A + i_bh * T * BT, (T * BT,), (1,), ((i_t * BT + i_i * BC + j) * BT + i_i * BC,), (BC,), (0,))
735
+ p_do = tl.make_block_ptr(do + i_bh * T*V, (T * V,), (1,), ((i_t * BT + i_i * BC + j) * V + i_v * BV,), (BV,), (0,))
736
+ # [BC,]
737
+ b_A = tl.load(p_A, boundary_check=(0,))
738
+ # [BV,]
739
+ b_z = tl.load(p_z, boundary_check=(0,))
740
+ b_do = tl.load(p_do, boundary_check=(0,))
741
+ # [BC, BV]
742
+ m_i = o_i[:, None] <= j
743
+ b_dv += tl.where(m_i, exp(b_v - b_z[None, :]) * b_A[:, None] * b_do[None, :], 0.)
744
+ p_dv = tl.make_block_ptr(dv + i_bh * T*V, (T, V), (V, 1), (i_t * BT + i_i * BC, i_v * BV), (BC, BV), (1, 0))
745
+ tl.store(p_dv, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
746
+
747
+
748
+ @triton.jit(do_not_specialize=['T'])
749
+ def chunk_abc_bwd_kernel_rcum_inter(
750
+ s,
751
+ z,
752
+ ss,
753
+ doo,
754
+ T,
755
+ S: tl.constexpr,
756
+ BT: tl.constexpr,
757
+ BS: tl.constexpr,
758
+ NT: tl.constexpr,
759
+ ):
760
+ i_m, i_bh = tl.program_id(0), tl.program_id(1)
761
+
762
+ b_sp = tl.zeros([BS], dtype=tl.float32)
763
+ b_zp = tl.full([BS], float('inf'), dtype=tl.float32)
764
+ for i_t in range(NT - 1, -1, -1):
765
+ p_s = tl.make_block_ptr(s + i_bh * T*S, (T, S), (S, 1), (i_t * BT, i_m * BS), (BT, BS), (1, 0))
766
+ p_z = tl.make_block_ptr(z + i_bh * T*S, (T, S), (S, 1), (i_t * BT, i_m * BS), (BT, BS), (1, 0))
767
+ p_zc = tl.make_block_ptr(z + i_bh * T*S, (T*S,), (1,), ((i_t * BT) * S + i_m * BS,), (BS,), (0,))
768
+ p_ss = tl.make_block_ptr(ss + i_bh * T*S, (T, S), (S, 1), (i_t * BT, i_m * BS), (BT, BS), (1, 0))
769
+ p_doo = tl.make_block_ptr(doo + i_bh * T*S, (T, S), (S, 1), (i_t * BT, i_m * BS), (BT, BS), (1, 0))
770
+ # [BS,]
771
+ b_zc = tl.load(p_zc, boundary_check=(0,))
772
+ # [BT, BS]
773
+ b_s = tl.load(p_s, boundary_check=(0, 1))
774
+ b_z = tl.load(p_z, boundary_check=(0, 1))
775
+ b_ss = tl.load(p_ss, boundary_check=(0, 1))
776
+
777
+ b_doo = exp(b_s - b_zp[None, :]) * b_sp[None, :]
778
+ tl.store(p_doo, b_doo.to(p_doo.dtype.element_ty), boundary_check=(0, 1))
779
+ # [BS,]
780
+ b_sp = b_sp * exp(b_zc - b_zp) + tl.sum(b_ss * exp(b_zc[None, :] - b_z), 0)
781
+ b_zp = b_zc
782
+
783
+
784
+ @triton.jit(do_not_specialize=['T'])
785
+ def chunk_abc_bwd_kernel_rcum_intra(
786
+ s,
787
+ z,
788
+ ss,
789
+ doo,
790
+ T,
791
+ S: tl.constexpr,
792
+ BT: tl.constexpr,
793
+ BC: tl.constexpr,
794
+ BS: tl.constexpr,
795
+ NC: tl.constexpr,
796
+ ):
797
+ i_s, i_c, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
798
+ i_t, i_i = i_c // NC, i_c % NC
799
+
800
+ o_i = tl.arange(0, BC)
801
+ m_o = tl.full([BC, BC], 1., dtype=tl.float32)
802
+
803
+ p_s = tl.make_block_ptr(s + i_bh * T*S, (T, S), (S, 1), (i_t * BT + i_i * BC, i_s * BS), (BC, BS), (1, 0))
804
+ p_zn = tl.make_block_ptr(z + i_bh * T*S, (T*S,), (1,), ((i_t * BT + i_i * BC + BC - 1) * S + i_s * BS,), (BS,), (0,))
805
+ p_doo = tl.make_block_ptr(doo + i_bh * T*S, (T, S), (S, 1), (i_t * BT + i_i * BC, i_s * BS), (BC, BS), (1, 0))
806
+ # [BC, BS]
807
+ b_s = tl.load(p_s, boundary_check=(0, 1))
808
+ # [BS,]
809
+ b_zn = tl.load(p_zn, boundary_check=(0,))
810
+
811
+ b_doo = tl.zeros([BC, BS], dtype=tl.float32)
812
+ for i_j in range(i_i + 1, NC):
813
+ p_z = tl.make_block_ptr(z + i_bh * T*S, (T, S), (S, 1), (i_t * BT + i_j * BC, i_s * BS), (BC, BS), (1, 0))
814
+ p_ss = tl.make_block_ptr(ss + i_bh * T*S, (T, S), (S, 1), (i_t * BT + i_j * BC, i_s * BS), (BC, BS), (1, 0))
815
+ # [BC, BS]
816
+ b_z = tl.load(p_z, boundary_check=(0, 1))
817
+ b_ss = tl.load(p_ss, boundary_check=(0, 1))
818
+ # [BC, BS]
819
+ b_doo += b_ss * exp(b_zn[None, :] - b_z)
820
+ b_doo = exp(b_s - b_zn[None, :]) * tl.dot(m_o.to(b_s.dtype), b_doo.to(b_s.dtype), allow_tf32=False)
821
+
822
+ for j in range(0, BC):
823
+ p_z = tl.make_block_ptr(z + i_bh * T*S, (T*S,), (1,), ((i_t * BT + i_i * BC + j) * S + i_s * BS,), (BS,), (0,))
824
+ p_ss = tl.make_block_ptr(ss + i_bh * T*S, (T*S,), (1,), ((i_t * BT + i_i * BC + j) * S + i_s * BS,), (BS,), (0,))
825
+ # [BS,]
826
+ b_z = tl.load(p_z, boundary_check=(0,))
827
+ b_ss = tl.load(p_ss, boundary_check=(0,))
828
+ # [BC, BS]
829
+ m_i = o_i[:, None] <= j
830
+ b_doo += tl.where(m_i, exp(b_s - b_z[None, :]) * b_ss[None, :], 0.)
831
+ b_doo += tl.load(p_doo, boundary_check=(0, 1))
832
+ tl.store(p_doo, b_doo.to(p_doo.dtype.element_ty), boundary_check=(0, 1))
833
+
834
+
835
+ class ChunkABCFunction(torch.autograd.Function):
836
+
837
+ @staticmethod
838
+ @input_guard
839
+ def forward(ctx, q, k, v, s, initial_state, output_final_state):
840
+ B, H, T, K, V, M = *q.shape, v.shape[-1], s.shape[-1]
841
+ BT, BC = 64, 16
842
+ BK = min(64, triton.next_power_of_2(K))
843
+ BV = min(64, triton.next_power_of_2(V))
844
+ BM = min(64, triton.next_power_of_2(M))
845
+ NT, NC = triton.cdiv(T, BT), triton.cdiv(BT, BC)
846
+ NV, NM = triton.cdiv(V, BV), triton.cdiv(M, BM)
847
+ num_warps = 4 if BK == 64 else 2
848
+ num_stages = 1
849
+
850
+ def fwd_pre(s, B, H, T, S):
851
+ # keep cummulative normalizer in fp32
852
+ z = torch.empty_like(s, dtype=torch.float)
853
+ grid = (B * H,)
854
+ logcumsumexp_fwd_kernel[grid](
855
+ s, z,
856
+ T=T, S=S,
857
+ )
858
+ return z
859
+
860
+ def fwd_inner(q, k, v, z, B, H, T, K, V, BT, BK, BV, NT, normk=False, h0=None, ht=None):
861
+ NK, NV = triton.cdiv(K, BK), triton.cdiv(V, BV)
862
+ h = q.new_empty(B, H, NT * K, V)
863
+ grid = (NV, NK, B * H)
864
+ chunk_abc_fwd_kernel_h[grid](
865
+ k, v, z, h, h0, ht,
866
+ T=T, K=K, V=V, BT=BT, BK=BK, BV=BV, NT=NT,
867
+ NORMK=normk,
868
+ USE_INITIAL_STATE=h0 is not None,
869
+ STORE_FINAL_STATE=ht is not None,
870
+ num_warps=num_warps,
871
+ num_stages=num_stages,
872
+ )
873
+ return h
874
+
875
+ final_state = None
876
+ if output_final_state:
877
+ final_state = (q.new_empty(B, H, K, M, dtype=torch.float),
878
+ q.new_empty(B, H, M, V, dtype=torch.float))
879
+
880
+ z = fwd_pre(s, B, H, T, M)
881
+ scale = K ** -0.5
882
+ hk = fwd_inner(
883
+ q=q, k=k, v=s, z=z,
884
+ B=B, H=H, T=T, K=K, V=M, BT=BT, BK=BK, BV=BM, NT=NT,
885
+ normk=False,
886
+ h0=initial_state[0] if initial_state is not None else None,
887
+ ht=final_state[0] if final_state is not None else None,
888
+ )
889
+ ok1 = torch.empty_like(s)
890
+ Ak = q.new_empty(B, H, T, BT)
891
+ grid = (NM, NT, B * H)
892
+ chunk_abc_fwd_kernel_K[grid](
893
+ q, k, z, hk, ok1, Ak,
894
+ scale=scale,
895
+ T=T, K=K, V=M, BT=BT, BK=BK, BV=BM, NT=NT,
896
+ num_warps=num_warps,
897
+ num_stages=num_stages,
898
+ )
899
+ ok0 = torch.empty_like(s)
900
+ grid = (NM, NT * NC, B * H)
901
+ chunk_abc_fwd_kernel_intra_K[grid](
902
+ s, z, ok0, Ak,
903
+ T=T, V=M, BT=BT, BC=BC, BV=BM, NC=NC,
904
+ num_warps=2,
905
+ num_stages=num_stages,
906
+ )
907
+ ok = ok0.add_(ok1)
908
+
909
+ scale = 1.
910
+ # p is kept in fp32 for safe softmax backward
911
+ p = softmax_fwd(ok, dtype=torch.float)
912
+ qv = p.to(q.dtype)
913
+
914
+ scale = 1.
915
+ hv = fwd_inner(
916
+ q=qv, k=s, v=v, z=z,
917
+ B=B, H=H, T=T, K=M, V=V, BT=BT, BK=BM, BV=BV, NT=NT,
918
+ normk=True,
919
+ h0=initial_state[1] if initial_state is not None else None,
920
+ ht=final_state[1] if final_state is not None else None,
921
+ )
922
+ Av = q.new_zeros(NM, B, H, T, BT)
923
+ grid = (NM, NT * NC * NC, B * H)
924
+ chunk_abc_fwd_kernel_intra_V[grid](
925
+ qv, s, z, Av,
926
+ scale=scale,
927
+ T=T, K=M, BT=BT, BC=BC, BK=BM, NC=NC,
928
+ num_warps=2,
929
+ num_stages=num_stages,
930
+ )
931
+ Av = Av.sum(0)
932
+ ov = torch.empty_like(v)
933
+ grid = (NV, NT, B * H)
934
+ chunk_abc_fwd_kernel_V[grid](
935
+ qv, v, z, hv, ov, Av,
936
+ scale=scale,
937
+ T=T,
938
+ K=M,
939
+ V=V,
940
+ BT=BT,
941
+ BK=BM,
942
+ BV=BV,
943
+ NT=NT,
944
+ num_warps=num_warps,
945
+ num_stages=num_stages,
946
+ )
947
+ ctx.save_for_backward(q, k, v, s, z, ok, p, hk, hv, Av)
948
+ ctx.BT = BT
949
+ return ov, final_state
950
+
951
+ @staticmethod
952
+ @input_guard
953
+ def backward(ctx, dov, dht=None):
954
+ q, k, v, s, z, ok, p, hk, hv, Av = ctx.saved_tensors
955
+ B, H, T, K, V, M = *q.shape, v.shape[-1], s.shape[-1]
956
+ BT, BC = ctx.BT, 16
957
+ BK = min(64, triton.next_power_of_2(K))
958
+ BV = min(64, triton.next_power_of_2(V))
959
+ BM = min(64, triton.next_power_of_2(M))
960
+ NT, NC = triton.cdiv(T, BT), triton.cdiv(BT, BC)
961
+ NK, NM = triton.cdiv(K, BK), triton.cdiv(M, BM)
962
+ num_warps = 4 if BK == 64 else 2
963
+ num_stages = 1
964
+
965
+ def bwd_inner(q, z, do, B, H, T, K, V, BT, BK, BV, NT, scale, normk=False):
966
+ NK, NV = triton.cdiv(K, BK), triton.cdiv(V, BV)
967
+ dh = q.new_empty(B, H, NT * K, V)
968
+ grid = (NK, NV, B * H)
969
+ chunk_abc_bwd_kernel_dh[grid](
970
+ q, z, do, dh,
971
+ scale=scale,
972
+ T=T, K=K, V=V, BT=BT, BK=BK, BV=BV, NT=NT,
973
+ NORMK=normk,
974
+ num_warps=num_warps,
975
+ num_stages=num_stages,
976
+ )
977
+ return dh
978
+
979
+ def bwd_post(s, z, ss, B, H, T, S, BT, BC, BS, NT, NC, NS):
980
+ doo = torch.empty_like(s)
981
+ grid = (NS, B * H)
982
+ chunk_abc_bwd_kernel_rcum_inter[grid](
983
+ s, z, ss, doo,
984
+ T=T, S=S, BT=BT, BS=BS, NT=NT,
985
+ num_warps=num_warps,
986
+ num_stages=num_stages,
987
+ )
988
+ grid = (NS, NT * NC, B * H)
989
+ chunk_abc_bwd_kernel_rcum_intra[grid](
990
+ s, z, ss, doo,
991
+ T=T, S=S, BT=BT, BC=BC, BS=BS, NC=NC,
992
+ num_warps=num_warps,
993
+ num_stages=num_stages,
994
+ )
995
+ return doo
996
+
997
+ scale = 1.
998
+ qv = p.to(q.dtype)
999
+ dhv = bwd_inner(
1000
+ qv, z, dov,
1001
+ B=B, H=H, T=T, K=M, V=V, BT=BT, BK=BM, BV=BV, NT=NT,
1002
+ scale=scale,
1003
+ normk=True,
1004
+ )
1005
+ dp1 = torch.empty_like(p)
1006
+ dsv1 = torch.empty_like(s, dtype=torch.float)
1007
+ dv = v.new_empty(NM, *v.shape)
1008
+ dAv = q.new_zeros(B, H, T, BT)
1009
+ grid = (NM, NT, B * H)
1010
+ chunk_abc_bwd_kernel_V[grid](
1011
+ s, v, z, hv, Av, dov, dhv, dp1, dsv1, dv, dAv,
1012
+ scale=scale,
1013
+ T=T, K=M, V=V, BT=BT, BK=BM, BV=BV, NT=NT,
1014
+ num_warps=num_warps,
1015
+ num_stages=num_stages,
1016
+ )
1017
+ dv = dv.sum(0)
1018
+ dp0 = torch.empty_like(p)
1019
+ dsv0 = s.new_zeros(s.shape, dtype=torch.float)
1020
+ grid = (NM, NT * NC, B * H)
1021
+ chunk_abc_bwd_kernel_intra_V[grid](
1022
+ qv, s, z, dAv, dp0, dsv0,
1023
+ T=T, K=M, BT=BT, BC=BC, BK=BM, NC=NC,
1024
+ num_warps=2,
1025
+ num_stages=num_stages,
1026
+ )
1027
+ dp = dp1.add_(dp0)
1028
+ dsv = dsv1.add_(dsv0)
1029
+
1030
+ # softmax gradient, equivalent to:
1031
+ # dok = p * (dp - (p * dp).sum(-1, True))
1032
+ dok = softmax_bwd(p, dp, dtype=ok.dtype)
1033
+
1034
+ scale = K ** -0.5
1035
+ dhk = bwd_inner(
1036
+ q, z, dok,
1037
+ B=B, H=H, T=T, K=K, V=M, BT=BT, BK=BK, BV=BM, NT=NT,
1038
+ scale=scale,
1039
+ normk=False,
1040
+ )
1041
+ dAk = q.new_zeros(NM, B, H, T, BT)
1042
+ grid = (NM, NT * NC * NC, B * H)
1043
+ chunk_abc_bwd_kernel_intra_K[grid](
1044
+ s, z, dok, dAk,
1045
+ scale=scale,
1046
+ T=T, V=M, BT=BT, BC=BC, BV=BM, NC=NC,
1047
+ num_warps=2,
1048
+ num_stages=num_stages,
1049
+ )
1050
+ dAk = dAk.sum(0)
1051
+
1052
+ Ak = q.new_zeros(NK, B, H, T, BT)
1053
+ dq = torch.empty_like(q)
1054
+ dk = torch.empty_like(k)
1055
+ dsk1 = s.new_empty(NK, *s.shape, dtype=torch.float)
1056
+ grid = (NK, NT, B * H)
1057
+ chunk_abc_bwd_kernel_K[grid](
1058
+ q, k, s, z, hk, Ak, dok, dhk, dq, dk, dsk1, dAk,
1059
+ scale=scale,
1060
+ T=T, K=K, V=M, BT=BT, BK=BK, BV=BM, NT=NT,
1061
+ num_warps=num_warps,
1062
+ num_stages=num_stages,
1063
+ )
1064
+ Ak = Ak.sum(0)
1065
+ dsk1 = dsk1.sum(0)
1066
+ dsk0 = torch.empty_like(s, dtype=torch.float)
1067
+ grid = (NM, NT * NC, B * H)
1068
+ chunk_abc_bwd_kernel_intra_KV[grid](
1069
+ s, z, Ak, dok, dsk0,
1070
+ T=T, V=M, BT=BT, BC=BC, BV=BM, NC=NC,
1071
+ num_warps=2,
1072
+ num_stages=num_stages,
1073
+ )
1074
+ ds = dsv.add_(dsk1.add_(dsk0))
1075
+ ds -= bwd_post(s, z, ok * dok + p * dp, B, H, T, M, BT, BC, BM, NT, NC, NM)
1076
+ ds = ds.to(s.dtype)
1077
+ return dq, dk, dv, ds, None, None
1078
+
1079
+
1080
+ @torch.compiler.disable
1081
+ def chunk_abc(
1082
+ q: torch.Tensor,
1083
+ k: torch.Tensor,
1084
+ v: torch.Tensor,
1085
+ s: torch.Tensor,
1086
+ initial_state: tuple[torch.Tensor] | None = None,
1087
+ output_final_state: bool = False,
1088
+ head_first: bool = False,
1089
+ ) -> tuple[torch.Tensor, torch.Tensor]:
1090
+ r"""
1091
+ Args:
1092
+ q (torch.Tensor):
1093
+ queries of shape `[B, T, H, K]`.
1094
+ k (torch.Tensor):
1095
+ keys of shape `[B, T, H, K]`.
1096
+ v (torch.Tensor):
1097
+ values of shape `[B, T, H, V]`.
1098
+ s (torch.Tensor):
1099
+ slot representations of shape `[B, T, H, M]`.
1100
+ initial_state (Optional[Tuple[torch.Tensor, torch.Tensor]]):
1101
+ Initial states of shape `[B, H, K, M]` and `[B, H, M, V]`. Default: `None`.
1102
+ output_final_state (Optional[bool]):
1103
+ Whether to output the final state of shape `[B, H, K, M]` and `[B, H, M, V]`. Default: `False`.
1104
+ head_first (Optional[bool]):
1105
+ Whether the inputs are in the head-first format. Default: `False`.
1106
+ This argument has been deprecated.
1107
+
1108
+ Returns:
1109
+ o (torch.Tensor):
1110
+ Outputs of shape `[B, T, H, V]`.
1111
+ final_state (torch.Tensor):
1112
+ Final state of shape `[B, H, K, M]` and `[B, H, M, V]` if `output_final_state=True` else `None`.
1113
+ """
1114
+ if not head_first:
1115
+ q, k, v, s = map(lambda x: x.transpose(1, 2), (q, k, v, s))
1116
+ o, final_state = ChunkABCFunction.apply(q, k, v, s, initial_state, output_final_state)
1117
+ if not head_first:
1118
+ o = o.transpose(1, 2)
1119
+ return o, final_state
build/torch-cuda/ops/abc/naive.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from einops import repeat
10
+
11
+
12
+ def naive_recurrent_abc(
13
+ q: torch.Tensor,
14
+ k: torch.Tensor,
15
+ v: torch.Tensor,
16
+ s: torch.Tensor,
17
+ g: torch.Tensor | None = None,
18
+ scale: int | None = None,
19
+ initial_state: torch.Tensor | None = None,
20
+ output_final_state: bool | None = False,
21
+ ) -> torch.Tensor:
22
+ dtype = q.dtype
23
+
24
+ NG = q.shape[1]//k.shape[1]
25
+ # [batch_size, n_heads, seq_len, n_slots]
26
+ if g is None:
27
+ z = s.float().logcumsumexp(2)
28
+ g = torch.cat((z[:, :, :1], z[:, :, :-1]), 2) - z
29
+ s = torch.exp(s - z)
30
+ q, k, v, s, g = map(lambda x: x.float(), (q, k, v, s, g))
31
+ k, v, s, g = map(lambda x: repeat(x, 'b h t d -> b (h g) t d', g=NG), (k, v, s, g))
32
+ if initial_state is not None:
33
+ initial_state = tuple(map(lambda x: repeat(x, 'b h k v -> b (h g) k v', g=NG), initial_state))
34
+
35
+ B, H, T, K, V, M = *q.shape, v.shape[-1], s.shape[-1]
36
+
37
+ hk = torch.zeros(B, H, K, M, dtype=torch.float, device=q.device)
38
+ ok = torch.zeros_like(s)
39
+
40
+ if scale is None:
41
+ scale = q.shape[-1] ** -0.5
42
+
43
+ final_state = None
44
+ if initial_state is not None:
45
+ hk += initial_state[0]
46
+
47
+ for i in range(T):
48
+ q_i = q[:, :, i] * scale
49
+ k_i = k[:, :, i]
50
+ v_i = s[:, :, i]
51
+ g_i = g[:, :, i].exp()
52
+ hk = hk * g_i[..., None, :] + k_i[..., None] * v_i[..., None, :]
53
+ ok[:, :, i] = (q_i[..., None] * hk).sum(-2)
54
+
55
+ qv = ok.softmax(-1)
56
+ hv = torch.zeros(B, H, M, V, dtype=torch.float, device=q.device)
57
+ ov = torch.zeros_like(v)
58
+ if initial_state is not None:
59
+ hv += initial_state[1]
60
+
61
+ for i in range(T):
62
+ q_i = qv[:, :, i]
63
+ k_i = s[:, :, i]
64
+ v_i = v[:, :, i]
65
+ g_i = g[:, :, i].exp()
66
+ hv = hv * g_i[..., :, None] + k_i[..., None] * v_i[..., None, :]
67
+ ov[:, :, i] = (q_i[..., None] * hv).sum(-2)
68
+
69
+ if output_final_state:
70
+ final_state = (hk.view(B, -1, NG, K, M)[:, :, 0], hv.view(B, -1, NG, M, V)[:, :, 0])
71
+ return ov.to(dtype), final_state
72
+
73
+
74
+ def naive_cumsum_abc(
75
+ q: torch.Tensor,
76
+ k: torch.Tensor,
77
+ v: torch.Tensor,
78
+ s: torch.Tensor,
79
+ ) -> torch.Tensor:
80
+ """
81
+ A simple implementation of vanilla ABC that is more aligned with the descriptions in the paper.
82
+ This is just for demonstration purposes, with no numerical stabilities guaranteed.
83
+ """
84
+
85
+ dtype = q.dtype
86
+ q, k, v, s = map(lambda x: x.float(), (q, k, v, s))
87
+
88
+ scale = q.shape[-1] ** -0.5
89
+ # [batch_size, n_heads, seq_len, n_slots]
90
+ s = (s - s.max(2, True)[0]).exp()
91
+ z = s.cumsum(2)
92
+ # [batch_size, n_heads, seq_len, n_slots, d_head]
93
+ K = (s.unsqueeze(-1) * k.unsqueeze(-2)).cumsum(2) / z.unsqueeze(-1)
94
+ V = (s.unsqueeze(-1) * v.unsqueeze(-2)).cumsum(2) / z.unsqueeze(-1)
95
+ # [batch_size, n_heads, seq_len, n_slots]
96
+ p = torch.einsum('...d,...md->...m', q * scale, K).softmax(-1)
97
+ # [batch_size, n_heads, seq_len, d_head]
98
+ o = torch.einsum('...m,...md->...d', p, V)
99
+ return o.to(dtype), None
build/torch-cuda/ops/attn/__init__.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 .naive import naive_parallel_attn
9
+ from .parallel import parallel_attn
10
+
11
+ __all__ = [
12
+ 'naive_parallel_attn',
13
+ 'parallel_attn',
14
+ ]