Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9f19b2c
feat: adapt torchtitan dev 20260713
hann-wang Jul 14, 2026
3a46ed3
fix: apply_rotary_emb_complex patch
hann-wang Jul 14, 2026
b9786c9
fix: token dispatcher with paded tokens;
hann-wang Jul 14, 2026
a9efbdb
feat: basic DTensor support in wanda
hann-wang Jul 15, 2026
ff64beb
fix: restore from_linear() classmethod and update test constructor usage
Copilot Jul 15, 2026
06e4f2f
fix: gpt_oss_20b optimizer and dataloader issues
hann-wang Jul 17, 2026
e84bc2d
Potential fix for pull request finding
hann-wang Jul 17, 2026
a1a8fab
fix: propagate lora_rank to DecomposedLinear.Config
hann-wang Jul 17, 2026
e45f460
Merge branch 'han/torchtitan-20260713' of https://github.com/AMD-AGI/…
hann-wang Jul 17, 2026
536c13d
fix: checkpoint manager failed to access untyped storage if tensor wr…
hann-wang Jul 20, 2026
db5fcf3
Potential fix for pull request finding
hann-wang Jul 20, 2026
dfc194a
Potential fix for pull request finding
hann-wang Jul 20, 2026
24ed297
fx: missing trainer for de-osc
hann-wang Jul 24, 2026
5e20087
[WIP] feat: madam optimizer
hann-wang Jul 27, 2026
ec147ef
test: decompsoed linear with svd init
hann-wang Jul 28, 2026
046589e
feat: universal optimal scaling (uos) for mxfp4
hann-wang Aug 4, 2026
5bb9208
fix: _quantize_then_mxfp_scaled_grouped_mm with uos
hann-wang Aug 4, 2026
29aab52
fix: de-osc with uos
hann-wang Aug 7, 2026
88b7d5a
feat: pre-split microbatches
hann-wang Aug 10, 2026
e9d3f5a
fix: token dispatcher swap
hann-wang Aug 11, 2026
04e87d7
fix: missing loop index
hann-wang Aug 12, 2026
7d88e38
Merge branch 'main' into han/torchtitan-20260713
hann-wang Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 3rdparty/torchtitan
Submodule torchtitan updated 646 files
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Changelog

## v0.0.2
## v0.1.0 [dev]

- Changed
- Mixed precision is now handled by FSDP even if world_size=1.
- Dropped Instella-3B model config.
- Use VarlenAttn instead of FlexAttn for GPT-OSS models.
- Position ids are always populated by dataloader.


## v0.0.2 [dev]

- Changed
- **[BREAKING CHANGE]** rewind validation dataloader each step
Expand Down
8 changes: 8 additions & 0 deletions alto/components/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT

from typing import TYPE_CHECKING
from dataclasses import dataclass

import torch
Expand All @@ -15,6 +16,9 @@

from alto.config import Recipe

if TYPE_CHECKING:
from torchtitan.models.base import BaseModel


class ModelOptConverter(ModelConverter, Configurable):

Expand Down Expand Up @@ -45,6 +49,10 @@ def convert(self, model: nn.Module):

for modifier in self.recipe.modifiers:
modifier.convert(model)

def convert_config(self, model_config: "BaseModel.Config"):
for modifier in self.recipe.modifiers:
modifier.convert_config(model_config)

def pre_step(self, model_parts: list[nn.Module], **kwargs):
for modifier in self.recipe.modifiers:
Expand Down
2 changes: 1 addition & 1 deletion alto/components/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def qdq(w: torch.Tensor, axis: int) -> torch.Tensor:
w = torch.nn.functional.pad(w, (0, 0, 0, 32 - original_rows % 32))

data_lp, scales = convert_to_mxfp4(
w, axis=axis, is_2d_block=is_2d_block,
w, axis=axis, is_2d_block=is_2d_block, use_uos=cfg.use_uos
)
dequantized = convert_from_mxfp4(
data_lp,
Expand Down
4 changes: 2 additions & 2 deletions alto/kernels/dispatch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

from .config import TrainingOpConfig
from .conversion import swap_params
from .attention import LPScaledDotProductAttentionWrapper
from .attention import LPScaledDotProductAttention

__all__ = [
"TrainingOpConfig",
"swap_params",
"LPScaledDotProductAttentionWrapper",
"LPScaledDotProductAttention",
]
32 changes: 23 additions & 9 deletions alto/kernels/dispatch/attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
# SPDX-License-Identifier: MIT

import torch
from torchtitan.models.common.attention import (ScaledDotProductAttentionWrapper)
from torchtitan.models.common.attention import (ScaledDotProductAttention)

from alto.kernels.fp4.mxfp4.triton_flash_attention_mxfp4 import triton_attention_mxfp4
from .config import TrainingOpConfig

__all__ = ["LPScaledDotProductAttentionWrapper"]
__all__ = ["LPScaledDotProductAttention"]


class LPScaledDotProductAttentionWrapper(ScaledDotProductAttentionWrapper):
class LPScaledDotProductAttention(ScaledDotProductAttention):

def __init__(self, config: TrainingOpConfig):
super().__init__()
Expand All @@ -25,17 +25,30 @@ def __init__(self, config: TrainingOpConfig):

def _get_name(self) -> str:
return f"{self.__class__.__name__}[{self.config}]"

def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
q_BLNH: torch.Tensor,
k_BLNH: torch.Tensor,
v_BLNH: torch.Tensor,
*,
attention_masks: None = None,
scale: float | None = None,
Comment on lines 34 to 36
enable_gqa: bool = False,
is_causal: bool = True,
):
**kwargs,
) -> torch.Tensor:
if attention_masks is not None:
raise ValueError(
"ScaledDotProductAttention does not support attention_masks; it "
"only supports causal/non-causal attention via is_causal."
)
# Transpose to (B, N, L, H) for SDPA
q, k, v = (
q_BLNH.transpose(1, 2),
k_BLNH.transpose(1, 2),
v_BLNH.transpose(1, 2),
)
batch, num_head_q, seqlen_q, head_dim_qk = q.shape
batch_k, num_head_kv, seqlen_kv, head_dim_qk_k = k.shape
batch_v, num_head_kv_v, seqlen_kv_v, head_dim_v = v.shape
Expand Down Expand Up @@ -64,4 +77,5 @@ def forward(
use_exp2=True,
layout="bhsd",
)[0]
return o
# Transpose back to (B, L, N, H)
return o.transpose(1, 2)
5 changes: 5 additions & 0 deletions alto/kernels/dispatch/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class TrainingOpConfig:
* MXFP4: apply a blockwise scale factor containing shared mantissa to each block
* NVFP4: not implemented
"""

use_uos: bool = False
"""
use 7.25 instead of 6 for MXFP4 target range.
"""


torch.serialization.add_safe_globals([TrainingOpConfig])
2 changes: 1 addition & 1 deletion alto/kernels/dispatch/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def post_order_traversal(
continue
module_prefix = f"{module_name}." if module_name else ""
full_param_name = f"{module_prefix}{cur_fqn}{'.' if cur_fqn else ''}{param_name}"
if target_parameter_name is None and param_name.endswith("bias"):
if target_parameter_name is None and "bias" in param_name:
logger.debug(f"Skipped {full_param_name} because it is a bias parameter")
continue
if not isinstance(param.data, TrainingWeightWrapperBaseTensor):
Expand Down
5 changes: 5 additions & 0 deletions alto/kernels/dispatch/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ def __tensor_unflatten__(cls, inner_tensors, flatten_spec, outer_size, outer_str
flatten_spec["config"],
)

def untyped_storage(self):
return self._data.untyped_storage()

# fsdp hooks based on https://github.com/pytorch/pytorch/blob/20e40492b046b9287726d3ec656117e4dc38f0e2/test/distributed/_composable/fsdp/test_fully_shard_extensions.py#L81
def fsdp_pre_all_gather(
self,
Expand Down Expand Up @@ -256,6 +259,7 @@ def __torch_function__(cls, func, types, args, kwargs={}):
use_hadamard=config.use_hadamard,
clip_mode=config.clip_mode,
use_macro_block_scaling=config.two_level_scaling == "blockwise",
use_uos=config.use_uos,
)

# linear op override
Expand Down Expand Up @@ -287,6 +291,7 @@ def __torch_function__(cls, func, types, args, kwargs={}):
clip_mode=config.clip_mode,
use_hadamard=config.use_hadamard,
use_macro_block_scaling=config.two_level_scaling == "blockwise",
use_uos=config.use_uos,
)
if bias is not None:
Y = Y + bias
Expand Down
13 changes: 12 additions & 1 deletion alto/kernels/fp4/mxfp4/mxfp_grouped_gemm/cg_backward.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ def forward(
clip_mode=False,
use_macro_block_scaling=False,
hadamard_transform: Optional[HadamardTransform] = None,
use_uos: bool = False,
):
"""Forward pass for contiguous grouped GEMM."""
original_dtype = inputs.dtype
Expand All @@ -641,11 +642,13 @@ def forward(
inputs_scaled,
axis=-1,
is_2d_block=use_2dblock_x,
use_uos=use_uos,
)
expert_weights_mxfp4, expert_weight_scales = torch.ops.torchtitan.convert_to_mxfp4(
expert_weights_scaled,
axis=quant_axis_w,
is_2d_block=use_2dblock_w,
use_uos=use_uos,
)

if is_cdna4():
Expand Down Expand Up @@ -697,6 +700,7 @@ def forward(
expert_weights_scaled,
axis=requant_axis_w,
is_2d_block=False,
use_uos=use_uos,
)
if not is_cdna4():
w_dq = torch.ops.torchtitan.convert_from_mxfp4(
Expand Down Expand Up @@ -724,6 +728,7 @@ def forward(
axis=0,
is_2d_block=False,
clip_mode=clip_mode,
use_uos=use_uos,
)
if not is_cdna4():
x_dq = torch.ops.torchtitan.convert_from_mxfp4(
Expand Down Expand Up @@ -752,6 +757,7 @@ def forward(
ctx.hadamard_transform = hadamard_transform
ctx.clip_mode = clip_mode
ctx.use_macro_block_scaling = use_macro_block_scaling
ctx.use_uos = use_uos

return res

Expand Down Expand Up @@ -785,6 +791,7 @@ def backward(ctx, grad_output):
axis=-1,
use_sr=ctx.use_sr_grad,
is_2d_block=True,
use_uos=ctx.use_uos,
)
grad_output_mxfp4_m = grad_output_mxfp4
grad_output_scales_m = grad_output_scales
Expand Down Expand Up @@ -812,6 +819,7 @@ def backward(ctx, grad_output):
axis=-1,
use_sr=ctx.use_sr_grad,
is_2d_block=False,
use_uos=ctx.use_uos,
)
if ctx.hadamard_transform is not None:
grad_output = ctx.hadamard_transform(grad_output, left_mul=True)
Expand All @@ -826,6 +834,7 @@ def backward(ctx, grad_output):
use_sr=ctx.use_sr_grad,
is_2d_block=False,
clip_mode=ctx.clip_mode,
use_uos=ctx.use_uos,
)

if not is_cdna4():
Expand Down Expand Up @@ -918,7 +927,7 @@ def backward(ctx, grad_output):
)
grad_weights *= dge_bwd(w_fp4_values, torch.float4_e2m1fn_x2)

return grad_inputs, grad_weights, None, None, None, None, None, None, None, None, None
return grad_inputs, grad_weights, None, None, None, None, None, None, None, None, None, None


def mxfp4_grouped_gemm(
Expand All @@ -934,6 +943,7 @@ def mxfp4_grouped_gemm(
use_hadamard: bool = False,
clip_mode: str = "none",
use_macro_block_scaling: bool = False,
use_uos: bool = False
) -> torch.Tensor:
"""
Interface for contiguous grouped GEMM with full backward pass support.
Expand Down Expand Up @@ -975,6 +985,7 @@ def mxfp4_grouped_gemm(
clip_mode,
use_macro_block_scaling,
hadamard_transform,
use_uos,
)

return res
Expand Down
2 changes: 2 additions & 0 deletions alto/kernels/fp4/mxfp4/mxfp_grouped_gemm/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def _quantize_then_mxfp_scaled_grouped_mm(
use_hadamard: bool,
clip_mode: str,
use_macro_block_scaling: bool,
use_uos: bool = False,
) -> torch.Tensor:
m_indices = create_indices_from_offsets_nosync(offs)
return mxfp4_grouped_gemm(
Expand All @@ -33,4 +34,5 @@ def _quantize_then_mxfp_scaled_grouped_mm(
use_hadamard=use_hadamard,
clip_mode=clip_mode,
use_macro_block_scaling=use_macro_block_scaling,
use_uos=use_uos,
)
13 changes: 12 additions & 1 deletion alto/kernels/fp4/mxfp4/mxfp_linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ def forward(
clip_mode,
use_macro_block_scaling,
hadamard_transform: Optional[HadamardTransform] = None,
use_uos: bool = False,
):
"""
Forward pass for the blockwise FP8 linear operation.
Expand Down Expand Up @@ -310,12 +311,14 @@ def forward(
x_scaled,
axis=-1,
is_2d_block=use_2dblock_x,
use_uos=use_uos,
)

w_mxfp4, w_scale = torch.ops.torchtitan.convert_to_mxfp4(
w_scaled,
axis=-1,
is_2d_block=use_2dblock_w,
use_uos=use_uos,
)

if is_cdna4():
Expand Down Expand Up @@ -363,6 +366,7 @@ def forward(
w_scaled,
axis=0,
is_2d_block=False,
use_uos=use_uos,
)
if not is_cdna4():
w_dq = torch.ops.torchtitan.convert_from_mxfp4(
Expand All @@ -388,6 +392,7 @@ def forward(
axis=0,
is_2d_block=False,
clip_mode=clip_mode,
use_uos=use_uos,
)
if not is_cdna4():
x_dq = torch.ops.torchtitan.convert_from_mxfp4(
Expand All @@ -412,6 +417,7 @@ def forward(
ctx.use_dge = use_dge
ctx.clip_mode = clip_mode
ctx.use_macro_block_scaling = use_macro_block_scaling
ctx.use_uos = use_uos

return y.view(*original_shape[:-1], -1) # Reshape back to original

Expand Down Expand Up @@ -440,6 +446,7 @@ def backward(ctx, grad_output):
axis=-1,
is_2d_block=True,
use_sr=ctx.use_sr_grad,
use_uos=ctx.use_uos,
)
grad_output_mxfp4_m = grad_output_mxfp4
grad_output_scales_m = grad_output_scales
Expand Down Expand Up @@ -467,6 +474,7 @@ def backward(ctx, grad_output):
axis=-1,
use_sr=ctx.use_sr_grad,
is_2d_block=False,
use_uos=ctx.use_uos,
)

if ctx.hadamard_transform is not None:
Expand All @@ -482,6 +490,7 @@ def backward(ctx, grad_output):
use_sr=ctx.use_sr_grad,
is_2d_block=False,
clip_mode=ctx.clip_mode,
use_uos=ctx.use_uos,
)

if not is_cdna4():
Expand Down Expand Up @@ -558,7 +567,7 @@ def backward(ctx, grad_output):
)
grad_weights *= dge_bwd(w_fp4_values, torch.float4_e2m1fn_x2)

return grad_inputs.view(*original_shape[:-1], -1), grad_weights, None, None, None, None, None, None, None
return grad_inputs.view(*original_shape[:-1], -1), grad_weights, None, None, None, None, None, None, None, None


def _to_mxfp4_then_scaled_mm(
Expand All @@ -571,6 +580,7 @@ def _to_mxfp4_then_scaled_mm(
clip_mode: str,
use_hadamard: bool,
use_macro_block_scaling: bool = False,
use_uos: bool = False,
) -> torch.Tensor:
if use_hadamard:
with torch.no_grad():
Expand All @@ -587,5 +597,6 @@ def _to_mxfp4_then_scaled_mm(
clip_mode,
use_macro_block_scaling,
hadamard_transform,
use_uos,
)
return y
Loading