trasmuon-pytorch 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .DS_Store
9
+ .env
10
+ .env.*
11
+ !.env.example
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) TrasMuon Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
@@ -0,0 +1,56 @@
1
+ # Notice
2
+
3
+ This repository is a trimmed research code release for the TrasMuon project and paper at <https://arxiv.org/abs/2602.13498>.
4
+
5
+ The repository remains MIT licensed under `LICENSE`. It also retains material adapted from the following upstream sources.
6
+
7
+ ## Modded-NanoGPT
8
+
9
+ Source: <https://github.com/KellerJordan/modded-nanogpt>
10
+
11
+ MIT License
12
+
13
+ Copyright (c) 2024 Keller Jordan
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+
33
+ ## Moonlight
34
+
35
+ Source: <https://github.com/MoonshotAI/Moonlight>
36
+
37
+ The MIT License (MIT)
38
+ Copyright (c) 2025 Moonshot AI
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining a copy
41
+ of this software and associated documentation files (the "Software"), to deal
42
+ in the Software without restriction, including without limitation the rights
43
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
44
+ copies of the Software, and to permit persons to whom the Software is
45
+ furnished to do so, subject to the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be included in all
48
+ copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
56
+ SOFTWARE.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.5
2
+ Name: trasmuon-pytorch
3
+ Version: 0.1.0
4
+ Summary: Trust-region adaptive scaling for orthogonalized momentum optimizers in PyTorch
5
+ Project-URL: Homepage, https://github.com/zangjiucheng/TrasMuon
6
+ Project-URL: Source, https://github.com/zangjiucheng/TrasMuon
7
+ Project-URL: Issues, https://github.com/zangjiucheng/TrasMuon/issues
8
+ Project-URL: Paper, https://arxiv.org/abs/2602.13498
9
+ Author: TrasMuon Authors
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ License-File: NOTICE.md
13
+ Keywords: deep-learning,muon,optimizer,pytorch
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: torch>=2.5.1
20
+ Requires-Dist: triton
21
+ Description-Content-Type: text/markdown
22
+
23
+ # TrasMuon
24
+
25
+ [![arXiv](https://img.shields.io/badge/arXiv-2602.13498-b31b1b.svg)](https://arxiv.org/abs/2602.13498)
26
+
27
+ Optimizer implementation for **TrasMuon: Trust-Region Adaptive Scaling for Orthogonalized Momentum Optimizers**. TrasMuon combines Muon with adaptive scaling, trust-region column gating, and schedule-free smoothing.
28
+
29
+ ![TrasMuon algorithm: adaptive scaling, trust-region column gating, and schedule-free smoothing](https://raw.githubusercontent.com/zangjiucheng/TrasMuon/main/assets/trasmuon-algorithm.png)
30
+
31
+ ## Installation
32
+
33
+ Requires Python ≥ 3.10, PyTorch ≥ 2.5.1, and Triton. Use Linux with CUDA-enabled PyTorch for GPU execution.
34
+
35
+ Install from GitHub:
36
+
37
+ ```bash
38
+ pip install git+https://github.com/zangjiucheng/TrasMuon.git
39
+ ```
40
+
41
+ After the first PyPI release, install with:
42
+
43
+ ```bash
44
+ pip install trasmuon-pytorch
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ```python
50
+ from trasmuon import TrasMuon
51
+
52
+ optimizer = TrasMuon(model.parameters(), lr=1e-3, weight_decay=0.01)
53
+
54
+ optimizer.zero_grad(set_to_none=True)
55
+ loss = loss_fn(model(inputs), targets)
56
+ loss.backward()
57
+ optimizer.step()
58
+ ```
59
+
60
+ Two-dimensional parameters use TrasMuon; other parameter shapes use the optimizer's AdamW fallback. See [`TrasMuon.__init__`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/optimizer.py) for available options.
61
+
62
+ ## Files
63
+
64
+ - [`trasmuon/optimizer.py`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/optimizer.py): optimizer implementation.
65
+ - [`trasmuon/newton_schulz_triton.py`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/newton_schulz_triton.py): required Newton–Schulz kernel dependency.
66
+ - [`trasmuon/__init__.py`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/__init__.py): exports `TrasMuon`.
67
+
68
+ ## Publishing
69
+
70
+ Build and validate a release locally:
71
+
72
+ ```bash
73
+ python -m pip install build twine
74
+ python -m build
75
+ python -m twine check --strict dist/*
76
+ ```
77
+
78
+ For the first release, add a [pending Trusted Publisher](https://pypi.org/manage/account/publishing/) on PyPI with:
79
+
80
+ - Project: `trasmuon-pytorch`
81
+ - Owner / repository: `zangjiucheng` / `TrasMuon`
82
+ - Workflow: `publish.yml`
83
+ - Environment: `pypi`
84
+
85
+ Create the matching `pypi` environment in the GitHub repository settings. Update the version in `pyproject.toml`, then publish a GitHub Release with the matching tag (initially `v0.1.0`). The release workflow builds and validates both distributions, then publishes them through Trusted Publishing. No PyPI API token is needed. See the [PyPI setup guide](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/).
86
+
87
+ ## Citation
88
+
89
+ If you use this code, cite the TrasMuon paper:
90
+
91
+ ```bibtex
92
+ @misc{cheng2026trasmuon,
93
+ title = {TrasMuon: Trust-Region Adaptive Scaling for Orthogonalized Momentum Optimizers},
94
+ author = {Peng Cheng and Jiucheng Zang and Qingnan Li and Liheng Ma and Yufei Cui and Yingxue Zhang and Boxing Chen and Ming Jian and Wen Tong},
95
+ year = {2026},
96
+ eprint = {2602.13498},
97
+ archivePrefix = {arXiv},
98
+ primaryClass = {cs.LG},
99
+ url = {https://arxiv.org/abs/2602.13498}
100
+ }
101
+ ```
102
+
103
+ ## License
104
+
105
+ This repository is released under the MIT license. See [LICENSE](https://github.com/zangjiucheng/TrasMuon/blob/main/LICENSE) and [NOTICE.md](https://github.com/zangjiucheng/TrasMuon/blob/main/NOTICE.md).
@@ -0,0 +1,83 @@
1
+ # TrasMuon
2
+
3
+ [![arXiv](https://img.shields.io/badge/arXiv-2602.13498-b31b1b.svg)](https://arxiv.org/abs/2602.13498)
4
+
5
+ Optimizer implementation for **TrasMuon: Trust-Region Adaptive Scaling for Orthogonalized Momentum Optimizers**. TrasMuon combines Muon with adaptive scaling, trust-region column gating, and schedule-free smoothing.
6
+
7
+ ![TrasMuon algorithm: adaptive scaling, trust-region column gating, and schedule-free smoothing](https://raw.githubusercontent.com/zangjiucheng/TrasMuon/main/assets/trasmuon-algorithm.png)
8
+
9
+ ## Installation
10
+
11
+ Requires Python ≥ 3.10, PyTorch ≥ 2.5.1, and Triton. Use Linux with CUDA-enabled PyTorch for GPU execution.
12
+
13
+ Install from GitHub:
14
+
15
+ ```bash
16
+ pip install git+https://github.com/zangjiucheng/TrasMuon.git
17
+ ```
18
+
19
+ After the first PyPI release, install with:
20
+
21
+ ```bash
22
+ pip install trasmuon-pytorch
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```python
28
+ from trasmuon import TrasMuon
29
+
30
+ optimizer = TrasMuon(model.parameters(), lr=1e-3, weight_decay=0.01)
31
+
32
+ optimizer.zero_grad(set_to_none=True)
33
+ loss = loss_fn(model(inputs), targets)
34
+ loss.backward()
35
+ optimizer.step()
36
+ ```
37
+
38
+ Two-dimensional parameters use TrasMuon; other parameter shapes use the optimizer's AdamW fallback. See [`TrasMuon.__init__`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/optimizer.py) for available options.
39
+
40
+ ## Files
41
+
42
+ - [`trasmuon/optimizer.py`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/optimizer.py): optimizer implementation.
43
+ - [`trasmuon/newton_schulz_triton.py`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/newton_schulz_triton.py): required Newton–Schulz kernel dependency.
44
+ - [`trasmuon/__init__.py`](https://github.com/zangjiucheng/TrasMuon/blob/main/trasmuon/__init__.py): exports `TrasMuon`.
45
+
46
+ ## Publishing
47
+
48
+ Build and validate a release locally:
49
+
50
+ ```bash
51
+ python -m pip install build twine
52
+ python -m build
53
+ python -m twine check --strict dist/*
54
+ ```
55
+
56
+ For the first release, add a [pending Trusted Publisher](https://pypi.org/manage/account/publishing/) on PyPI with:
57
+
58
+ - Project: `trasmuon-pytorch`
59
+ - Owner / repository: `zangjiucheng` / `TrasMuon`
60
+ - Workflow: `publish.yml`
61
+ - Environment: `pypi`
62
+
63
+ Create the matching `pypi` environment in the GitHub repository settings. Update the version in `pyproject.toml`, then publish a GitHub Release with the matching tag (initially `v0.1.0`). The release workflow builds and validates both distributions, then publishes them through Trusted Publishing. No PyPI API token is needed. See the [PyPI setup guide](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/).
64
+
65
+ ## Citation
66
+
67
+ If you use this code, cite the TrasMuon paper:
68
+
69
+ ```bibtex
70
+ @misc{cheng2026trasmuon,
71
+ title = {TrasMuon: Trust-Region Adaptive Scaling for Orthogonalized Momentum Optimizers},
72
+ author = {Peng Cheng and Jiucheng Zang and Qingnan Li and Liheng Ma and Yufei Cui and Yingxue Zhang and Boxing Chen and Ming Jian and Wen Tong},
73
+ year = {2026},
74
+ eprint = {2602.13498},
75
+ archivePrefix = {arXiv},
76
+ primaryClass = {cs.LG},
77
+ url = {https://arxiv.org/abs/2602.13498}
78
+ }
79
+ ```
80
+
81
+ ## License
82
+
83
+ This repository is released under the MIT license. See [LICENSE](https://github.com/zangjiucheng/TrasMuon/blob/main/LICENSE) and [NOTICE.md](https://github.com/zangjiucheng/TrasMuon/blob/main/NOTICE.md).
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.26"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "trasmuon-pytorch"
7
+ version = "0.1.0"
8
+ description = "Trust-region adaptive scaling for orthogonalized momentum optimizers in PyTorch"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE", "NOTICE.md"]
13
+ authors = [{ name = "TrasMuon Authors" }]
14
+ keywords = ["pytorch", "optimizer", "muon", "deep-learning"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
+ ]
21
+ dependencies = ["torch>=2.5.1", "triton"]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/zangjiucheng/TrasMuon"
25
+ Source = "https://github.com/zangjiucheng/TrasMuon"
26
+ Issues = "https://github.com/zangjiucheng/TrasMuon/issues"
27
+ Paper = "https://arxiv.org/abs/2602.13498"
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["trasmuon"]
31
+
32
+ [tool.hatch.build.targets.sdist]
33
+ include = ["/trasmuon", "/assets", "/README.md", "/LICENSE", "/NOTICE.md", "/pyproject.toml"]
@@ -0,0 +1,5 @@
1
+ from .optimizer import TrasMuon
2
+
3
+ __all__ = [
4
+ "TrasMuon",
5
+ ]
@@ -0,0 +1,374 @@
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+ from torch import Tensor
5
+
6
+
7
+ def _get_autotune_configs():
8
+ return [
9
+ triton.Config(
10
+ {
11
+ "BLOCK_SIZE_M": bm,
12
+ "BLOCK_SIZE_N": bn,
13
+ "BLOCK_SIZE_K": bk,
14
+ "GROUP_SIZE_M": 8,
15
+ "LOWER_UPPER": 1,
16
+ },
17
+ num_stages=stages,
18
+ num_warps=warps,
19
+ )
20
+ for bm in [64, 128]
21
+ for bn in [64, 128, 256]
22
+ for bk in [64, 128]
23
+ for stages, warps in [(3, 4), (3, 8), (4, 4)]
24
+ if bm // bn <= 2 and bn // bm <= 2
25
+ ]
26
+
27
+
28
+ @triton.jit
29
+ def _pid_to_block(
30
+ pid,
31
+ M,
32
+ BLOCK_SIZE_M: tl.constexpr,
33
+ BLOCK_SIZE_N: tl.constexpr,
34
+ GROUP_SIZE_M: tl.constexpr,
35
+ ):
36
+ """
37
+ Helper function to map Triton program ID to (batch, row, col) of the output matrix.
38
+ """
39
+ # Split output matrix into blocks of size (BLOCK_SIZE_M, BLOCK_SIZE_N)
40
+ num_pid_m = tl.cdiv(M, BLOCK_SIZE_M)
41
+ num_pid_n = tl.cdiv(M, BLOCK_SIZE_N)
42
+
43
+ # Map PID to a single matrix in batch
44
+ batch_idx = pid // (num_pid_m * num_pid_n)
45
+ pid = pid % (num_pid_m * num_pid_n)
46
+
47
+ # Map PID to 2D grid of blocks
48
+ pid_m = pid // num_pid_n
49
+ pid_n = pid % num_pid_n
50
+ pid_m, pid_n = tl.swizzle2d(pid_m, pid_n, num_pid_m, num_pid_n, GROUP_SIZE_M)
51
+
52
+ m_idx = pid_m * BLOCK_SIZE_M
53
+ n_idx = pid_n * BLOCK_SIZE_N
54
+
55
+ return batch_idx, m_idx, n_idx
56
+
57
+
58
+ @triton.autotune(
59
+ configs=_get_autotune_configs(),
60
+ key=["M", "K", "a_stride_r", "a_stride_c", "c_stride_r", "c_stride_c"],
61
+ )
62
+ @triton.jit
63
+ def ns_line_1_kernel(
64
+ A_ptr,
65
+ C_ptr,
66
+ M,
67
+ K,
68
+ a_stride_b,
69
+ a_stride_r,
70
+ a_stride_c,
71
+ c_stride_b,
72
+ c_stride_r,
73
+ c_stride_c,
74
+ BLOCK_SIZE_M: tl.constexpr,
75
+ BLOCK_SIZE_N: tl.constexpr,
76
+ BLOCK_SIZE_K: tl.constexpr,
77
+ GROUP_SIZE_M: tl.constexpr,
78
+ LOWER_UPPER: tl.constexpr,
79
+ ):
80
+ """
81
+ Input A has shape (M, K)
82
+ Output C has shape (M, M)
83
+ Compute C = A @ A.T
84
+ """
85
+
86
+ pid = tl.program_id(axis=0)
87
+ batch_idx, m_idx, n_idx = _pid_to_block(
88
+ pid, M, BLOCK_SIZE_M, BLOCK_SIZE_N, GROUP_SIZE_M
89
+ )
90
+
91
+ # Skip blocks that don't need to be computed
92
+ skip_block_below_diag = (LOWER_UPPER == 0) and (n_idx + BLOCK_SIZE_N <= m_idx)
93
+ skip_block_above_diag = (LOWER_UPPER != 0) and (m_idx + BLOCK_SIZE_M <= n_idx)
94
+ if skip_block_below_diag or skip_block_above_diag:
95
+ return
96
+
97
+ # Index into one matrix of batch
98
+ A_ptr += batch_idx * a_stride_b
99
+ C_ptr += batch_idx * c_stride_b
100
+
101
+ # Create pointer arrays for A and A.T
102
+ offs_m = (m_idx + tl.arange(0, BLOCK_SIZE_M)) % M
103
+ offs_n = (n_idx + tl.arange(0, BLOCK_SIZE_N)) % M
104
+ offs_k = tl.arange(0, BLOCK_SIZE_K)
105
+ a_ptrs = A_ptr + (offs_m[:, None] * a_stride_r + offs_k[None, :] * a_stride_c)
106
+ at_ptrs = A_ptr + (offs_k[:, None] * a_stride_c + offs_n[None, :] * a_stride_r)
107
+
108
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
109
+
110
+ # Accumulate over blocks of K
111
+ for k in tl.range(0, tl.cdiv(K, BLOCK_SIZE_K)):
112
+ a = tl.load(a_ptrs, mask=offs_k[None, :] < K - k * BLOCK_SIZE_K, other=0.0)
113
+ at = tl.load(at_ptrs, mask=offs_k[:, None] < K - k * BLOCK_SIZE_K, other=0.0)
114
+ accumulator = tl.dot(a, at, accumulator)
115
+ a_ptrs += BLOCK_SIZE_K * a_stride_c
116
+ at_ptrs += BLOCK_SIZE_K * a_stride_c
117
+
118
+ out_dtype = C_ptr.dtype.element_ty
119
+ output = accumulator.to(out_dtype)
120
+
121
+ # Store block of C
122
+ offs_cm = m_idx + tl.arange(0, BLOCK_SIZE_M)
123
+ offs_cn = n_idx + tl.arange(0, BLOCK_SIZE_N)
124
+ c_ptrs = C_ptr + (offs_cm[:, None] * c_stride_r + offs_cn[None, :] * c_stride_c)
125
+ c_mask = (offs_cm[:, None] < M) & (offs_cn[None, :] < M)
126
+ tl.store(c_ptrs, output, mask=c_mask)
127
+
128
+ # Store block of C mirrored across the diagonal
129
+ c_ptrs_t = C_ptr + (offs_cn[:, None] * c_stride_r + offs_cm[None, :] * c_stride_c)
130
+ c_mask_t = (offs_cn[:, None] < M) & (offs_cm[None, :] < M)
131
+ tl.store(c_ptrs_t, output.T, mask=c_mask_t)
132
+
133
+
134
+ def ns_line_1(A: Tensor, *, out: Tensor = None):
135
+ """
136
+ Launch Triton kernel to compute C = A @ A.T
137
+ """
138
+ if A.ndim > 3 or A.ndim < 2:
139
+ raise ValueError(f"Input tensor must be 2D or 3D, but got {A.ndim}D tensor.")
140
+
141
+ M, K = A.shape[-2:]
142
+
143
+ if out is None:
144
+ out = torch.empty((*A.shape[:-1], M), device=A.device, dtype=A.dtype)
145
+ assert out.size(-2) == M, "Output matrix has incorrect shape"
146
+ assert out.size(-1) == M, "Output matrix has incorrect shape"
147
+
148
+ batch_size = A.size(0) if A.ndim == 3 else 1
149
+ input_batch_stride = A.stride(0) if A.ndim == 3 else 0
150
+ output_batch_stride = out.stride(0) if out.ndim == 3 else 0
151
+
152
+ grid = lambda meta: (
153
+ batch_size
154
+ * triton.cdiv(M, meta["BLOCK_SIZE_M"])
155
+ * triton.cdiv(M, meta["BLOCK_SIZE_N"]),
156
+ )
157
+ ns_line_1_kernel[grid](
158
+ A_ptr=A,
159
+ C_ptr=out,
160
+ M=M,
161
+ K=K,
162
+ a_stride_b=input_batch_stride,
163
+ a_stride_r=A.stride(-2),
164
+ a_stride_c=A.stride(-1),
165
+ c_stride_b=output_batch_stride,
166
+ c_stride_r=out.stride(-2),
167
+ c_stride_c=out.stride(-1),
168
+ )
169
+
170
+ return out
171
+
172
+
173
+ @triton.autotune(
174
+ configs=_get_autotune_configs(),
175
+ key=["M", "a_stride_r", "a_stride_c", "c_stride_r", "c_stride_c"],
176
+ )
177
+ @triton.jit
178
+ def ns_line_2_kernel(
179
+ A_ptr,
180
+ C_ptr,
181
+ M,
182
+ a_stride_b,
183
+ a_stride_r,
184
+ a_stride_c,
185
+ c_stride_b,
186
+ c_stride_r,
187
+ c_stride_c,
188
+ alpha,
189
+ beta,
190
+ BLOCK_SIZE_M: tl.constexpr,
191
+ BLOCK_SIZE_N: tl.constexpr,
192
+ BLOCK_SIZE_K: tl.constexpr,
193
+ GROUP_SIZE_M: tl.constexpr,
194
+ LOWER_UPPER: tl.constexpr,
195
+ ):
196
+ """
197
+ Input A is square matrix with shape (M, M)
198
+ Output C has shape (M, M)
199
+ Compute C = alpha * A @ A.T + beta * A
200
+ """
201
+
202
+ pid = tl.program_id(axis=0)
203
+ batch_idx, m_idx, n_idx = _pid_to_block(
204
+ pid, M, BLOCK_SIZE_M, BLOCK_SIZE_N, GROUP_SIZE_M
205
+ )
206
+
207
+ # Skip blocks that don't need to be computed
208
+ skip_block_below_diag = (LOWER_UPPER == 0) and (n_idx + BLOCK_SIZE_N <= m_idx)
209
+ skip_block_above_diag = (LOWER_UPPER != 0) and (m_idx + BLOCK_SIZE_M <= n_idx)
210
+ if skip_block_below_diag or skip_block_above_diag:
211
+ return
212
+
213
+ # Index into one matrix of batch
214
+ A_ptr += batch_idx * a_stride_b
215
+ C_ptr += batch_idx * c_stride_b
216
+
217
+ # Create pointer arrays for A and A.T
218
+ offs_m = (m_idx + tl.arange(0, BLOCK_SIZE_M)) % M
219
+ offs_n = (n_idx + tl.arange(0, BLOCK_SIZE_N)) % M
220
+ offs_k = tl.arange(0, BLOCK_SIZE_K)
221
+ a_ptrs = A_ptr + (offs_m[:, None] * a_stride_r + offs_k[None, :] * a_stride_c)
222
+ at_ptrs = A_ptr + (offs_k[:, None] * a_stride_c + offs_n[None, :] * a_stride_r)
223
+
224
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
225
+
226
+ # Accumulate over blocks of K
227
+ for k in tl.range(0, tl.cdiv(M, BLOCK_SIZE_K)):
228
+ a = tl.load(a_ptrs, mask=offs_k[None, :] < M - k * BLOCK_SIZE_K, other=0.0)
229
+ at = tl.load(at_ptrs, mask=offs_k[:, None] < M - k * BLOCK_SIZE_K, other=0.0)
230
+ accumulator = tl.dot(a, at, accumulator)
231
+ a_ptrs += BLOCK_SIZE_K * a_stride_c
232
+ at_ptrs += BLOCK_SIZE_K * a_stride_c
233
+
234
+ # Load block of A to add (corresponds to the current block of C)
235
+ offs_am = m_idx + tl.arange(0, BLOCK_SIZE_M)
236
+ offs_an = n_idx + tl.arange(0, BLOCK_SIZE_N)
237
+ a_add_ptrs = A_ptr + (offs_am[:, None] * a_stride_r + offs_an[None, :] * a_stride_c)
238
+ a_add_mask = (offs_am[:, None] < M) & (offs_an[None, :] < M)
239
+ a_add = tl.load(a_add_ptrs, mask=a_add_mask, other=0.0).to(tl.float32)
240
+
241
+ # Apply alpha and beta
242
+ accumulator *= alpha
243
+ accumulator += a_add * beta
244
+
245
+ out_dtype = C_ptr.dtype.element_ty
246
+ output = accumulator.to(out_dtype)
247
+
248
+ # Store block of C
249
+ offs_cm = m_idx + tl.arange(0, BLOCK_SIZE_M)
250
+ offs_cn = n_idx + tl.arange(0, BLOCK_SIZE_N)
251
+ c_ptrs = C_ptr + (offs_cm[:, None] * c_stride_r + offs_cn[None, :] * c_stride_c)
252
+ c_mask = (offs_cm[:, None] < M) & (offs_cn[None, :] < M)
253
+ tl.store(c_ptrs, output, mask=c_mask)
254
+
255
+ # Store block of C mirrored across the diagonal
256
+ c_ptrs_t = C_ptr + (offs_cn[:, None] * c_stride_r + offs_cm[None, :] * c_stride_c)
257
+ c_mask_t = (offs_cn[:, None] < M) & (offs_cm[None, :] < M)
258
+ tl.store(c_ptrs_t, output.T, mask=c_mask_t)
259
+
260
+
261
+ def ns_line_2(A: Tensor, alpha: float, beta: float, *, out: Tensor = None):
262
+ """
263
+ Launch Triton kernel to compute C = alpha * A @ A.T + beta * A
264
+ """
265
+ if A.ndim > 3 or A.ndim < 2:
266
+ raise ValueError(f"Input tensor must be 2D or 3D, but got {A.ndim}D tensor.")
267
+
268
+ M, K = A.shape[-2:]
269
+ if M != K:
270
+ raise ValueError(
271
+ f"Input must be symmetric square matrix, but got shape {A.shape}"
272
+ )
273
+
274
+ if out is None:
275
+ out = torch.empty((*A.shape[:-1], M), device=A.device, dtype=A.dtype)
276
+ assert out.size(-2) == M, "Output matrix has incorrect shape"
277
+ assert out.size(-1) == M, "Output matrix has incorrect shape"
278
+
279
+ batch_size = A.size(0) if A.ndim == 3 else 1
280
+ input_batch_stride = A.stride(0) if A.ndim == 3 else 0
281
+ output_batch_stride = out.stride(0) if out.ndim == 3 else 0
282
+
283
+ grid = lambda meta: (
284
+ batch_size
285
+ * triton.cdiv(M, meta["BLOCK_SIZE_M"])
286
+ * triton.cdiv(M, meta["BLOCK_SIZE_N"]),
287
+ )
288
+ ns_line_2_kernel[grid](
289
+ A_ptr=A,
290
+ C_ptr=out,
291
+ M=M,
292
+ a_stride_b=input_batch_stride,
293
+ a_stride_r=A.stride(-2),
294
+ a_stride_c=A.stride(-1),
295
+ c_stride_b=output_batch_stride,
296
+ c_stride_r=out.stride(-2),
297
+ c_stride_c=out.stride(-1),
298
+ alpha=alpha,
299
+ beta=beta,
300
+ )
301
+
302
+ return out
303
+
304
+
305
+ @torch.compile(dynamic=False, fullgraph=True)
306
+ def zeropower_via_newtonschulz5(G: Tensor, epsilon: float = 1e-7):
307
+ """
308
+ Reference implementation of Newton-Schulz without Triton.
309
+ """
310
+ # Newton-Schulz constants
311
+ ns_consts = [
312
+ (4.0848, -6.8946, 2.9270),
313
+ (3.9505, -6.3029, 2.6377),
314
+ (3.7418, -5.5913, 2.3037),
315
+ (2.8769, -3.1427, 1.2046),
316
+ (2.8366, -3.0525, 1.2012),
317
+ ]
318
+
319
+ X = G.to(dtype=torch.bfloat16)
320
+ if G.size(-2) > G.size(-1):
321
+ X = X.mT
322
+
323
+ # Ensure spectral norm is at most 1
324
+ X = X / (X.norm(dim=(-2, -1), keepdim=True) + epsilon)
325
+
326
+ for a, b, c in ns_consts:
327
+ A = X @ X.mT
328
+ B = b * A + c * (A @ A)
329
+ X = a * X + B @ X
330
+
331
+ if G.size(-2) > G.size(-1):
332
+ X = X.mT
333
+ return X
334
+
335
+
336
+ @torch.compile(dynamic=False, fullgraph=True)
337
+ def newton_schulz_triton(G: Tensor, epsilon: float = 1e-7):
338
+ """
339
+ Triton implementation of Newton-Schulz iteration
340
+ """
341
+ # Newton-Schulz constants
342
+ ns_consts = [
343
+ (4.0848, -6.8946, 2.9270),
344
+ (3.9505, -6.3029, 2.6377),
345
+ (3.7418, -5.5913, 2.3037),
346
+ (2.8769, -3.1427, 1.2046),
347
+ (2.8366, -3.0525, 1.2012),
348
+ ]
349
+
350
+ X = G.to(dtype=torch.bfloat16)
351
+ if G.size(-2) > G.size(-1):
352
+ X = X.mT
353
+
354
+ # Ensure spectral norm is at most 1
355
+ X = X / (X.norm(dim=(-2, -1), keepdim=True) + epsilon)
356
+
357
+ # Allocate buffers
358
+ X = X.contiguous()
359
+ A = torch.empty((*X.shape[:-1], X.size(-2)), device=X.device, dtype=X.dtype)
360
+ B = torch.empty_like(A)
361
+ C = torch.empty_like(X)
362
+
363
+ ns_line_3 = torch.baddbmm if X.ndim > 2 else torch.addmm
364
+
365
+ # Perform the NS iterations
366
+ for a, b, c in ns_consts:
367
+ ns_line_1(X, out=A) # A = X @ X.mT
368
+ ns_line_2(A, alpha=c, beta=b, out=B) # B = b * A + c * A @ A
369
+ ns_line_3(X, B, X, beta=a, out=C) # C = a * X + B @ X
370
+ X, C = C, X # Swap references to avoid unnecessary copies
371
+
372
+ if G.size(-2) > G.size(-1):
373
+ X = X.mT
374
+ return X