Pure DMuon Routing¶
Pure DMuon means every trainable parameter can enter DMuon's ownership runtime instead of leaving non-Muon parameters on the ordinary FSDP2/AdamW path. This mode is useful when a training stack wants one runtime to control parameter placement, gradient movement, and optimizer stepping for the whole model. The important point is that optimizer math and communication placement are separate decisions.
predicate decides whether a parameter is DMuon-managed. param_policy decides what DMuon does with that managed parameter. The route selects optimizer math and communication placement:
| Route | Optimizer math | Forward parameter movement | Backward gradient movement | Typical parameters |
|---|---|---|---|---|
"muon" |
Muon + Newton-Schulz | owner broadcast |
reduce to owner |
attention and MLP projection matrices |
"adamw" |
AdamW | owner broadcast |
reduce to owner |
LayerNorm, bias, small AdamW parameters |
"sharded_adamw" |
AdamW | all_gather from parameter shards |
reduce_scatter to optimizer shards |
very large AdamW parameters such as embeddings and lm_head |
The "adamw" and "sharded_adamw" routes both run AdamW. They differ only in how the parameter and gradient are distributed. Small AdamW parameters should normally use "adamw": one owner updates the parameter and publishes the updated value by broadcast. This has the same update semantics as a sharded AdamW update, but avoids creating many small all-gather and reduce-scatter collectives inside every transformer block.
Large terminal AdamW parameters are different. Input embeddings are read at the beginning of the forward pass, and output heads such as lm_head produce gradients near the end of backward. If a single owner rank handles those tensors through broadcast and reduce, the owner can become a front-of-forward or tail-of-backward bottleneck. Routing those tensors to "sharded_adamw" spreads both the forward all-gather and the backward reduce-scatter across all ranks.
Control Surface¶
Policy control has two steps. First, predicate decides which parameters enter
DMuon's runtime at all. For pure DMuon this predicate is usually broad:
Every trainable parameter that returns False stays outside DMuon and must be
handled by the surrounding FSDP/DDP runtime. Every trainable parameter that
returns True is replaced by a DMuon placeholder and receives a structured
policy from param_policy.
Second, param_policy starts from defaults and applies ordered overrides
whose name tokens match the full parameter name. Each override updates
only the fields it sets. contains is still accepted as a legacy alias for
name, but new integrations should use name. The main route choices are:
- Return
"muon"for projection matrices that should be updated by Muon. - Return
"adamw"for small AdamW parameters such as LayerNorm weights, bias tensors, and other scalars/vectors. These parameters use DMuon's ownerbroadcastandreducepath. - Return
"sharded_adamw"only for large AdamW tensors whose communication should be split across all ranks, typically input embeddings andlm_head.
For any trainable parameter included by predicate, make sure the final
route is one of these three strings. A missing route is normalized to the
default "muon" route, so accidentally leaving LayerNorm or bias parameters
without an AdamW override would put them on the wrong optimizer path.
The final policy is consumed while dedicate_params() still has access to the
original full parameter tensor. This matters for "sharded_adamw" because DMuon
must build per-rank shard storage before the parameter is replaced by its
placeholder. Optimizer param groups may later group dedicated parameters for
hyperparameters, but they cannot create "sharded_adamw" storage after
dedicate_params() has finished.
Optimizer param_groups also do not override these per-parameter route hints by
default. A semantic group may contain both Muon matrices and AdamW-routed small
parameters; DMuon splits that group into route-specific optimizer subgroups while
keeping the route selected by param_policy. Set dmuon_route,
dmuon_optimizer, or matrix_optimizer on a user group only when you
intentionally want a group-level route override.
The dtype fields follow FSDP2 mixed-precision terminology. param_dtype is the
dtype of the materialized parameter used by forward/backward compute and forward
parameter communication. grad_dtype is the dtype for DMuon's gradient buffers
and gradient reduction. output_dtype casts floating-point outputs at the
module boundary, and cast_forward_inputs=True casts floating-point inputs to
param_dtype. master_dtype and optim_dtype describe DMuon's storage and
optimizer-update precision and stay separate from forward compute dtype.
Routing Policy¶
A practical LLM policy is:
- Parameters suitable for matrix optimization use
"muon"by default. - Embedding and
lm_headweights return"sharded_adamw". - LayerNorm, bias, and small AdamW parameters return
"adamw".
dmuon.dedicate_params(
model,
mesh,
predicate=lambda name, param: param.requires_grad,
param_policy={
"defaults": {
"route": "muon",
"param_dtype": torch.bfloat16,
"grad_dtype": None,
"master_dtype": torch.float32,
"optim_dtype": torch.float32,
},
"overrides": [
{
"name": ["embed_tokens", "word_embeddings", "wte", "lm_head"],
"set": {"route": "sharded_adamw"},
},
{
"name": ["norm", "ln_", ".bias"],
"set": {"route": "adamw"},
},
{
"name": ["action_head", "action_decoder"],
"set": {
"param_dtype": torch.float32,
"grad_dtype": torch.float32,
"output_dtype": torch.float32,
"cast_forward_inputs": True,
},
},
],
},
)
If this policy is active, LayerNorm parameters do not need special-case FSDP
wrapping to avoid all-gather/reduce-scatter. They match the "adamw" override
and therefore use DMuon's owner broadcast/reduce path. Projection matrices that
do not match a special override keep the default "muon" route. Parameters
under action_head can keep fp32 forward compute without forcing the rest of
the model out of bf16 compute.
Hook Boundaries¶
Route selection is independent from hook placement. DMuon still needs a module boundary where it can prepare full parameters before forward and collect gradients after backward. Decoder blocks usually provide that boundary for projection matrices and normalization weights. Embeddings and output heads are often called outside decoder blocks, so a pure DMuon integration should include those modules in hook_boundary_predicate.
terminal_module_ids = {
id(module)
for module in (
getattr(model, "lm_head", None),
getattr(getattr(model, "model", None), "embed_tokens", None),
)
if isinstance(module, (torch.nn.Embedding, torch.nn.Linear))
}
def hook_boundary(module):
if id(module) in terminal_module_ids:
return True
return isinstance(module, TransformerDecoderLayer)
hook_boundary_predicate controls where DMuon communication is attached. param_policy controls which communication primitive and dtype policy is used for each parameter at that boundary.
Activation casts are hook-boundary operations. If one hook boundary contains
parameters with different effective param_dtype values and any group enables
cast_forward_inputs, DMuon raises during setup. Split the action head into its
own hook boundary, or set cast_forward_inputs=False and make the module handle
activation casts explicitly.
Validation¶
After constructing the optimizer, inspect the route split before running a long job:
The expected pattern is that Muon-owned projection matrices appear under the Muon route, LayerNorm and bias parameters appear under AdamW, and only the large embedding/head tensors appear under sharded AdamW. The parameter rows also show param_dtype, grad_dtype, output_dtype, and matched policy override indices. If hundreds of small AdamW parameters appear under sharded AdamW, the route policy is too broad.
route_hint_fn remains available for legacy route-only integrations. New pure
DMuon integrations should prefer param_policy because route, parameter dtype,
gradient dtype, and module-boundary casting are resolved together before DMuon
replaces the original parameters.