Instructions to use Motif-Technologies/optimizer with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Kernels
How to use Motif-Technologies/optimizer with Kernels:
# !pip install kernels from kernels import get_kernel # a version (or an explicit revision) is required; see the "Files and versions" tab for the available ones kernel = get_kernel("Motif-Technologies/optimizer", version=1) - Notebooks
- Google Colab
- Kaggle
Commit ·
573242f
1
Parent(s): 33929c0
Replace hardcoded NS coefficients with analytically optimal ones [skip-build]
Browse filesReplaces the five fixed (a, b, c) quintic coefficients in
`_zeropower_via_newtonschulz5` with per-step optimal coefficients computed by
`_optimal_composition` / `_optimal_quintic` via the equioscillation (Chebyshev)
criterion. The precomputed `_coeffs_list` (10 iters, l=1e-3, safety_factor_eps=1e-2,
cushion=0.02) minimizes the worst-case deviation from 1 over the singular-value
interval, and the function now supports arbitrary `steps` by repeating the last
coefficient set. Docstrings updated accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
torch-ext/optimizer/newton_schulz.py
CHANGED
|
@@ -1,26 +1,106 @@
|
|
| 1 |
import torch
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from .matmul_transpose_triton import matmul_transpose_assign
|
|
|
|
| 4 |
|
| 5 |
COMM_DTYPE = torch.bfloat16
|
| 6 |
DEFAULT_CHUNK_SIZE_RATIO = 4
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
@torch.no_grad()
|
| 14 |
-
# matmul_transpose_assign from : https://github.com/nil0x9/flash-muon
|
| 15 |
def _zeropower_via_newtonschulz5(G, steps):
|
| 16 |
"""
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
"""
|
| 25 |
assert len(G.shape) == 2
|
| 26 |
assert G.dtype == COMM_DTYPE
|
|
@@ -28,18 +108,14 @@ def _zeropower_via_newtonschulz5(G, steps):
|
|
| 28 |
|
| 29 |
if G.size(0) > G.size(1):
|
| 30 |
X = X.T
|
| 31 |
-
|
| 32 |
X = X / (X.norm() + 1e-7)
|
|
|
|
|
|
|
| 33 |
buf1 = torch.empty(X.size(0), X.size(0), dtype=X.dtype, device=X.device)
|
| 34 |
buf2 = torch.empty(X.size(0), X.size(0), dtype=X.dtype, device=X.device)
|
| 35 |
# Perform the NS iterations
|
| 36 |
-
for a, b, c in
|
| 37 |
-
(4.0848, -6.8946, 2.9270),
|
| 38 |
-
(3.9505, -6.3029, 2.6377),
|
| 39 |
-
(3.7418, -5.5913, 2.3037),
|
| 40 |
-
(2.8769, -3.1427, 1.2046),
|
| 41 |
-
(2.8366, -3.0525, 1.2012),
|
| 42 |
-
]:
|
| 43 |
matmul_transpose_assign(X, buf1)
|
| 44 |
matmul_transpose_assign(buf1, buf2)
|
| 45 |
buf1.mul_(b).add_(buf2, alpha=c)
|
|
@@ -47,4 +123,5 @@ def _zeropower_via_newtonschulz5(G, steps):
|
|
| 47 |
|
| 48 |
if G.size(0) > G.size(1):
|
| 49 |
X = X.T
|
|
|
|
| 50 |
return X
|
|
|
|
| 1 |
import torch
|
| 2 |
+
from itertools import repeat
|
| 3 |
+
from math import inf, sqrt
|
| 4 |
+
import numpy as np
|
| 5 |
|
| 6 |
from .matmul_transpose_triton import matmul_transpose_assign
|
| 7 |
+
from loguru import logger
|
| 8 |
|
| 9 |
COMM_DTYPE = torch.bfloat16
|
| 10 |
DEFAULT_CHUNK_SIZE_RATIO = 4
|
| 11 |
|
| 12 |
+
def _optimal_quintic(l, u):
|
| 13 |
+
"""
|
| 14 |
+
Use the simplified Remez algorithm to find the optimal odd quintic approximant
|
| 15 |
+
to the constant function x -> 1 over the interval [l, u].
|
| 16 |
+
|
| 17 |
+
Returns (a, b, c) for p(x) = ax + bx^3 + cx^5 that minimizes the maximum
|
| 18 |
+
approximation error max_{x in [l,u]} |p(x) - 1|. Iterates by updating the
|
| 19 |
+
two interior equioscillation nodes q, r until convergence. Returns the
|
| 20 |
+
closed-form equioscillating solution when l ≈ u.
|
| 21 |
+
"""
|
| 22 |
+
assert 0 <= l <= u
|
| 23 |
+
if 1 - 5e-6 <= l / u:
|
| 24 |
+
return (15/8)/u, (-10/8)/(u**3), (3/8)/(u**5)
|
| 25 |
+
q = (3*l + u) / 4
|
| 26 |
+
r = (l + 3*u) / 4
|
| 27 |
+
E, old_E = inf, None
|
| 28 |
+
while not old_E or abs(old_E - E) > 1e-15:
|
| 29 |
+
old_E = E
|
| 30 |
+
LHS = np.array([
|
| 31 |
+
[l, l**3, l**5, 1],
|
| 32 |
+
[q, q**3, q**5, -1],
|
| 33 |
+
[r, r**3, r**5, 1],
|
| 34 |
+
[u, u**3, u**5, -1],
|
| 35 |
+
])
|
| 36 |
+
a, b, c, E = np.linalg.solve(LHS, np.ones(4))
|
| 37 |
+
q, r = np.sqrt((-3*b + np.array([-1, 1]) *
|
| 38 |
+
sqrt(9*b**2 - 20*a*c)) / (10*c))
|
| 39 |
+
return float(a), float(b), float(c)
|
| 40 |
|
| 41 |
+
|
| 42 |
+
def _optimal_composition(l, num_iters, safety_factor_eps=0, cushion=0):
|
| 43 |
+
"""
|
| 44 |
+
Compute the Polar Express coefficient series for `num_iters` quintic iterations.
|
| 45 |
+
|
| 46 |
+
Builds a sequence of per-step optimal odd quintic coefficients (a, b, c) that
|
| 47 |
+
compose to map singular values from [l, 1] toward 1. At each step:
|
| 48 |
+
1. Solves `_optimal_quintic` on [max(l, cushion*u), u]. The `cushion`
|
| 49 |
+
prevents near-zero singular values from stalling by raising the effective
|
| 50 |
+
lower bound; if it is active (cushion*u > l), the coefficients are
|
| 51 |
+
rescaled so that p(l) and p(u) are centered around 1 w.r.t. the true [l, u].
|
| 52 |
+
2. Deflates the coefficients by (1 + safety_factor_eps)^degree for all but the
|
| 53 |
+
last iteration, providing numerical headroom at the cost of a slightly slower
|
| 54 |
+
final convergence step.
|
| 55 |
+
3. Advances the interval: l <- p(l), u <- 2 - p(l) (by symmetry of p around 1).
|
| 56 |
+
|
| 57 |
+
Returns a list of (a, b, c) tuples, one per iteration.
|
| 58 |
+
|
| 59 |
+
Reference: Amsel et al., "The Polar Express: Optimal Matrix Sign Methods and
|
| 60 |
+
Their Application to the Muon Algorithm", https://arxiv.org/abs/2505.16932
|
| 61 |
+
"""
|
| 62 |
+
u = 1
|
| 63 |
+
assert 0 <= l <= u
|
| 64 |
+
safety_factor = 1 + safety_factor_eps
|
| 65 |
+
coefficients = []
|
| 66 |
+
for iter in range(num_iters):
|
| 67 |
+
a, b, c = _optimal_quintic(max(l, cushion*u), u)
|
| 68 |
+
if cushion*u > l:
|
| 69 |
+
pl = a*l + b*l**3 + c*l**5
|
| 70 |
+
pu = a*u + b*u**3 + c*u**5
|
| 71 |
+
rescaler = 2/(pl + pu)
|
| 72 |
+
a *= rescaler; b *= rescaler; c *= rescaler
|
| 73 |
+
if iter < num_iters - 1:
|
| 74 |
+
a /= safety_factor; b /= safety_factor**3; c /= safety_factor**5
|
| 75 |
+
coefficients.append((a, b, c))
|
| 76 |
+
l = a*l + b*l**3 + c*l**5
|
| 77 |
+
u = 2 - l
|
| 78 |
+
return coefficients
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
_coeffs_list = _optimal_composition(l=1e-3, num_iters=10, safety_factor_eps=1e-2, cushion=0.02)
|
| 82 |
+
|
| 83 |
+
# This code is adapted from:
|
| 84 |
+
# KellerJordan/Muon (https://github.com/KellerJordan/Muon/blob/master/muon.py)
|
| 85 |
+
# NoahAmsel/PolarExpress (https://github.com/NoahAmsel/PolarExpress)
|
| 86 |
+
# matmul_transpose_assign kernel from nil0x9/flash-muon (https://github.com/nil0x9/flash-muon)
|
| 87 |
@torch.no_grad()
|
|
|
|
| 88 |
def _zeropower_via_newtonschulz5(G, steps):
|
| 89 |
"""
|
| 90 |
+
Compute the polar factor of G via the Polar Express method.
|
| 91 |
+
|
| 92 |
+
Applies `steps` quintic iterations X <- aX + bX^3 + cX^5, where (a, b, c)
|
| 93 |
+
are the Polar Express coefficients from `_coeffs_list`. Each step is the
|
| 94 |
+
optimal odd quintic approximant to x -> 1 over the current singular-value
|
| 95 |
+
interval, minimizing the maximum approximation error (Remez / minimax criterion).
|
| 96 |
+
The composition maps singular values from [l, 1] to near 1, producing the
|
| 97 |
+
polar factor (orthogonal factor in the polar decomposition G = UP).
|
| 98 |
+
|
| 99 |
+
`_coeffs_list` is precomputed for 10 iterations (l=1e-3, safety_factor_eps=1e-2,
|
| 100 |
+
cushion=0.02). If `steps` exceeds 10, the final coefficient set is repeated.
|
| 101 |
+
|
| 102 |
+
Reference: Amsel et al., "The Polar Express: Optimal Matrix Sign Methods and
|
| 103 |
+
Their Application to the Muon Algorithm", https://arxiv.org/abs/2505.16932
|
| 104 |
"""
|
| 105 |
assert len(G.shape) == 2
|
| 106 |
assert G.dtype == COMM_DTYPE
|
|
|
|
| 108 |
|
| 109 |
if G.size(0) > G.size(1):
|
| 110 |
X = X.T
|
| 111 |
+
|
| 112 |
X = X / (X.norm() + 1e-7)
|
| 113 |
+
hs = _coeffs_list[:steps] + list(
|
| 114 |
+
repeat(_coeffs_list[-1], steps - len(_coeffs_list)))
|
| 115 |
buf1 = torch.empty(X.size(0), X.size(0), dtype=X.dtype, device=X.device)
|
| 116 |
buf2 = torch.empty(X.size(0), X.size(0), dtype=X.dtype, device=X.device)
|
| 117 |
# Perform the NS iterations
|
| 118 |
+
for a, b, c in hs:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
matmul_transpose_assign(X, buf1)
|
| 120 |
matmul_transpose_assign(buf1, buf2)
|
| 121 |
buf1.mul_(b).add_(buf2, alpha=c)
|
|
|
|
| 123 |
|
| 124 |
if G.size(0) > G.size(1):
|
| 125 |
X = X.T
|
| 126 |
+
|
| 127 |
return X
|