dtensor-muon
A distributed-ready implementation of the Muon optimizer built on PyTorch
DTensor. It runs the
orthogonalization step efficiently across sharded parameters (FSDP / tensor-parallel meshes)
and falls back to Adam/AdamW for the parameters Muon doesn't apply to — all from a single
optimizer instance.
Unlike the original distributed implementation, when used with FSDP2, there is zero communication overhead as the optimizer can run orthogonalization directly over the local sharded parameters.
What is Muon?
Muon updates 2D+ parameters by taking the momentum-smoothed gradient and replacing it with its nearest semi-orthogonal matrix (the "zero-power" or matrix-sign of the gradient) before applying it. The orthogonalization is computed iteratively so it stays cheap on the GPU. This implementation provides two iteration schemes:
newton_schulz— the classic Newton-Schulz iteration, with a fused Triton Gram-matrix kernel.polar_express— the Polar Express scheme (arXiv:2505.16932), which uses precomputed coefficients for faster convergence.
Features
- DTensor / distributed first — orthogonalization is run across the device mesh, with a dedicated fast path for parameters sharded only along dim 0 (FSDP).
- Unified Muon + Adam — one optimizer handles both. Parameters Muon can't update (1D tensors, embeddings, the LM head, etc.) are routed to a fused Adam/AdamW path via per-group configuration.
- Three variants:
Muon— the reference per-parameter implementation.MuonForeach— batchedforeachoperations for higher throughput.MuonLP— Experimental quantized (4-bit / 8-bit / fp8) optimizer states viatorchaofor reduced memory.
- Cautious weight decay — an extension beyond the original Muon (which has no cautious
variant): weight decay is applied only where the update and parameter share a sign
(
u * p > 0), following the Cautious Optimizers technique. On by default; setuse_cautious_wd=Falsefor plain (non-cautious) decoupled weight decay. - Nesterov momentum, automatic shape-based learning-rate scaling, optional
torch.compile, and Triton kernels.
Requirements
- Python ≥ 3.12
- PyTorch ≥ 2.12.1
- Triton (for the fused Newton-Schulz kernel)
torchao(optional, only forMuonLP)
Installation
# Install from PyPI
uv pip install dtensor-muon
# Install it directly from the repository:
uv pip install git+https://github.com/tonyf/dtensor-muon.git
# include torchao for the low-precision optimizer (MuonLP)
uv pip install "dtensor-muon[lp] @ git+https://github.com/tonyf/dtensor-muon.git"
Development
Clone the repository and sync the environment:
git clone https://github.com/tonyf/dtensor-muon.git
cd dtensor-muon
uv venv
source .venv/bin/activate
uv sync # core install
uv sync --extra lp # include torchao for the low-precision optimizer
Usage
Muon is applied to 2D+ weight matrices, while norms, biases, and embeddings are typically
left to Adam. Configure this with param groups using the "algorithm" key:
import torch
from dtensor_muon import Muon
model = ...
muon_params = [p for n, p in model.named_parameters() if p.ndim >= 2 and "embed" not in n]
adam_params = [p for n, p in model.named_parameters() if p.ndim < 2 or "embed" in n]
optimizer = Muon(
[
{"params": muon_params}, # "muon" is the default
{"params": adam_params, "algorithm": "adamw"},
],
lr=1e-3,
wd=0.1,
)
# standard training loop
for batch in dataloader:
loss = model(batch).loss
loss.backward()
optimizer.step()
optimizer.zero_grad()
Choosing a variant
from dtensor_muon import Muon, MuonForeach, MuonLP # MuonLP requires the `lp` extra
MuonForeach and MuonLP are drop-in replacements with the same constructor. Use
MuonForeach for throughput and MuonLP to shrink optimizer-state memory.
Selecting an orthogonalization strategy
optimizer = Muon(params, orthogonalization_strategy="polar_express") # or "newton_schulz"
Key options
| Option | Default | Description |
|---|---|---|
lr |
1e-3 |
Learning rate. |
wd |
0.1 |
Weight decay. |
use_cautious_wd |
True |
Cautious weight decay — apply decay only where update and param share a sign (u * p > 0). An addition not present in the original Muon; set False for plain weight decay. |
momentum |
0.95 |
Muon momentum. |
nesterov |
True |
Use Nesterov momentum. |
ns_steps |
5 |
Orthogonalization iteration steps. |
orthogonalization_strategy |
"polar_express" |
"newton_schulz" or "polar_express". |
adam_betas |
(0.9, 0.95) |
Betas for the Adam path. |
is_adamw |
True |
Decoupled (AdamW) vs. coupled weight decay for the Adam path. |
fused_adam |
True |
Use the fused Adam kernel. |
compile |
False |
torch.compile the per-parameter step. |
Most options can also be overridden per param group.
Benchmarks
A self-contained benchmark suite lives in benchmark/. Run the whole thing
(kernels, single-device optimizers, and the distributed orthogonalization paths) with:
uv run python benchmark/run.py # full run, writes benchmark/RESULTS.md
uv run python benchmark/run.py --quick # small shapes, fast smoke
It degrades gracefully: CUDA-only sections are skipped on a CPU-only host, and the
distributed section uses NCCL/CUDA when ≥2 GPUs are visible and falls back to gloo/CPU
otherwise. Full numbers and methodology are in benchmark/RESULTS.md.
The snapshot below was measured on 2× NVIDIA RTX PRO 6000 Blackwell, PyTorch 2.12.1+cu130.
Triton Gram kernel vs x @ x.mT — a clear win, 1.5–1.9× across shapes/dtypes
(e.g. (32, 2048, 1024) bf16: 1.13 ms → 0.61 ms, 1.85×).
Orthogonalization loops (5 steps, bf16) — the fused Triton loop is at parity with
or slightly faster than the torch.compiled PyTorch loop (≈0.96–1.08×), reflecting the
Gram-kernel win net of the rest of the iteration (the non-symmetric B @ X matmul is a
plain matmul in both). The uncompiled eager loop is ~1.3× slower than either.
Single-device optimizer step (48 weight matrices, full step()), vs a naive
reference Muon:
| optimizer | step (ms) | vs naive |
|---|---|---|
| naive (Newton-Schulz) | 158 | 1.00× (ref) |
Muon |
260–280 | 0.56–0.61× |
MuonForeach |
257–270 | 0.59–0.62× |
On this Blackwell card the naive eager loop is still faster than the torch.compile +
Triton paths at these shapes (native bf16 matmul is very fast, so per-step() Python and
compile overhead dominate); MuonForeach's batching roughly matches per-parameter Muon.
(MuonLP targets optimizer-state memory, not step time, so it isn't in this table.)
Distributed orthogonalization (2× GPU, NCCL, params sharded on dim 0):
| path | per-call (ms) | vs single-device |
|---|---|---|
| single-device (replicated, no collectives) | 42.6 | 1.00× (ref) |
FSDP fast path (foreach_zeropower_3d_fsdp) |
20.4 | 2.09× |
general path (foreach_zeropower + redistribute) |
33.2 | 1.28× |
The FSDP fast path works on each rank's local shard with no collectives — ~2.1× the single-device baseline (batch split across ranks) and ~1.6× faster than the general redistribute path. This is the core payoff of the DTensor design.
Project layout
src/dtensor_muon/
├── optim/ # Muon, MuonForeach, MuonLP optimizers
├── orthogonalize/ # zeropower dispatch, Newton-Schulz & Polar Express, DTensor handling
├── kernels/ # Triton Gram-matrix kernel
└── utils/ # DTensor and foreach helpers
Testing and linting
uv run pytest # run the test suite
uv run ruff check # lint
uv run ty # type check
Metadata
Release files for dtensor-muon 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dtensor_muon-0.1.0.tar.gz | 16.5 kB | With provenance | Details for dtensor_muon-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dtensor_muon-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for dtensor_muon-0.1.0-py3-none-any.whl |
Total release size: 39.7 kB
Release files / dtensor_muon-0.1.0.tar.gz
| Download URL | dtensor_muon-0.1.0.tar.gz |
|---|---|
| Size | 16.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
05b1c83cde3f1cb356f1d6d7d64f4bd5f2b642ebbfe47eec8de838e1c5099a33
|
|
BLAKE2b-256 checksum How to use checksums |
30912350e44186d59bb05968b87382064d5b77d6580e5a4fe70fb40693bf5808
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 23, 2026.
Transparency logRelease files / dtensor_muon-0.1.0-py3-none-any.whl
| Download URL | dtensor_muon-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12d601ba2f72dc5f3cfb09d2a84d2cd386de732a3804562fa6ec66b7dc7a778c
|
|
BLAKE2b-256 checksum How to use checksums |
37347cdf56eae5fddaba37b0cf1df9a81474ee5bc508b2c60eceef2d1dbd41c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 23, 2026.
Transparency logSecurity for dtensor-muon 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for dtensor_muon-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.