Muon · Newton–Schulz

Five matrix multiplies that turn a gradient into a rotation

Muon's entire trick is replacing the momentum matrix with the nearest semi-orthogonal matrix. Newton–Schulz is how you do that without ever computing an SVD. Here is the whole thing, from where the name comes from down to why the coefficients are 3.4445, −4.7750 and 2.0315.

01What we are actually computing

Muon is SGD with momentum, plus one extra operation. For a 2-D weight matrix W:

Mt = μMt−1 + Gt
Wt = Wt−1 η · ortho(Mt)
Everything interesting lives inside ortho.

If the momentum matrix has the singular value decomposition M = UΣV, then

ortho(M) = UV
Throw away Σ. Keep only the rotation part.

That is it. Every singular value gets set to 1; the singular directions are untouched. This is the orthogonal factor of the polar decomposition M = QP, and it is also the closest semi-orthogonal matrix to M in Frobenius norm.

Why you would want that

Two ways to see it. The geometric one: a real momentum matrix has singular values spread over orders of magnitude, so a raw SGD step moves the network almost entirely along two or three dominant directions and barely touches the rest. Orthogonalizing gives every direction the same step size.

The clean one: UV is exactly the steepest-descent direction when you measure step size in the spectral norm. Among all updates with ‖ΔW2 ≤ 1, the one most aligned with the gradient is UV:

G, ΔW= Σi σi uiΔW vi Σi σi
with equality precisely at ΔW = UV. Muon is spectral-norm steepest descent.

Drag the slider below to morph a 2×2 matrix into its orthogonalized version. The faint dots are the input unit circle; the bright dots are their images. Orthogonalizing squashes the ellipse back into a circle without spinning the dots around it.

Fig 1 · ortho() in two dimensions
G UV

02Where the name comes from

Newton's method can be aimed at the polar factor directly. The iteration is

Xk+1 = ½ ( Xk + Xk−⊤ )
Quadratically convergent — and useless on a GPU, because of that inverse.

Inverting a matrix every step, in low precision, for every weight matrix in the network, is a non-starter. This is where Schulz enters. Note that

X−⊤ = X (XX)−1

and that near the answer XX is close to the identity, so its inverse can be approximated by the first two terms of a Neumann series — the trick Schulz published in 1933 for inverting matrices iteratively:

(XX)−1 2I XX

Substitute that into Newton's step and the inverse vanishes:

Xk+1 = ½ ( Xk + Xk(2I XkXk) ) = 1.5 Xk 0.5 XkXkXk
The Newton–Schulz iteration. Nothing but matrix multiplication and scaling.

Newton gives the iteration; Schulz makes it inverse-free. That is the whole naming story, and it is also the whole reason it belongs in a training loop: matmuls are the one thing a GPU is spectacularly good at.

03The one fact that makes it all work

Here is the piece that usually clicks last, and it is the piece worth internalizing. Write X = UΣV and expand the cubic term, using VV = I and UU = I:

XXX = (UΣV)(VΣU)(UΣV) = UΣ³V

So one Newton–Schulz step gives

Xk+1 = 1.5 UΣV 0.5 UΣ³V = U (1.5Σ 0.5Σ³) V
Same U. Same V. Only the diagonal moved.
The key insight

The matrix iteration is a scalar iteration in disguise. It never rotates the singular vectors — it runs the exact same one-dimensional map on every singular value at once, independently, in parallel.

So the question "does this converge to UV?" collapses into the much easier question "does the number σ go to 1?"

This holds for any odd polynomial built from XX, because (XX)jX = UΣ2j+1V. That freedom is exactly what Muon exploits in §5.

04So — does σ go to 1?

The scalar map is

f(s) = 1.5s 0.5s³

Its fixed points solve f(s) = s, i.e. s(1 − s²) = 0, so s ∈ {0, 1} for positive values. On (0, 1) we have f(s) > s and f(s) < 1, so the sequence climbs monotonically to 1. Near the top, set s = 1 − ε:

f(1 ε) = 1 1.5ε² + 0.5ε³
The error squares every step. Textbook quadratic convergence.

The basin is (0, √3). Past √3 the map goes negative and blows up. Which explains a line in the code that otherwise looks arbitrary:

X0 = G / ‖GF
Because σmax ≤ ‖GF always, this drops every singular value into (0, 1] — safely inside the basin, for the price of one norm.

Reading the cobweb

A cobweb diagram makes a scalar iteration visible. From the current value on the horizontal axis, go vertically to the curve (that applies the map), then horizontally to the diagonal (that feeds the output back in as the next input). Repeat. The staircase is the iteration.

Fig 2 · cobweb of the scalar map
σ₀

Set σ₀ near 1 and the classic cubic snaps home in two or three steps. Now drag it down toward 0.05 and watch the staircase turn into a crawl. That is the whole problem.

05Why Muon changes the numbers

Near zero, f(s) ≈ 1.5s. Small singular values grow by a factor of 1.5 per step, and nothing faster. Real momentum matrices are badly conditioned; after Frobenius normalization the smallest singular value can easily sit around 10−3. Getting from there to 1 at 1.5× per step takes on the order of seventeen iterations — every optimizer step, on every 2-D parameter. Far too expensive.

Muon's answer is to give up on exactness. Use a quintic, and hand-tune the coefficients for speed rather than for correctness:

g(s) = 3.4445s 4.7750s³ + 2.0315s
Matrix form: aX + bXXX + c(XXX

The slope at the origin is now 3.4445 instead of 1.5 — small singular values are hauled upward more than twice as fast per step. The price is real, and worth stating plainly:

The trade

Muon's polynomial does not converge to 1. In fact g(1) = 3.4445 − 4.7750 + 2.0315 = 0.7010, so 1 is not even a fixed point. Both of its positive fixed points (near 0.868 and 1.264) are repelling.

Instead the iteration overshoots, undershoots, and settles into a small oscillation in a band of roughly 0.7 to 1.2 around 1. The output is not orthogonal — it is approximately orthogonal, to within about ±30%.

Empirically, training does not care. What matters is that no direction is starved, not that every singular value equals exactly 1. So Muon buys a 3× speedup at the low end and pays for it with accuracy it did not need.

Flip the toggle in Fig 2 back to quintic and set σ₀ near 1: you can watch the staircase refuse to settle. Here is the same starting spectrum run through both maps, on a log axis, so that constant multiplicative growth appears as constant vertical movement:

Fig 3 · the same 26 singular values, five steps, two polynomials
1.5s − 0.5s³  · classic
3.4445s − 4.775s³ + 2.0315s⁵ · Muon

The shaded band is 0.7 to 1.3 — close enough to orthogonal for Muon's purposes. After five steps the classic iteration has landed its top handful of values and left the tail in the basement. The quintic has everything in the band.

Concretely, here is how many iterations each map needs to drag a singular value up to 0.7:

06The code, annotated

def zeropower_via_newtonschulz5(G, steps=5, eps=1e-7):
    a, b, c = 3.4445, -4.7750, 2.0315
    X = G.bfloat16()                     # 1
    X = X / (X.norm() + eps)             # 2
    transposed = G.size(0) > G.size(1)
    if transposed:
        X = X.T                          # 3
    for _ in range(steps):
        A = X @ X.T                      # 4  matmul 1
        B = b * A + c * (A @ A)           #    matmul 2
        X = a * X + B @ X                 #    matmul 3
    if transposed:
        X = X.T
    return X

1 — bfloat16. The output is deliberately approximate to within ±30% anyway, so roughly three significant digits is plenty. Halves the memory traffic and lands the whole thing on tensor cores.

2 — Frobenius normalization. The basin guard from §4. Since σmax ≤ ‖GF, this puts every singular value inside (0, 1]. The eps covers a zero gradient.

3 — The transpose. The iteration only ever forms XX, so you want the short side on the outside. Flipping a tall matrix to wide means the Gram matrix is m × m with m the smaller dimension. For a 4096×1024 weight that is a 1024² product instead of a 4096² one.

4 — The factoring. Written naively, aX + bXXX + c(XXX costs four matmuls. Setting A = XX and pulling out X on the right gives aX + (bA + cA²)X — three. Combined with the transpose, that took the cost from 6nm² to 4nm² + 2m³ FLOPs.

Fifteen matmuls total on a matrix the size of one weight tensor. Against a forward and backward pass over a full batch, it disappears — reported overhead is under 1% of training time.

07Watch it run on a real matrix

Below is an actual 12×12 matrix constructed with singular values log-spaced from 1 down to 0.01, then Frobenius-normalized — a plausible stand-in for a badly conditioned momentum buffer. The left panel is X itself; the right is XX, which converges to the identity exactly when X becomes orthogonal. Step through with each polynomial and compare.

Fig 4 · a 12×12 matrix, five real iterations
X
X X  →  I

Watch the diagonal of the right panel light up. With the cubic, five steps illuminate only the corner corresponding to the large singular values, and the rest of the diagonal stays dark — those directions are still being suppressed. With the quintic the whole diagonal comes up together, unevenly, hovering around 1 rather than pinned at it. That unevenness is the ±30% band, and it is what Muon ships.

08Fine print

Primary source: Keller Jordan's Muon: An optimizer for hidden layers in neural networks (December 2024), and Bernstein & Newhouse, Old Optimizer, New Norm: An Anthology, which is where the spectral-norm framing comes from. Both go further than this page does.