Skip to content
Merged

SonicMoe #45433

Show file tree
Hide file tree
Changes from 6 commits
Commits
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
1 change: 1 addition & 0 deletions src/transformers/integrations/hub_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ def register_kernel_mapping_transformers(*args, **kwargs):
"falcon_mamba-ssm": {"repo_id": "kernels-community/mamba-ssm", "version": 1},
"finegrained-fp8": {"repo_id": "kernels-community/finegrained-fp8", "version": 1},
"deep-gemm": {"repo_id": "kernels-community/deep-gemm", "version": 1},
"sonic-moe": {"repo_id": "kernels-community/sonic-moe", "revision": "concatenated-gate-up"},
}

_KERNEL_MODULE_MAPPING: dict[str, ModuleType | None] = {}
Expand Down
3 changes: 3 additions & 0 deletions src/transformers/integrations/moe.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
is_torch_less_or_equal,
is_torchdynamo_compiling,
)
from .sonicmoe import sonicmoe_experts_forward


if is_torch_available():
Expand All @@ -31,6 +32,7 @@

logger = logging.get_logger(__name__)


# Examples of experts class with its eager mm implementation
# class Experts(torch.nn.Module):
# """Collection of expert weights stored as 3D tensors."""
Expand Down Expand Up @@ -452,6 +454,7 @@ class ExpertsInterface(GeneralInterface):
"""Interface for registering custom experts forward functions."""

_global_mapping = {
"sonicmoe": sonicmoe_experts_forward,
"batched_mm": batched_mm_experts_forward,
"grouped_mm": grouped_mm_experts_forward,
}
Expand Down
93 changes: 93 additions & 0 deletions src/transformers/integrations/sonicmoe.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo, this is so small that we can also move this under moe directly - it's essentially the one forward wrapper :D

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to also not even have this here but on kernels!

@IlyasMoutawwakil IlyasMoutawwakil Apr 20, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it's simple now but my guess is that it might get more complicated (fp8/fp4 support for example).
i made it into a separate file to avoid what we have in finegrained_fp8 with deepgemm for example 🤔 my understanding is that we want to move deepgemm integration to a separate file as well

would be nice to also not even have this here but on kernels!

it's very transformers and experts impl specific (which changes from time to time), for example if we were to move it in kernels, we will have to add many more inputs in the function (has_gate, has_bias, is_transposed, is_concatenated..) which will ultimately just be moe_general_routing with the weight axis permutation baked into it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, fair assesment, I only thought about the current version which is shortsighted

kernels should really only act as thin wrapper and we handle the specifics here imo - otherwise kernels maintainer will always have to do additional work from version change to version change (if things break)

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright 2026 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""SonicMoE integration: fused MoE using CuteDSL kernels from `kernels-community/sonic-moe`.

Provides `sonicmoe_experts_forward` registered as "sonicmoe" in the ExpertsInterface.
Requirements: CUDA, `kernels`, `nvidia-cutlass-dsl`, has_gate=True, is_transposed=False.
Comment thread
vasqu marked this conversation as resolved.
Resolved
"""

import torch

from ..utils import logging
from .hub_kernels import lazy_load_kernel


logger = logging.get_logger(__name__)

# Map activation function names from HF config to SonicMoE epilogue names
ACT_MAP = {"silu": "swiglu", "gelu": "geglu", "relu": "reglu"}


def sonicmoe_experts_forward(
self: torch.nn.Module,
hidden_states: torch.Tensor,
top_k_index: torch.Tensor,
top_k_weights: torch.Tensor,
) -> torch.Tensor:
if self.is_transposed:
raise ValueError("sonicmoe requires non-transposed weights (is_transposed=False)")
if not self.has_gate:
raise ValueError("sonicmoe requires gated experts (has_gate=True)")
if hidden_states.device.type != "cuda":
raise ValueError("sonicmoe requires CUDA device")

sonic_moe = lazy_load_kernel("sonic-moe")
moe_general_routing = getattr(sonic_moe, "moe_general_routing_inputs", None)
ActivationType = getattr(getattr(sonic_moe, "enums", None), "ActivationType", None)
if moe_general_routing is None or ActivationType is None:
raise ImportError(
"moe_general_routing_inputs function or ActivationType enum not found in kernels-community/sonic-moe. "
"Make sure you have the `kernels` package and `nvidia-cutlass-dsl` installed."
)
Comment thread
IlyasMoutawwakil marked this conversation as resolved.
Resolved

device = hidden_states.device
num_experts = self.num_experts
num_top_k = top_k_index.size(-1)
num_tokens = hidden_states.size(0)
grad_enabled = torch.is_grad_enabled()
stream_id = torch.cuda.current_stream(device).cuda_stream

# Flatten — token_indices must be int32, sorted ascending (required by sonic-moe)
token_idx = torch.arange(num_tokens, device=device).unsqueeze(1).expand(-1, num_top_k).reshape(-1).int()
router_scores = top_k_weights.reshape(-1).to(hidden_states.dtype)
expert_ids = top_k_index.reshape(-1).int()

# Map activation function
act_name = getattr(self.config, "hidden_act", "silu").lower()
activation_type = getattr(ActivationType, ACT_MAP.get(act_name, "swiglu").upper(), ActivationType.SWIGLU)

# Permute weights to (E, H, I) as expected by sonic-moe (E=num_experts, H=hidden_size, I=intermediate_size)
w1 = self.gate_up_proj.permute(1, 2, 0) # (2*I, H, E)
w2 = self.down_proj.permute(1, 2, 0) # (I, H, E)
b1 = self.gate_up_proj_bias if self.has_bias else None
b2 = self.down_proj_bias if self.has_bias else None

output, _ = moe_general_routing(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious: Is this compatible with torch compile? My hunch says no because they seldomly register the fake types upstream without external contributions lol

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's actually not 😭 well supposedly works on torch 2.9 or smth but my tests failed because of torch dynamo issues with cuda streams Dao-AILab/sonic-moe#21

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess torch 2.12 will fix it if I followed correctly pytorch/pytorch#177610

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should i link it in a comment ?

hidden_states,
router_scores,
token_idx,
expert_ids,
w1,
b1,
w2,
b2,
E=num_experts,
stream_id=stream_id,
activation_type=activation_type,
is_inference_mode_enabled=not grad_enabled,
is_concatenated_gate_up=True,
)

return output
Loading