A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 3, 10:30 UTC
[ROCm][Quantization][3/N] Refactor quark_moe w4a4 w/ oracle (#41436)
Signed-off-by: Bowen Bao <bowenbao@amd.com>
Bowen Bao committed
May 18, 2026 at 10:46 UTC
a2c8fc66573664395f491a94da1882fdf92e034b
8 files changed
+224
-158
tests/evals/gsm8k/configs/Qwen3.5-35B-A3B-MXFP4-AITER-TP2.yaml
new
+12
@@ -0,0 +1,12 @@
1
+model_name: "amd/Qwen3.5-35B-A3B-MXFP4"
2
+accuracy_threshold: 0.89
3
+tolerance: 0.03
4
+num_questions: 1319
5
+num_fewshot: 5
6
+server_args: >-
7
+ --max-model-len 4096
8
+ --tensor-parallel-size 2
9
+ --gpu-memory-utilization 0.35
10
+ --moe-backend aiter
11
+env:
12
+ VLLM_ROCM_USE_AITER: "1"
tests/evals/gsm8k/configs/Qwen3.5-35B-A3B-MXFP4-EMU-TP2.yaml
renamed
+3
-1
@@ -1,8 +1,10 @@
1
model_name: "amd/Qwen3.5-35B-A3B-MXFP4"
2
-accuracy_threshold: 0.82
2
+accuracy_threshold: 0.89
3
tolerance: 0.03
4
num_questions: 1319
5
num_fewshot: 5
6
server_args: >-
7
--max-model-len 4096
8
--tensor-parallel-size 2
9
+ --moe-backend emulation
10
+ --gpu-memory-utilization 0.35
tests/evals/gsm8k/configs/models-mi3xx.txt
+2
-1
@@ -3,4 +3,5 @@ DeepSeek-R1-DP_MI325.yaml
3
DeepSeek-V3.2-TP_MI325.yaml
4
DeepSeek-V3.2-DP_MI325.yaml
5
Qwen3-30B-A3B-NVFP4.yaml
6
-Qwen3.5-35B-A3B-MXFP4-TP2.yaml
\ No newline at end of file
6
+Qwen3.5-35B-A3B-MXFP4-AITER-TP2.yaml
7
+Qwen3.5-35B-A3B-MXFP4-EMU-TP2.yaml
\ No newline at end of file
tests/evals/gsm8k/configs/models-qwen35-mi355.txt
+2
-1
@@ -1,2 +1,3 @@
1
Qwen3.5-35B-A3B-DEP2.yaml
2
-Qwen3.5-35B-A3B-MXFP4-TP2.yaml
2
+Qwen3.5-35B-A3B-MXFP4-AITER-TP2.yaml
3
+Qwen3.5-35B-A3B-MXFP4-EMU-TP2.yaml
tests/quantization/test_gfx950_moe.py
+86
-2
@@ -1,6 +1,90 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+"""
4
+Tests for MXFP4 MoE oracle backend selection on mi355x (GFX950).
5
6
+These tests run on real hardware — no mocks. Skipped on non-GFX950 platforms.
7
+"""
8
5
-def test_mi355_moe():
6
- print("TODO: add tests for Mi355 MoE quantization")
9
+import pytest
10
+import torch
11
+
12
+from vllm.model_executor.layers.fused_moe.config import (
13
+ FusedMoEConfig,
14
+ FusedMoEParallelConfig,
15
+ RoutingMethodType,
16
+)
17
+from vllm.model_executor.layers.fused_moe.oracle.mxfp4 import (
18
+ Mxfp4MoeBackend,
19
+ select_mxfp4_moe_backend,
20
+)
21
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
22
+ kMxfp4Dynamic,
23
+)
24
+from vllm.platforms import current_platform
25
+
26
+ROCM_AVAILABLE = current_platform.is_rocm()
27
+ROCM_GFX950 = False
28
+ROCM_AITER_AVAILABLE = False
29
+
30
+if ROCM_AVAILABLE:
31
+ from vllm._aiter_ops import rocm_aiter_ops
32
+ from vllm.platforms.rocm import on_gfx950
33
+
34
+ ROCM_GFX950 = on_gfx950()
35
+ ROCM_AITER_AVAILABLE = rocm_aiter_ops.is_fused_moe_enabled()
36
+
37
+
38
+def _make_w4a4_moe_config(moe_backend: str = "auto") -> FusedMoEConfig:
39
+ from vllm.model_executor.layers.fused_moe.activation import MoEActivation
40
+
41
+ return FusedMoEConfig(
42
+ num_experts=8,
43
+ experts_per_token=2,
44
+ hidden_dim=256,
45
+ intermediate_size_per_partition=256,
46
+ num_local_experts=8,
47
+ num_logical_experts=8,
48
+ moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
49
+ activation=MoEActivation.SILU,
50
+ in_dtype=torch.bfloat16,
51
+ device="cuda",
52
+ routing_method=RoutingMethodType.Renormalize,
53
+ moe_backend=moe_backend,
54
+ )
55
+
56
+
57
+@pytest.mark.skipif(not ROCM_GFX950, reason="Requires GFX950 (mi355x)")
58
+@pytest.mark.skipif(not ROCM_AITER_AVAILABLE, reason="Requires AITER enabled")
59
+def test_w4a4_dispatches_to_aiter():
60
+ """With AITER enabled + GFX950, W4A4 selects AITER_MXFP4_MXFP4."""
61
+ config = _make_w4a4_moe_config()
62
+ backend, experts_cls = select_mxfp4_moe_backend(
63
+ config, activation_key=kMxfp4Dynamic
64
+ )
65
+ assert backend == Mxfp4MoeBackend.AITER_MXFP4_MXFP4
66
+ assert experts_cls is not None
67
+
68
+
69
+@pytest.mark.skipif(not ROCM_GFX950, reason="Requires GFX950 (mi355x)")
70
+@pytest.mark.skipif(
71
+ ROCM_AITER_AVAILABLE,
72
+ reason="Test requires AITER disabled (unset VLLM_ROCM_USE_AITER)",
73
+)
74
+def test_w4a4_raises_without_aiter_and_no_moe_backend():
75
+ """Without AITER and no --moe-backend, raises NotImplementedError
76
+ with hint to use --moe-backend emulation."""
77
+ config = _make_w4a4_moe_config()
78
+ with pytest.raises(NotImplementedError, match="--moe-backend emulation"):
79
+ select_mxfp4_moe_backend(config, activation_key=kMxfp4Dynamic)
80
+
81
+
82
+@pytest.mark.skipif(not ROCM_GFX950, reason="Requires GFX950 (mi355x)")
83
+def test_w4a4_dispatches_to_emulation_with_moe_backend():
84
+ """With --moe-backend emulation, W4A4 selects EMULATION."""
85
+ config = _make_w4a4_moe_config(moe_backend="emulation")
86
+ backend, experts_cls = select_mxfp4_moe_backend(
87
+ config, activation_key=kMxfp4Dynamic
88
+ )
89
+ assert backend == Mxfp4MoeBackend.EMULATION
90
+ assert experts_cls is not None
vllm/model_executor/layers/fused_moe/experts/rocm_aiter_moe.py
+17
@@ -26,6 +26,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
26
kFp8Static128BlockSym,
27
kFp8StaticChannelSym,
28
kFp8StaticTensorSym,
29
+ kMxfp4Dynamic,
30
kMxfp4Static,
31
)
32
@@ -377,6 +378,21 @@ class AiterExperts(mk.FusedMoEExpertsModular):
378
def activation_format() -> mk.FusedMoEActivationFormat:
379
return mk.FusedMoEActivationFormat.Standard
380
381
+ @staticmethod
382
+ def is_supported_config(
383
+ cls, moe_config, weight_key, activation_key, activation_format
384
+ ):
385
+ is_supported, reason = super().is_supported_config(
386
+ cls, moe_config, weight_key, activation_key, activation_format
387
+ )
388
+ if not is_supported and not rocm_aiter_ops.is_fused_moe_enabled():
389
+ reason = (
390
+ f"{reason}. AITER MoE is not enabled — "
391
+ "set VLLM_ROCM_USE_AITER=1 and VLLM_ROCM_USE_AITER_MOE=1 "
392
+ "to enable it"
393
+ )
394
+ return is_supported, reason
395
+
396
@staticmethod
397
def _supports_current_device() -> bool:
398
return rocm_aiter_ops.is_fused_moe_enabled()
@@ -397,6 +413,7 @@ class AiterExperts(mk.FusedMoEExpertsModular):
413
(kFp8StaticTensorSym, kFp8DynamicTensorSym),
414
(kFp8StaticChannelSym, kFp8DynamicTokenSym),
415
(kMxfp4Static, None),
416
+ (kMxfp4Static, kMxfp4Dynamic),
417
]
418
if (weight_key, activation_key) not in SUPPORTED_W_A:
419
return False
vllm/model_executor/layers/fused_moe/oracle/mxfp4.py
+76
-2
@@ -31,6 +31,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
31
QuantKey,
32
kFp8Dynamic128Sym,
33
kFp8StaticTensorSym,
34
+ kMxfp4Dynamic,
35
kMxfp4Static,
36
kMxfp8Dynamic,
37
)
@@ -74,6 +75,7 @@ class Mxfp4MoeBackend(Enum):
75
# Keep the legacy name as an alias while the ROCm split backend rename settles.
76
AITER = "AITER_MXFP4_BF16"
77
AITER_MXFP4_FP8 = "AITER_MXFP4_FP8" # W4A8: triton kernel
78
+ AITER_MXFP4_MXFP4 = "AITER_MXFP4_MXFP4" # W4A4: CK kernel
79
# Triton
80
TRITON = "TRITON"
81
TRITON_UNFUSED = "TRITON_UNFUSED"
@@ -91,6 +93,7 @@ class Mxfp4MoeBackend(Enum):
93
AITER_BACKENDS = (
94
Mxfp4MoeBackend.AITER_MXFP4_BF16,
95
Mxfp4MoeBackend.AITER_MXFP4_FP8,
96
+ Mxfp4MoeBackend.AITER_MXFP4_MXFP4,
97
)
98
99
@@ -195,6 +198,13 @@ def backend_to_kernel_cls(
198
199
return [AiterW4A8ExpertsMonolithic]
200
201
+ elif backend == Mxfp4MoeBackend.AITER_MXFP4_MXFP4:
202
+ from vllm.model_executor.layers.fused_moe.experts.rocm_aiter_moe import (
203
+ AiterExperts,
204
+ )
205
+
206
+ return [AiterExperts]
207
+
208
elif backend == Mxfp4MoeBackend.XPU:
209
from vllm.model_executor.layers.fused_moe.experts.xpu_moe import XPUExpertsMXFp4
210
@@ -241,8 +251,10 @@ def map_mxfp4_backend(runner_backend: MoEBackend) -> list[Mxfp4MoeBackend]:
251
"aiter": [
252
Mxfp4MoeBackend.AITER_MXFP4_BF16,
253
Mxfp4MoeBackend.AITER_MXFP4_FP8,
254
+ Mxfp4MoeBackend.AITER_MXFP4_MXFP4,
255
],
256
"aiter_mxfp4_fp8": [Mxfp4MoeBackend.AITER_MXFP4_FP8],
257
+ "aiter_mxfp4_mxfp4": [Mxfp4MoeBackend.AITER_MXFP4_MXFP4],
258
"xpu": [Mxfp4MoeBackend.XPU],
259
"cpu": [Mxfp4MoeBackend.CPU],
260
"emulation": [Mxfp4MoeBackend.EMULATION],
@@ -263,6 +275,7 @@ def _get_priority_backends_for_gpt_oss() -> list[Mxfp4MoeBackend]:
275
Mxfp4MoeBackend.FLASHINFER_TRTLLM_MXFP4_MXFP8,
276
Mxfp4MoeBackend.AITER_MXFP4_BF16,
277
Mxfp4MoeBackend.AITER_MXFP4_FP8,
278
+ Mxfp4MoeBackend.AITER_MXFP4_MXFP4,
279
Mxfp4MoeBackend.TRITON,
280
Mxfp4MoeBackend.FLASHINFER_CUTLASS_MXFP4_BF16,
281
Mxfp4MoeBackend.FLASHINFER_CUTLASS_MXFP4_MXFP8,
@@ -272,7 +285,6 @@ def _get_priority_backends_for_gpt_oss() -> list[Mxfp4MoeBackend]:
285
Mxfp4MoeBackend.MARLIN,
286
Mxfp4MoeBackend.BATCHED_MARLIN,
287
Mxfp4MoeBackend.XPU,
275
- Mxfp4MoeBackend.EMULATION,
288
]
289
return _AVAILABLE_BACKENDS
290
@@ -308,6 +320,8 @@ def _backend_activation_key(backend: Mxfp4MoeBackend) -> QuantKey | None:
320
return kMxfp8Dynamic
321
if backend == Mxfp4MoeBackend.AITER_MXFP4_FP8:
322
return kFp8StaticTensorSym
323
+ if backend == Mxfp4MoeBackend.AITER_MXFP4_MXFP4:
324
+ return kMxfp4Dynamic
325
return None # BF16 activation
326
327
@@ -379,6 +393,7 @@ def _filter_by_activation(
393
b
394
for b in backends
395
if _backend_activation_key(b) == requested_activation_key
396
+ or b == Mxfp4MoeBackend.EMULATION
397
]
398
bf16 = [b for b in backends if _backend_activation_key(b) is None]
399
return bf16 if bf16 else backends
@@ -556,7 +571,12 @@ def select_mxfp4_moe_backend(
571
572
if current_platform.is_cuda() or current_platform.is_rocm():
573
raise NotImplementedError(
559
- "No MXFP4 MoE backend supports the deployment configuration."
574
+ "No MXFP4 MoE backend supports the deployment configuration. "
575
+ f"weight_key=kMxfp4Static, activation_key={activation_key}. "
576
+ "Native backends require specific hardware. "
577
+ "Set `VLLM_LOGGING_LEVEL=DEBUG` to see detailed unsupported reasons. "
578
+ "To use the emulation backend for research/debugging, pass "
579
+ "--moe-backend emulation."
580
)
581
582
return Mxfp4MoeBackend.NONE, None
@@ -957,6 +977,49 @@ def convert_gpt_oss_weight_to_mxfp4_moe_kernel_format(
977
w2_bias,
978
)
979
980
+ elif mxfp4_backend == Mxfp4MoeBackend.AITER_MXFP4_MXFP4:
981
+ from vllm._aiter_ops import rocm_aiter_ops
982
+
983
+ if w13_bias is not None:
984
+ w13_bias = w13_bias.data.to(torch.float32)
985
+ if w2_bias is not None:
986
+ w2_bias = w2_bias.data.to(torch.float32)
987
+
988
+ # e8m0_shuffle on weight scales (GFX950 swizzle layout)
989
+ from aiter.utility.fp4_utils import e8m0_shuffle
990
+
991
+ s0, s1, _ = w13_weight_scale.shape
992
+ w13_weight_scale.data = e8m0_shuffle(w13_weight_scale.view(s0 * s1, -1)).view(
993
+ s0, s1, -1
994
+ )
995
+
996
+ s0, s1, _ = w2_weight_scale.shape
997
+ w2_weight_scale.data = e8m0_shuffle(w2_weight_scale.view(s0 * s1, -1)).view(
998
+ s0, s1, -1
999
+ )
1000
+
1001
+ # View as native FP4 dtype
1002
+ fp4_dtype = getattr(torch, "float4_e2m1fn_x2", None)
1003
+ if fp4_dtype is not None:
1004
+ w13_weight.data = w13_weight.data.view(fp4_dtype)
1005
+ w2_weight.data = w2_weight.data.view(fp4_dtype)
1006
+
1007
+ # Shuffle weights for AITER CK kernel
1008
+ shuffled_w13, shuffled_w2 = rocm_aiter_ops.shuffle_weights(
1009
+ w13_weight, w2_weight
1010
+ )
1011
+ shuffled_w13.is_shuffled = True
1012
+ shuffled_w2.is_shuffled = True
1013
+
1014
+ return (
1015
+ shuffled_w13,
1016
+ shuffled_w2,
1017
+ w13_weight_scale,
1018
+ w2_weight_scale,
1019
+ w13_bias,
1020
+ w2_bias,
1021
+ )
1022
+
1023
elif mxfp4_backend == Mxfp4MoeBackend.AITER_MXFP4_BF16:
1024
from vllm._aiter_ops import rocm_aiter_ops
1025
@@ -1541,6 +1604,17 @@ def make_mxfp4_moe_quant_config(
1604
block_shape=None,
1605
gemm1_clamp_limit=swiglu_limit,
1606
)
1607
+ elif mxfp4_backend == Mxfp4MoeBackend.AITER_MXFP4_MXFP4:
1608
+ return ocp_mx_moe_quant_config(
1609
+ quant_dtype="mxfp4",
1610
+ w1_bias=w1_bias,
1611
+ w2_bias=w2_bias,
1612
+ w1_scale=w1_scale,
1613
+ w2_scale=w2_scale,
1614
+ gemm1_alpha=gemm1_alpha,
1615
+ gemm1_beta=gemm1_beta,
1616
+ gemm1_clamp_limit=swiglu_limit,
1617
+ )
1618
elif mxfp4_backend in (
1619
Mxfp4MoeBackend.MARLIN,
1620
Mxfp4MoeBackend.BATCHED_MARLIN,
vllm/model_executor/layers/quantization/quark/quark_moe.py
+26
-151
@@ -55,6 +55,7 @@ from vllm.model_executor.layers.quantization.utils.ocp_mx_utils import (
55
from vllm.model_executor.layers.quantization.utils.quant_utils import (
56
GroupShape,
57
kFp8StaticTensorSym,
58
+ kMxfp4Dynamic,
59
kNvfp4Dynamic,
60
kNvfp4Static,
61
)
@@ -1042,6 +1043,11 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1043
self.mxfp4_backend, self.experts_cls = select_mxfp4_moe_backend(
1044
moe, activation_key=kFp8StaticTensorSym
1045
)
1046
+ elif self.ocp_mx_scheme == "w_mxfp4_a_mxfp4":
1047
+ # W4A4: MXFP4 weights + MXFP4 activations
1048
+ self.mxfp4_backend, self.experts_cls = select_mxfp4_moe_backend(
1049
+ moe, activation_key=kMxfp4Dynamic
1050
+ )
1051
1052
# Validation for unsupported schemes
1053
if any(
@@ -1061,45 +1067,19 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1067
"Please open an issue."
1068
)
1069
1064
- self.use_rocm_aiter_moe = rocm_aiter_ops.is_fused_moe_enabled()
1065
-
1070
self.model_type = getattr(
1071
get_current_vllm_config().model_config.hf_config, "model_type", None
1072
)
1073
1070
- # TODO: Remove once all OCP MX schemes use the kernel abstraction
1071
- _AITER_NATIVE_OCP_MX_SCHEMES = ("w_mxfp4", "w_mxfp4_a_mxfp4", "w_mxfp4_a_fp8")
1072
- self.emulate = (
1073
- not current_platform.supports_mx()
1074
- or self.ocp_mx_scheme not in _AITER_NATIVE_OCP_MX_SCHEMES
1075
- ) and (
1076
- self.mxfp4_backend is Mxfp4MoeBackend.NONE or not self.use_rocm_aiter_moe
1077
- )
1078
-
1079
- if self.emulate:
1080
- # We use the same code path between MXFP4/MXFP6 emulation.
1074
+ # If no native backend available, use emulation.
1075
+ if self.mxfp4_backend is Mxfp4MoeBackend.NONE:
1076
self.mxfp4_backend = Mxfp4MoeBackend.EMULATION
1077
1083
- # TODO: Remove `self.mxfp4_backend != Mxfp4MoeBackend.NONE` and make it so that
1084
- # all MXFP4 backends use the kernel abstraction.
1085
- if self.mxfp4_backend != Mxfp4MoeBackend.NONE:
1086
- self.experts_cls = backend_to_kernel_cls(self.mxfp4_backend)[0]
1078
+ self.experts_cls = backend_to_kernel_cls(self.mxfp4_backend)[0]
1079
1088
- # Log backend selection
1089
- if self.mxfp4_backend != Mxfp4MoeBackend.NONE:
1090
- logger.info_once(
1091
- f"Using {self.mxfp4_backend.value} backend for {self.ocp_mx_scheme}"
1092
- )
1093
- elif self.emulate:
1094
- logger.warning_once(
1095
- f"The current mode (supports_mx={current_platform.supports_mx()}, "
1096
- f"use_rocm_aiter_moe={self.use_rocm_aiter_moe}, "
1097
- f"ocp_mx_scheme={self.ocp_mx_scheme}) "
1098
- "does not support native MXFP4/MXFP6 "
1099
- "computation. Simulated weight dequantization and activation "
1100
- "QDQ (quantize and dequantize) will be used, with the linear "
1101
- "layers computed in high precision."
1102
- )
1080
+ logger.info_once(
1081
+ f"Using {self.mxfp4_backend.value} backend for {self.ocp_mx_scheme}"
1082
+ )
1083
1084
def maybe_roundup_sizes(
1085
self,
@@ -1243,97 +1223,8 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1223
layer.w13_input_scale = None
1224
layer.w2_input_scale = None
1225
1246
- def process_weights_after_loading(self, layer: RoutedExperts):
1247
- # For MXFP4 schemes with native backend, use oracle
1248
- if self.mxfp4_backend != Mxfp4MoeBackend.NONE:
1249
- self._setup_kernel(layer)
1250
- return
1251
-
1252
- if self.static_input_scales and self.input_dtype == "fp8":
1253
- # firstly, process activations if fp8 static input
1254
- if layer.w13_input_scale is None or layer.w2_input_scale is None:
1255
- raise ValueError(
1256
- "QuantConfig has static quantization, but found "
1257
- "activation scales are None."
1258
- )
1259
- if not all_close_1d(layer.w13_input_scale) or not all_close_1d(
1260
- layer.w2_input_scale
1261
- ):
1262
- logger.warning_once(
1263
- "Found input_scales that are not equal for "
1264
- "fp8 MoE layer. Using the maximum across experts "
1265
- "for each layer. "
1266
- )
1267
- layer.w13_input_scale = torch.nn.Parameter(
1268
- layer.w13_input_scale.max(), requires_grad=False
1269
- )
1270
- layer.w2_input_scale = torch.nn.Parameter(
1271
- layer.w2_input_scale.max(), requires_grad=False
1272
- )
1273
-
1274
- if current_platform.is_fp8_fnuz():
1275
- # Normalize the weights and scales
1276
- _, _, w13_input_scale = normalize_e4m3fn_to_e4m3fnuz(
1277
- torch.empty_like(layer.w13_weight, dtype=torch.float8_e4m3fn),
1278
- torch.empty_like(
1279
- layer.w13_weight_scale, dtype=layer.w13_weight_scale.dtype
1280
- ),
1281
- layer.w13_input_scale,
1282
- )
1283
- _, _, w2_input_scale = normalize_e4m3fn_to_e4m3fnuz(
1284
- torch.empty_like(layer.w2_weight, dtype=torch.float8_e4m3fn),
1285
- torch.empty_like(
1286
- layer.w2_weight_scale, dtype=layer.w13_weight_scale.dtype
1287
- ),
1288
- layer.w2_input_scale,
1289
- )
1290
- # Reset the parameter
1291
- if w13_input_scale is not None:
1292
- layer.w13_input_scale = torch.nn.Parameter(
1293
- w13_input_scale, requires_grad=False
1294
- )
1295
- if w2_input_scale is not None:
1296
- layer.w2_input_scale = torch.nn.Parameter(
1297
- w2_input_scale, requires_grad=False
1298
- )
1299
-
1300
- # TODO(bowenbao): gradually migrate to oracles.
1301
- # Existing AITER path for w_mxfp4_a_mxfp4 and other schemes
1302
- from aiter.utility.fp4_utils import e8m0_shuffle
1303
-
1304
- # Pre-shuffle weight scales
1305
- s0, s1, _ = layer.w13_weight_scale.shape
1306
- w13_weight_scale = layer.w13_weight_scale.view(s0 * s1, -1)
1307
- w13_weight_scale = e8m0_shuffle(w13_weight_scale)
1308
- layer.w13_weight_scale.data = w13_weight_scale.view(s0, s1, -1)
1309
-
1310
- s0, s1, _ = layer.w2_weight_scale.shape
1311
- w2_weight_scale = layer.w2_weight_scale.view(s0 * s1, -1)
1312
- w2_weight_scale = e8m0_shuffle(w2_weight_scale)
1313
- layer.w2_weight_scale.data = w2_weight_scale.view(s0, s1, -1)
1314
-
1315
- if self.fp4_dtype is not None:
1316
- layer.w13_weight = torch.nn.Parameter(
1317
- layer.w13_weight.view(self.fp4_dtype),
1318
- requires_grad=layer.w13_weight.requires_grad,
1319
- )
1320
- layer.w2_weight = torch.nn.Parameter(
1321
- layer.w2_weight.view(self.fp4_dtype),
1322
- requires_grad=layer.w2_weight.requires_grad,
1323
- )
1324
- # Pre-shuffle weight
1325
- shuffled_w13, shuffled_w2 = rocm_aiter_ops.shuffle_weights(
1326
- layer.w13_weight.data, layer.w2_weight.data
1327
- )
1328
-
1329
- layer.w13_weight = torch.nn.Parameter(shuffled_w13, requires_grad=False)
1330
- layer.w2_weight = torch.nn.Parameter(shuffled_w2, requires_grad=False)
1331
- layer.w13_weight.is_shuffled = True
1332
- layer.w2_weight.is_shuffled = True
1333
-
1334
- # Build quant config for AITER path
1335
- self.moe_quant_config = self.get_fused_moe_quant_config(layer)
1336
- torch.accelerator.empty_cache()
1226
+ def process_weights_after_loading(self, layer):
1227
+ self._setup_kernel(layer)
1228
1229
def _setup_kernel(self, layer: RoutedExperts):
1230
"""Setup kernel using oracle functions for MXFP4 schemes (W4A16, W4A8)."""
@@ -1375,6 +1266,10 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1266
replace_parameter(layer, "w13_bias", w13_bias)
1267
replace_parameter(layer, "w2_bias", w2_bias)
1268
1269
+ if self.mxfp4_backend == Mxfp4MoeBackend.AITER_MXFP4_MXFP4:
1270
+ layer.w13_weight.is_shuffled = True
1271
+ layer.w2_weight.is_shuffled = True
1272
+
1273
torch.accelerator.empty_cache()
1274
1275
# Build quant config and kernel
@@ -1464,38 +1359,18 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1359
shared_experts: SharedExperts | None,
1360
shared_experts_input: torch.Tensor | None,
1361
) -> torch.Tensor:
1467
- # For oracle-based kernels (W4A16, W4A8) or emulation kernel
1468
- if self.moe_kernel is not None:
1469
- return self.moe_kernel.apply(
1470
- hidden_states=x,
1471
- w1=layer.w13_weight,
1472
- w2=layer.w2_weight,
1473
- topk_weights=topk_weights,
1474
- topk_ids=topk_ids,
1475
- activation=layer.activation,
1476
- global_num_experts=layer.global_num_experts,
1477
- apply_router_weight_on_input=layer.apply_router_weight_on_input,
1478
- expert_map=layer.expert_map,
1479
- shared_experts=shared_experts,
1480
- shared_experts_input=shared_experts_input,
1481
- )
1482
-
1483
- # AITER path
1484
- # TODO: Refactor this to use modular MOE kernel as well.
1485
- from vllm.model_executor.layers.fused_moe.experts.rocm_aiter_moe import (
1486
- rocm_aiter_fused_experts,
1487
- )
1488
-
1489
- return rocm_aiter_fused_experts(
1490
- x,
1491
- layer.w13_weight,
1492
- layer.w2_weight,
1362
+ assert self.moe_kernel is not None
1363
+ return self.moe_kernel.apply(
1364
+ hidden_states=x,
1365
+ w1=layer.w13_weight,
1366
+ w2=layer.w2_weight,
1367
topk_weights=topk_weights,
1368
topk_ids=topk_ids,
1369
activation=layer.activation,
1496
- quant_config=self.moe_quant_config,
1497
- moe_config=layer.moe_config,
1370
+ global_num_experts=layer.global_num_experts,
1371
+ apply_router_weight_on_input=layer.apply_router_weight_on_input,
1372
expert_map=layer.expert_map,
1373
+ shared_experts_input=shared_experts_input,
1374
)
1375
1376
def apply_monolithic(