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 8, 10:00 UTC
[Refactor] Replace `activation: str` with `MoEActivation` enum (#33843)
Signed-off-by: mgoin <mgoin64@gmail.com> Signed-off-by: Michael Goin <mgoin64@gmail.com>
Michael Goin committed
Feb 11, 2026 at 20:29 UTC
ff1f83b056aedcf3e2d978d267011b2b79c08aca
48 files changed
+474
-282
benchmarks/kernels/benchmark_cutlass_moe_fp8.py
+2
-1
@@ -11,6 +11,7 @@ import torch
11
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
12
from tests.kernels.moe.utils import make_dummy_moe_config
13
from vllm import _custom_ops as ops
14
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
15
from vllm.model_executor.layers.fused_moe.config import fp8_w8a8_moe_quant_config
16
from vllm.model_executor.layers.fused_moe.cutlass_moe import CutlassExpertsFp8
17
from vllm.model_executor.layers.fused_moe.fused_moe import fused_experts, fused_topk
@@ -161,7 +162,7 @@ def bench_run(
162
w2_fp8q_cutlass,
163
topk_weights,
164
topk_ids,
164
- activation="silu",
165
+ activation=MoEActivation.SILU,
166
global_num_experts=num_experts,
167
)
168
torch.cuda.synchronize()
benchmarks/kernels/benchmark_moe.py
+3
-1
@@ -16,6 +16,7 @@ import torch
16
from ray.experimental.tqdm_ray import tqdm
17
18
from vllm.model_executor.layers.fused_moe import fused_topk
19
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
20
from vllm.model_executor.layers.fused_moe.config import (
21
FusedMoEConfig,
22
FusedMoEParallelConfig,
@@ -211,7 +212,8 @@ def benchmark_config(
212
hidden_dim=hidden_size,
213
intermediate_size_per_partition=shard_intermediate_size,
214
num_local_experts=num_experts,
214
- activation="silu",
215
+ num_logical_experts=num_experts,
216
+ activation=MoEActivation.SILU,
217
moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
218
in_dtype=init_dtype,
219
routing_method=RoutingMethodType.TopK,
tests/kernels/moe/modular_kernel_tools/common.py
+2
-1
@@ -22,6 +22,7 @@ from vllm.distributed import (
22
)
23
from vllm.forward_context import set_forward_context
24
from vllm.model_executor.layers.fused_moe import fused_topk
25
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
26
from vllm.model_executor.layers.fused_moe.all2all_utils import (
27
maybe_make_prepare_finalize,
28
)
@@ -599,7 +600,7 @@ def make_modular_kernel(
600
moe_parallel_config=moe_parallel_config,
601
in_dtype=config.dtype,
602
max_num_tokens=next_power_of_2(config.M),
602
- activation="silu",
603
+ activation=MoEActivation.SILU,
604
device=vllm_config.device_config.device,
605
routing_method=RoutingMethodType.DeepSeekV3,
606
)
tests/kernels/moe/test_cpu_fused_moe.py
+5
-4
@@ -6,6 +6,7 @@ import torch
6
7
from tests.kernels.allclose_default import get_default_atol, get_default_rtol
8
from vllm._custom_ops import cpu_fused_moe, cpu_prepack_moe_weight
9
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10
from vllm.model_executor.layers.fused_moe.cpu_fused_moe import _CPU_MOE_ACT_FN
11
from vllm.platforms import current_platform
12
from vllm.utils.torch_utils import set_random_seed
@@ -19,7 +20,7 @@ EXPERT_NUM = [
20
HIDDEN_DIM = [128, 2880]
21
INTERMEDIATE_DIM = [128, 2880]
22
BATCH_SIZE = [1, 64, 256]
22
-ACT = ["silu", "swigluoai"]
23
+ACT = [MoEActivation.SILU, MoEActivation.SWIGLUOAI]
24
USE_BIAS = [True, False]
25
ISA = ["amx", "vec"] if torch._C._cpu._is_amx_tile_supported() else ["vec"]
26
DTYPE = [torch.bfloat16]
@@ -33,7 +34,7 @@ def ref_fused_moe(
34
w2_bias: torch.Tensor | None,
35
topk_weights: torch.Tensor,
36
topk_ids: torch.Tensor,
36
- activation: str,
37
+ activation: MoEActivation,
38
) -> torch.Tensor:
39
len_experts = w13.size(0)
40
@@ -103,7 +104,7 @@ def test_cpu_fused_moe(
104
intermediate_size: int,
105
use_bias: bool,
106
dtype: torch.dtype,
106
- act: str,
107
+ act: MoEActivation,
108
isa: str,
109
):
110
set_random_seed(0)
@@ -153,7 +154,7 @@ def test_cpu_fused_moe(
154
w2_bias,
155
topk_weight,
156
topk_ids,
156
- act,
157
+ act.value,
158
isa,
159
)
160
tests/kernels/moe/test_cutlass_moe.py
+2
-1
@@ -12,6 +12,7 @@ from tests.kernels.moe.utils import make_dummy_moe_config
12
from vllm import _custom_ops as ops
13
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
14
from vllm.model_executor.layers.fused_moe import fused_experts, fused_topk
15
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
16
from vllm.model_executor.layers.fused_moe.config import (
17
FUSED_MOE_UNQUANTIZED_CONFIG,
18
FusedMoEQuantConfig,
@@ -531,7 +532,7 @@ def test_run_cutlass_moe_fp8(
532
c_strides1 = torch.full((e,), 2 * n, device="cuda", dtype=torch.int64)
533
c_strides2 = torch.full((e,), k, device="cuda", dtype=torch.int64)
534
534
- activation = "silu"
535
+ activation = MoEActivation.SILU
536
a1q, a1q_scale = moe_kernel_quantize_input(
537
mt.a, mt.a_scale, torch.float8_e4m3fn, per_act_token
538
)
tests/kernels/moe/test_deepep_deepgemm_moe.py
+2
-1
@@ -16,6 +16,7 @@ from typing_extensions import ParamSpec
16
17
from vllm.config import VllmConfig, set_current_vllm_config
18
from vllm.forward_context import set_forward_context
19
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
20
from vllm.model_executor.layers.fused_moe.config import (
21
FusedMoEQuantConfig,
22
fp8_w8a8_moe_quant_config,
@@ -324,7 +325,7 @@ def deepep_deepgemm_moe_impl(
325
w2=w2,
326
topk_weights=test_tensors.topk_weights,
327
topk_ids=test_tensors.topk,
327
- activation="silu",
328
+ activation=MoEActivation.SILU,
329
global_num_experts=num_experts,
330
expert_map=build_expert_map(),
331
apply_router_weight_on_input=False,
tests/kernels/moe/test_deepep_moe.py
+2
-1
@@ -15,6 +15,7 @@ from vllm import _custom_ops as ops
15
from vllm.config import VllmConfig, set_current_vllm_config
16
from vllm.model_executor.layers.activation import SiluAndMul
17
from vllm.model_executor.layers.fused_moe import TritonExperts
18
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
19
from vllm.model_executor.layers.fused_moe.config import (
20
FusedMoEQuantConfig,
21
)
@@ -260,7 +261,7 @@ def deep_ep_moe_impl(
261
w2=w2,
262
topk_weights=topk_weights_chunk,
263
topk_ids=topk_chunk,
263
- activation="silu",
264
+ activation=MoEActivation.SILU,
265
global_num_experts=num_experts,
266
expert_map=build_expert_map(),
267
apply_router_weight_on_input=False,
tests/kernels/moe/test_flashinfer.py
+12
-8
@@ -7,6 +7,7 @@ import torch
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
10
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
11
from vllm.model_executor.layers.fused_moe.config import (
12
FusedMoEConfig,
13
FusedMoEParallelConfig,
@@ -93,9 +94,14 @@ class TestData:
94
95
@staticmethod
96
def make_moe_tensors_8bit(
96
- m: int, k: int, n: int, e: int, is_trtllm: bool, activation: str = "silu"
97
+ m: int,
98
+ k: int,
99
+ n: int,
100
+ e: int,
101
+ is_trtllm: bool,
102
+ activation: MoEActivation = MoEActivation.SILU,
103
) -> "TestData":
98
- is_gated = activation != "relu2_no_mul"
104
+ is_gated = activation.is_gated
105
106
hidden_states = torch.randn((m, k), device="cuda", dtype=torch.bfloat16) / 10
107
w13 = torch.randn(
@@ -194,7 +200,7 @@ def test_flashinfer_per_tensor_moe_fp8_no_graph(
200
topk_weights=topk_weights,
201
topk_ids=topk_ids,
202
inplace=False,
197
- activation="silu",
203
+ activation=MoEActivation.SILU,
204
global_num_experts=e,
205
expert_map=None,
206
apply_router_weight_on_input=True,
@@ -219,21 +225,19 @@ def test_flashinfer_per_tensor_moe_fp8_no_graph(
225
@pytest.mark.parametrize("m,n,k", MNK_FACTORS)
226
@pytest.mark.parametrize("e", NUM_EXPERTS)
227
@pytest.mark.parametrize("topk", TOP_KS)
222
-@pytest.mark.parametrize("activation", ["silu", "relu2_no_mul"])
228
+@pytest.mark.parametrize("activation", [MoEActivation.SILU, MoEActivation.RELU2_NO_MUL])
229
def test_flashinfer_cutlass_moe_fp8_no_graph(
230
m: int,
231
n: int,
232
k: int,
233
e: int,
234
topk: int,
229
- activation: str,
235
+ activation: MoEActivation,
236
monkeypatch,
237
workspace_init,
238
):
239
set_random_seed(7)
240
monkeypatch.setenv("VLLM_FUSED_MOE_CHUNK_SIZE", "8192")
235
- assert activation in ["silu", "relu2_no_mul"]
236
- is_act_and_mul = activation == "silu_and_mul"
241
with set_current_vllm_config(vllm_config):
242
td = TestData.make_moe_tensors_8bit(
243
m, k, n, e, is_trtllm=False, activation=activation
@@ -292,7 +296,7 @@ def test_flashinfer_cutlass_moe_fp8_no_graph(
296
device="cuda",
297
moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
298
in_dtype=torch.bfloat16,
295
- is_act_and_mul=is_act_and_mul,
299
+ is_act_and_mul=activation.is_gated,
300
routing_method=RoutingMethodType.TopK,
301
)
302
tests/kernels/moe/test_flashinfer_moe.py
+5
-6
@@ -13,6 +13,7 @@ from tests.kernels.utils import torch_moe
13
from vllm import _custom_ops as ops
14
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
15
from vllm.model_executor.layers.fused_moe import fused_topk
16
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
17
from vllm.model_executor.layers.fused_moe.config import (
18
FusedMoEConfig,
19
FusedMoEParallelConfig,
@@ -54,7 +55,7 @@ MNK_FACTORS = [
55
@pytest.mark.parametrize("e", [40, 64, 256])
56
@pytest.mark.parametrize("topk", [1, 6, 8])
57
@pytest.mark.parametrize("dtype", [torch.bfloat16])
57
-@pytest.mark.parametrize("activation", ["silu_and_mul", "relu2"])
58
+@pytest.mark.parametrize("activation", [MoEActivation.SILU, MoEActivation.RELU2_NO_MUL])
59
@torch.inference_mode()
60
def test_flashinfer_fp4_moe_no_graph(
61
m: int,
@@ -63,7 +64,7 @@ def test_flashinfer_fp4_moe_no_graph(
64
e: int,
65
topk: int,
66
dtype: torch.dtype,
66
- activation: str,
67
+ activation: MoEActivation,
68
workspace_init,
69
):
70
set_random_seed(7)
@@ -73,7 +74,7 @@ def test_flashinfer_fp4_moe_no_graph(
74
a = torch.randn((m, k), device="cuda", dtype=dtype) / 10
75
76
quant_blocksize = 16
76
- is_gated_act = activation == "silu_and_mul"
77
+ is_gated_act = activation.is_gated
78
79
w1_q, w2_q, quant_config = make_test_quant_config(
80
e,
@@ -112,15 +113,13 @@ def test_flashinfer_fp4_moe_no_graph(
113
inplace=False,
114
)
115
115
- fi_activation = {"silu_and_mul": "silu", "relu2": "relu2_no_mul"}[activation]
116
-
116
flashinfer_output = flashinfer_experts(
117
hidden_states=a,
118
w1=w1_q,
119
w2=w2_q,
120
topk_weights=topk_weights,
121
topk_ids=topk_ids,
123
- activation=fi_activation,
122
+ activation=activation,
123
)
124
125
# Reference check:
tests/kernels/moe/test_modular_oai_triton_moe.py
+2
-1
@@ -7,6 +7,7 @@ Test modular OAI Triton MoE
7
import pytest
8
import torch
9
10
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
11
from vllm.utils.import_utils import has_triton_kernels
12
13
if not has_triton_kernels():
@@ -192,7 +193,7 @@ def oai_triton_moe_impl(
193
w2=w2,
194
topk_weights=topk_weights,
195
topk_ids=topk_ids,
195
- activation="swigluoai",
196
+ activation=MoEActivation.SWIGLUOAI,
197
global_num_experts=num_experts,
198
expert_map=None,
199
apply_router_weight_on_input=False,
tests/kernels/moe/test_moe.py
+19
-5
@@ -29,6 +29,7 @@ from vllm.config import VllmConfig, set_current_vllm_config
29
from vllm.distributed.parallel_state import init_distributed_environment
30
from vllm.forward_context import get_forward_context, set_forward_context
31
from vllm.model_executor.layers.fused_moe import (
32
+ MoEActivation,
33
fused_topk,
34
)
35
from vllm.model_executor.layers.fused_moe.config import (
@@ -1155,7 +1156,10 @@ def test_fused_marlin_moe_with_bias(m):
1156
@pytest.mark.parametrize("m", [1, 64, 256])
1157
@pytest.mark.parametrize("n,k", [(1024, 1024), (2048, 2048)])
1158
@pytest.mark.parametrize("e,topk", [(8, 2), (64, 4)])
1158
-def test_fused_marlin_moe_non_gated(m: int, n: int, k: int, e: int, topk: int):
1159
+@pytest.mark.parametrize("activation", [MoEActivation.RELU2_NO_MUL])
1160
+def test_fused_marlin_moe_non_gated(
1161
+ m: int, n: int, k: int, e: int, topk: int, activation: MoEActivation
1162
+):
1163
"""Test Marlin MoE with non-gated activation (relu2_no_mul).
1164
1165
Non-gated activations like relu2 don't have the gate-up projection pattern,
@@ -1198,7 +1202,7 @@ def test_fused_marlin_moe_non_gated(m: int, n: int, k: int, e: int, topk: int):
1202
w2_data.w_ref,
1203
score,
1204
topk,
1201
- activation="relu2",
1205
+ activation=activation,
1206
)
1207
1208
marlin_output = fused_marlin_moe(
@@ -1223,7 +1227,7 @@ def test_fused_marlin_moe_non_gated(m: int, n: int, k: int, e: int, topk: int):
1227
w2_zeros=w2_data.zeros,
1228
quant_type_id=quant_type.id,
1229
is_k_full=is_k_full,
1226
- activation="relu2_no_mul",
1230
+ activation=activation,
1231
)
1232
1233
torch.testing.assert_close(marlin_output, torch_output, atol=1e-1, rtol=0)
@@ -1330,9 +1334,18 @@ def test_moe_sum(m: int, topk: int, k: int, dtype: torch.dtype):
1334
@pytest.mark.parametrize("topk", [2])
1335
@pytest.mark.parametrize("dtype", [torch.float32, torch.bfloat16])
1336
@pytest.mark.parametrize("with_bias", [False, True])
1333
-@pytest.mark.parametrize("activation", ["silu"])
1337
+@pytest.mark.parametrize("activation", [MoEActivation.SILU])
1338
@pytest.mark.skipif(not current_platform.is_cpu(), reason="CPU only test")
1335
-def test_cpu_fused_moe_basic(m, n, k, e, topk, dtype, with_bias, activation):
1339
+def test_cpu_fused_moe_basic(
1340
+ m: int,
1341
+ n: int,
1342
+ k: int,
1343
+ e: int,
1344
+ topk: int,
1345
+ dtype: torch.dtype,
1346
+ with_bias: bool,
1347
+ activation: MoEActivation,
1348
+):
1349
from vllm.model_executor.layers.fused_moe.cpu_fused_moe import CPUFusedMOE
1350
1351
device = "cpu"
@@ -1608,6 +1621,7 @@ def test_unquantized_bf16_flashinfer_trtllm_backend(
1621
hidden_dim=k,
1622
intermediate_size_per_partition=n,
1623
num_local_experts=e,
1624
+ num_logical_experts=e,
1625
activation="silu",
1626
device="cuda",
1627
moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
tests/kernels/moe/test_pplx_cutlass_moe.py
+2
-1
@@ -9,6 +9,7 @@ from tests.kernels.utils import torch_experts
9
from vllm import _custom_ops as ops
10
from vllm.config import VllmConfig, set_current_vllm_config
11
from vllm.model_executor.layers.fused_moe import fused_topk
12
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
13
from vllm.model_executor.layers.fused_moe.config import (
14
FusedMoEConfig,
15
FusedMoEParallelConfig,
@@ -149,7 +150,7 @@ def pplx_cutlass_moe(
150
num_local_experts=num_local_experts,
151
num_logical_experts=num_experts,
152
moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
152
- activation="silu",
153
+ activation=MoEActivation.SILU,
154
in_dtype=torch.bfloat16,
155
device="cuda",
156
routing_method=RoutingMethodType.Llama4,
tests/kernels/moe/test_triton_moe_no_act_mul.py
+14
-14
@@ -11,15 +11,11 @@ import pytest
11
import torch
12
13
from tests.kernels.moe.utils import make_dummy_moe_config
14
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
15
from vllm.model_executor.layers.fused_moe.config import (
16
FUSED_MOE_UNQUANTIZED_CONFIG,
17
)
18
from vllm.model_executor.layers.fused_moe.fused_moe import TritonExperts
18
-from vllm.model_executor.layers.fused_moe.utils import (
19
- GELU_NO_MUL,
20
- RELU2_NO_MUL,
21
- SILU_NO_MUL,
22
-)
19
from vllm.platforms import current_platform
20
21
# Test parameters
@@ -28,7 +24,11 @@ N_SIZES = [128, 256]
24
K_SIZES = [64, 128]
25
TOPK_VALUES = [1, 2]
26
NUM_EXPERTS = 8
31
-NO_MUL_ACTIVATIONS = [SILU_NO_MUL, GELU_NO_MUL, RELU2_NO_MUL]
27
+NO_MUL_ACTIVATIONS = [
28
+ MoEActivation.SILU_NO_MUL,
29
+ MoEActivation.GELU_NO_MUL,
30
+ MoEActivation.RELU2_NO_MUL,
31
+]
32
33
34
def make_test_tensors(
@@ -73,7 +73,7 @@ def test_triton_experts_no_mul_activation(
73
n: int,
74
k: int,
75
topk: int,
76
- activation: str,
76
+ activation: MoEActivation,
77
):
78
hidden_states, w1, w2, topk_weights, topk_ids = make_test_tensors(
79
m, n, k, NUM_EXPERTS, topk
@@ -161,11 +161,11 @@ def test_workspace_shapes_no_mul_vs_gated():
161
)
162
163
ws1_no_mul, _, out_no_mul = experts.workspace_shapes(
164
- M, N, K, topk, 8, 8, None, SILU_NO_MUL
164
+ M, N, K, topk, 8, 8, None, MoEActivation.SILU_NO_MUL
165
)
166
167
ws1_gated, _, out_gated = experts.workspace_shapes(
168
- M, N, K, topk, 8, 8, None, "silu"
168
+ M, N, K, topk, 8, 8, None, MoEActivation.SILU
169
)
170
171
# For no_mul: activation_out_dim = N
@@ -202,10 +202,10 @@ def test_adjust_n_for_activation():
202
N = 256
203
204
# Gated activations should return N // 2
205
- assert experts.adjust_N_for_activation(N, "silu") == N // 2
206
- assert experts.adjust_N_for_activation(N, "gelu") == N // 2
205
+ assert experts.adjust_N_for_activation(N, MoEActivation.SILU) == N // 2
206
+ assert experts.adjust_N_for_activation(N, MoEActivation.GELU) == N // 2
207
208
# Non-gated activations should return N
209
- assert experts.adjust_N_for_activation(N, SILU_NO_MUL) == N
210
- assert experts.adjust_N_for_activation(N, GELU_NO_MUL) == N
211
- assert experts.adjust_N_for_activation(N, RELU2_NO_MUL) == N
209
+ assert experts.adjust_N_for_activation(N, MoEActivation.SILU_NO_MUL) == N
210
+ assert experts.adjust_N_for_activation(N, MoEActivation.GELU_NO_MUL) == N
211
+ assert experts.adjust_N_for_activation(N, MoEActivation.RELU2_NO_MUL) == N
tests/kernels/moe/utils.py
+2
-1
@@ -12,6 +12,7 @@ from vllm.model_executor.layers.fused_moe import (
12
fused_experts,
13
fused_topk,
14
)
15
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
16
from vllm.model_executor.layers.fused_moe.config import (
17
FusedMoEConfig,
18
FusedMoEParallelConfig,
@@ -54,7 +55,7 @@ def make_dummy_moe_config(
55
num_local_experts=num_experts,
56
num_logical_experts=num_experts,
57
moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
57
- activation="silu",
58
+ activation=MoEActivation.SILU,
59
in_dtype=in_dtype,
60
device="cuda",
61
routing_method=RoutingMethodType.TopK,
tests/kernels/utils.py
+4
-3
@@ -15,6 +15,7 @@ from torch._prims_common import TensorLikeType
15
from tests.kernels.quant_utils import native_w8a8_block_matmul
16
from vllm.model_executor.custom_op import op_registry
17
from vllm.model_executor.layers.activation import SiluAndMul
18
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
19
from vllm.model_executor.layers.fused_moe.utils import moe_kernel_quantize_input
20
from vllm.utils.torch_utils import make_tensor_with_pad
21
from vllm.v1.attention.backend import AttentionType
@@ -840,7 +841,7 @@ def torch_experts(
841
per_act_token_quant=False,
842
block_shape: list[int] | None = None,
843
apply_router_weights_on_input: bool = False,
843
- activation: str = "silu_and_mul",
844
+ activation: MoEActivation = MoEActivation.SILU,
845
) -> torch.Tensor:
846
assert (
847
global_num_experts == -1
@@ -883,7 +884,7 @@ def torch_experts(
884
885
f32 = torch.float32
886
886
- act = op_registry[activation]
887
+ act = op_registry[activation.custom_op_name]
888
889
for i in range(num_experts):
890
mask = topk_ids == i
@@ -973,7 +974,7 @@ def torch_moe(
974
b_bias2: torch.Tensor | None = None,
975
global_num_experts: int = -1,
976
expert_map: torch.Tensor | None = None,
976
- activation: str = "silu_and_mul",
977
+ activation: MoEActivation = MoEActivation.SILU,
978
) -> torch.Tensor:
979
score = torch.softmax(score, dim=-1, dtype=torch.float32)
980
topk_weight, topk_ids = torch.topk(score, topk)
vllm/model_executor/layers/fused_moe/__init__.py
+7
-1
@@ -4,6 +4,11 @@
4
from contextlib import contextmanager
5
from typing import Any
6
7
+from vllm.model_executor.layers.fused_moe.activation import (
8
+ MoEActivation,
9
+ activation_without_mul,
10
+ apply_moe_activation,
11
+)
12
from vllm.model_executor.layers.fused_moe.config import (
13
FusedMoEConfig,
14
RoutingMethodType,
@@ -27,7 +32,6 @@ from vllm.model_executor.layers.fused_moe.shared_fused_moe import SharedFusedMoE
32
from vllm.model_executor.layers.fused_moe.unquantized_fused_moe_method import (
33
UnquantizedFusedMoEMethod,
34
)
30
-from vllm.model_executor.layers.fused_moe.utils import activation_without_mul
35
from vllm.model_executor.layers.fused_moe.zero_expert_fused_moe import (
36
ZeroExpertFusedMoE,
37
)
@@ -54,6 +58,7 @@ __all__ = [
58
"FusedMoERouter",
59
"FusedMoEConfig",
60
"FusedMoEMethodBase",
61
+ "MoEActivation",
62
"UnquantizedFusedMoEMethod",
63
"FusedMoeWeightScaleSupported",
64
"FusedMoEPermuteExpertsUnpermute",
@@ -63,6 +68,7 @@ __all__ = [
68
"SharedFusedMoE",
69
"ZeroExpertFusedMoE",
70
"activation_without_mul",
71
+ "apply_moe_activation",
72
"override_config",
73
"get_config",
74
]
vllm/model_executor/layers/fused_moe/activation.py
new
+136
@@ -0,0 +1,136 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+"""MoE activation function enum and utilities."""
4
+
5
+from enum import Enum
6
+
7
+import torch
8
+import torch.nn.functional as F
9
+
10
+
11
+class MoEActivation(Enum):
12
+ """Activation functions for MoE layers."""
13
+
14
+ # Gated activations (gate * activation(up)) expect input of shape [..., 2*d]
15
+ # and produce output of shape [..., d]
16
+ SILU = "silu"
17
+ GELU = "gelu"
18
+ RELU2 = "relu2"
19
+ SWIGLUOAI = "swigluoai"
20
+ SWIGLUSTEP = "swiglustep"
21
+
22
+ # Non-gated activations (no mul with gate) expect input of shape [..., d]
23
+ # and produce output of shape [..., d].
24
+ # NOTE: Non-gated activations require the "_no_mul" suffix to be present.
25
+ SILU_NO_MUL = "silu_no_mul"
26
+ GELU_NO_MUL = "gelu_no_mul"
27
+ RELU2_NO_MUL = "relu2_no_mul"
28
+
29
+ @property
30
+ def is_gated(self) -> bool:
31
+ """Returns True if activation expects gate*activation(up) pattern.
32
+
33
+ Gated activations expect input tensor with 2x the output size,
34
+ where the first half is the gate and second half is the up projection.
35
+ """
36
+ return not self.value.endswith("_no_mul")
37
+
38
+ @property
39
+ def custom_op_name(self) -> str:
40
+ """Maps to the CustomOp name of activations
41
+ in vllm/model_executor/layers/activation.py."""
42
+ return _CUSTOM_OP_NAMES[self]
43
+
44
+ def without_mul(self) -> "MoEActivation":
45
+ """Get the non-gated variant of this activation.
46
+
47
+ For activations that have a _no_mul variant, returns that variant.
48
+ For activations without a _no_mul variant (or already _no_mul),
49
+ returns self.
50
+ """
51
+ return _WITHOUT_MUL.get(self, self)
52
+
53
+ @classmethod
54
+ def from_str(cls, s: str) -> "MoEActivation":
55
+ """Parse from string for backward compatibility."""
56
+ for member in cls:
57
+ if member.value == s:
58
+ return member
59
+ valid = [m.value for m in cls]
60
+ raise ValueError(f"Unknown MoE activation: {s!r}. Valid activations: {valid}")
61
+
62
+
63
+# Module-level lookup tables used by MoEActivation functions.
64
+_CUSTOM_OP_NAMES: dict[MoEActivation, str] = {
65
+ MoEActivation.SILU: "silu_and_mul",
66
+ MoEActivation.GELU: "gelu_and_mul",
67
+ MoEActivation.SWIGLUOAI: "swigluoai_and_mul",
68
+ MoEActivation.SWIGLUSTEP: "swiglustep_and_mul",
69
+ MoEActivation.RELU2: "relu2",
70
+ MoEActivation.SILU_NO_MUL: "silu_and_mul",
71
+ MoEActivation.GELU_NO_MUL: "gelu_and_mul",
72
+ MoEActivation.RELU2_NO_MUL: "relu2",
73
+}
74
+
75
+_WITHOUT_MUL: dict[MoEActivation, MoEActivation] = {
76
+ MoEActivation.SILU: MoEActivation.SILU_NO_MUL,
77
+ MoEActivation.GELU: MoEActivation.GELU_NO_MUL,
78
+ MoEActivation.RELU2: MoEActivation.RELU2_NO_MUL,
79
+}
80
+
81
+
82
+def activation_without_mul(activation: str) -> str:
83
+ """Get the non-gated variant of an activation function.
84
+
85
+ Args:
86
+ activation: The activation function name (e.g., "silu", "gelu")
87
+
88
+ Returns:
89
+ The non-gated activation name (e.g., "silu_no_mul", "gelu_no_mul")
90
+ """
91
+ return MoEActivation.from_str(activation).without_mul().value
92
+
93
+
94
+def apply_moe_activation(
95
+ activation: MoEActivation,
96
+ output: torch.Tensor,
97
+ input: torch.Tensor,
98
+) -> torch.Tensor:
99
+ """Apply MoE activation function."""
100
+ assert input.dim() == 2, "Input must be 2D"
101
+ assert output.dim() == 2, "Output must be 2D"
102
+ if activation.is_gated:
103
+ assert output.size(-1) * 2 == input.size(-1), (
104
+ f"{activation.value} expects 2x ratio: "
105
+ f"{output.size(-1) * 2} vs {input.size(-1)}"
106
+ )
107
+ else:
108
+ assert output.size(-1) == input.size(-1), (
109
+ f"{activation.value} expects equal sizes: "
110
+ f"{output.size(-1)} vs {input.size(-1)}"
111
+ )
112
+
113
+ # Activations with gated multiplication (gate × activation(up))
114
+ if activation == MoEActivation.SILU:
115
+ torch.ops._C.silu_and_mul(output, input)
116
+ elif activation == MoEActivation.GELU:
117
+ torch.ops._C.gelu_and_mul(output, input)
118
+ elif activation == MoEActivation.SWIGLUOAI:
119
+ torch.ops._C.swigluoai_and_mul(output, input)
120
+ elif activation == MoEActivation.SWIGLUSTEP:
121
+ from vllm.model_executor.layers.activation import swiglustep_and_mul_triton
122
+
123
+ swiglustep_and_mul_triton(output, input)
124
+
125
+ # Activations without gated multiplication
126
+ elif activation == MoEActivation.SILU_NO_MUL:
127
+ output.copy_(F.silu(input))
128
+ elif activation == MoEActivation.GELU_NO_MUL:
129
+ output.copy_(F.gelu(input))
130
+ elif activation == MoEActivation.RELU2_NO_MUL:
131
+ F.relu(input, inplace=True)
132
+ torch.square(input, out=output)
133
+ else:
134
+ raise ValueError(f"Unsupported FusedMoe activation: {activation}")
135
+
136
+ return output
vllm/model_executor/layers/fused_moe/batched_deep_gemm_moe.py
+5
-4
@@ -7,6 +7,7 @@ import torch
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm.forward_context import get_forward_context, is_forward_context_available
9
from vllm.logger import init_logger
10
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
11
from vllm.model_executor.layers.fused_moe.config import (
12
FusedMoEConfig,
13
FusedMoEParallelConfig,
@@ -303,8 +304,8 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
304
return (weight_key, activation_key) in SUPPORTED_W_A
305
306
@staticmethod
306
- def _supports_activation(activation: str) -> bool:
307
- return activation in ["silu"]
307
+ def _supports_activation(activation: MoEActivation) -> bool:
308
+ return activation == MoEActivation.SILU
309
310
@staticmethod
311
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -338,7 +339,7 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
339
global_num_experts: int,
340
local_num_experts: int,
341
expert_tokens_meta: mk.ExpertTokensMetadata | None,
341
- activation: str,
342
+ activation: MoEActivation,
343
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
344
# FIXME (varun): We should be able to dispatch only from the leader
345
# DP ranks in the case of TP > 1. At the moment, all the Ranks
@@ -389,7 +390,7 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
390
w2: torch.Tensor,
391
topk_weights: torch.Tensor,
392
topk_ids: torch.Tensor,
392
- activation: str,
393
+ activation: MoEActivation,
394
global_num_experts: int,
395
expert_map: torch.Tensor | None,
396
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/config.py
+2
-1
@@ -14,6 +14,7 @@ from vllm.distributed import (
14
get_tensor_model_parallel_rank,
15
)
16
from vllm.logger import init_logger
17
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
18
from vllm.model_executor.layers.quantization.utils.ocp_mx_utils import (
19
OCP_MX_DTYPES,
20
OCP_MX_Scheme,
@@ -1132,7 +1133,7 @@ class FusedMoEConfig:
1133
intermediate_size_per_partition: int
1134
num_local_experts: int
1135
num_logical_experts: int
1135
- activation: str
1136
+ activation: MoEActivation
1137
device: torch.device | str
1138
routing_method: RoutingMethodType
1139
moe_parallel_config: FusedMoEParallelConfig
vllm/model_executor/layers/fused_moe/cpu_fused_moe.py
+12
-10
@@ -9,6 +9,7 @@ from torch.nn import functional as F
9
from vllm import _custom_ops as ops
10
from vllm._custom_ops import cpu_fused_moe, cpu_prepack_moe_weight
11
from vllm.model_executor.layers.activation import SiluAndMul
12
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
13
from vllm.model_executor.layers.quantization.utils.layer_utils import replace_parameter
14
from vllm.utils.torch_utils import direct_register_custom_op
15
@@ -36,9 +37,9 @@ def _swigluoai_forward_native(
37
# Map activation names to their native forward functions.
38
# Uses static methods or standalone functions to avoid instantiating CustomOp
39
# classes, which would call get_current_vllm_config() before config is set.
39
-_CPU_MOE_ACT_FN: dict[str, Callable[[torch.Tensor], torch.Tensor]] = {
40
- "silu": SiluAndMul.forward_native,
41
- "swigluoai": _swigluoai_forward_native,
40
+_CPU_MOE_ACT_FN: dict[MoEActivation, Callable[[torch.Tensor], torch.Tensor]] = {
41
+ MoEActivation.SILU: SiluAndMul.forward_native,
42
+ MoEActivation.SWIGLUOAI: _swigluoai_forward_native,
43
}
44
45
@@ -168,9 +169,9 @@ class SGLFusedMOE:
169
routed_scaling_factor: float = 1.0,
170
e_score_correction_bias: torch.Tensor | None = None,
171
apply_router_weight_on_input: bool = False,
171
- activation: str = "silu",
172
+ activation: MoEActivation = MoEActivation.SILU,
173
) -> torch.Tensor:
173
- assert activation == "silu", f"{activation} is not supported."
174
+ assert activation == MoEActivation.SILU, f"{activation} is not supported."
175
assert not apply_router_weight_on_input
176
topk_weights, topk_ids = select_experts(
177
hidden_states=x,
@@ -235,7 +236,7 @@ class CPUFusedMOE:
236
routed_scaling_factor: float = 1.0,
237
e_score_correction_bias: torch.Tensor | None = None,
238
apply_router_weight_on_input: bool = False,
238
- activation: str = "silu",
239
+ activation: MoEActivation = MoEActivation.SILU,
240
) -> torch.Tensor:
241
assert activation in _CPU_MOE_ACT_FN, f"{activation} is not supported."
242
@@ -353,7 +354,7 @@ class CPUFusedMOE:
354
input: torch.Tensor,
355
topk_weights: torch.Tensor,
356
topk_ids: torch.Tensor,
356
- activation: str,
357
+ activation: MoEActivation,
358
global_num_experts: int = -1,
359
skip_weighted: bool = False,
360
) -> torch.Tensor:
@@ -371,7 +372,7 @@ class CPUFusedMOE:
372
getattr(layer, "w2_bias", None),
373
topk_weights,
374
topk_ids,
374
- activation,
375
+ activation.value,
376
self.isa,
377
skip_weighted,
378
)
@@ -383,7 +384,7 @@ class CPUFusedMOE:
384
input: torch.Tensor,
385
topk_weights: torch.Tensor,
386
topk_ids: torch.Tensor,
386
- activation: str,
387
+ activation: MoEActivation,
388
global_num_experts: int = -1,
389
skip_weighted: bool = False,
390
) -> torch.Tensor:
@@ -419,6 +420,7 @@ def cpu_fused_moe_torch(
420
global_num_experts: int = -1,
421
skip_weighted: bool = False,
422
) -> None:
423
+ act = MoEActivation.from_str(activation)
424
layer = _CPU_MOE_LAYER_CACHE[layer_id]()
425
426
# Ref code from https://github.com/sgl-project/sglang/blob/716e682721397df103f347d22da8bd46c6016dab/python/sglang/srt/layers/moe/fused_moe_native.py#L53
@@ -442,7 +444,7 @@ def cpu_fused_moe_torch(
444
tokens_for_this_expert = sorted_tokens[start_idx:end_idx]
445
446
gate_up = layer.gate_up_linear[i](tokens_for_this_expert) # type: ignore
445
- gate_up = _CPU_MOE_ACT_FN[activation](gate_up)
447
+ gate_up = _CPU_MOE_ACT_FN[act](gate_up)
448
expert_out = layer.down_linear[i](gate_up) # type: ignore
449
outputs.append(expert_out)
450
start_idx = end_idx
vllm/model_executor/layers/fused_moe/cutlass_moe.py
+31
-20
@@ -7,6 +7,10 @@ import torch
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm import _custom_ops as ops
9
from vllm.logger import init_logger
10
+from vllm.model_executor.layers.fused_moe.activation import (
11
+ MoEActivation,
12
+ apply_moe_activation,
13
+)
14
from vllm.model_executor.layers.fused_moe.config import (
15
FusedMoEConfig,
16
FusedMoEParallelConfig,
@@ -25,7 +29,6 @@ from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
29
)
30
from vllm.model_executor.layers.fused_moe.utils import (
31
_resize_cache,
28
- apply_moe_activation,
32
)
33
from vllm.model_executor.layers.quantization.utils.quant_utils import (
34
QuantKey,
@@ -51,7 +54,7 @@ def run_cutlass_moe_fp8(
54
w1: torch.Tensor,
55
w2: torch.Tensor,
56
topk_ids: torch.Tensor,
54
- activation: str,
57
+ activation: MoEActivation,
58
global_num_experts: int,
59
expert_map: torch.Tensor | None,
60
w1_scale: torch.Tensor | None,
@@ -73,7 +76,7 @@ def run_cutlass_moe_fp8(
76
):
77
a1q = hidden_states
78
76
- assert not activation.endswith("_no_mul"), "Only gated activation is supported"
79
+ assert activation.is_gated, "Only gated activation is supported"
80
assert w1_scale is not None
81
assert w2_scale is not None
82
assert w1.dtype == torch.float8_e4m3fn
@@ -310,8 +313,12 @@ class CutlassExpertsFp8Base(mk.FusedMoEPermuteExpertsUnpermute):
313
return (weight_key, activation_key) in SUPPORTED_W_A
314
315
@staticmethod
313
- def _supports_activation(activation: str) -> bool:
314
- return activation in ["silu", "gelu", "swigluoai"]
316
+ def _supports_activation(activation: MoEActivation) -> bool:
317
+ return activation in [
318
+ MoEActivation.SILU,
319
+ MoEActivation.GELU,
320
+ MoEActivation.SWIGLUOAI,
321
+ ]
322
323
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
324
# Let PrepareAndFinalize::finalize() decide the impl.
@@ -325,7 +332,7 @@ class CutlassExpertsFp8Base(mk.FusedMoEPermuteExpertsUnpermute):
332
w2: torch.Tensor,
333
topk_weights: torch.Tensor,
334
topk_ids: torch.Tensor,
328
- activation: str,
335
+ activation: MoEActivation,
336
global_num_experts: int,
337
expert_map: torch.Tensor | None,
338
a1q_scale: torch.Tensor | None,
@@ -415,7 +422,7 @@ class CutlassExpertsFp8(CutlassExpertsFp8Base):
422
global_num_experts: int,
423
local_num_experts: int,
424
expert_tokens_meta: mk.ExpertTokensMetadata | None,
418
- activation: str,
425
+ activation: MoEActivation,
426
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
427
activation_out_dim = self.adjust_N_for_activation(N, activation)
428
workspace1 = (M * topk, max(N, K))
@@ -456,7 +463,7 @@ class CutlassBatchedExpertsFp8(CutlassExpertsFp8Base):
463
global_num_experts: int,
464
local_num_experts: int,
465
expert_tokens_meta: mk.ExpertTokensMetadata | None,
459
- activation: str,
466
+ activation: MoEActivation,
467
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
468
num_dp = self.num_dispatchers
469
assert num_dp is not None
@@ -489,7 +496,7 @@ def run_cutlass_moe_fp4(
496
w2_alphas: torch.Tensor,
497
topk_weights: torch.Tensor,
498
topk_ids: torch.Tensor,
492
- activation: str,
499
+ activation: MoEActivation,
500
workspace13: torch.Tensor,
501
workspace2: torch.Tensor,
502
m: int,
@@ -612,7 +619,7 @@ def run_cutlass_moe_fp4(
619
blockscale_offsets[:-1],
620
)
621
del rep_a_fp4, rep_a_blockscale
615
- if activation == "silu":
622
+ if activation == MoEActivation.SILU:
623
# Fused SiLU+Mul+NVFP4 quantization
624
# Note: c2 workspace is no longer needed since SiLU is fused with quantization.
625
# c3 reuses workspace13 after c1 is consumed.
@@ -682,8 +689,12 @@ class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
689
return (weight_key, activation_key) == (kNvfp4Static, kNvfp4Dynamic)
690
691
@staticmethod
685
- def _supports_activation(activation: str) -> bool:
686
- return activation in ["silu", "gelu", "swigluoai"]
692
+ def _supports_activation(activation: MoEActivation) -> bool:
693
+ return activation in [
694
+ MoEActivation.SILU,
695
+ MoEActivation.GELU,
696
+ MoEActivation.SWIGLUOAI,
697
+ ]
698
699
@staticmethod
700
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -716,7 +727,7 @@ class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
727
global_num_experts: int,
728
local_num_experts: int,
729
expert_tokens_meta: mk.ExpertTokensMetadata | None,
719
- activation: str,
730
+ activation: MoEActivation,
731
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
732
workspace1 = (M * topk, max(2 * N, K))
733
workspace2 = (M * topk, N)
@@ -731,7 +742,7 @@ class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
742
w2: torch.Tensor,
743
topk_weights: torch.Tensor,
744
topk_ids: torch.Tensor,
734
- activation: str,
745
+ activation: MoEActivation,
746
global_num_experts: int,
747
expert_map: torch.Tensor | None,
748
a1q_scale: torch.Tensor | None, # unused
@@ -776,7 +787,7 @@ def run_cutlass_moe_w4a8_fp8(
787
w1: torch.Tensor,
788
w2: torch.Tensor,
789
topk_ids: torch.Tensor,
779
- activation: str,
790
+ activation: MoEActivation,
791
global_num_experts: int,
792
expert_map: torch.Tensor | None,
793
w1_scale: torch.Tensor | None,
@@ -970,7 +981,7 @@ class CutlassExpertsW4A8Fp8(mk.FusedMoEPermuteExpertsUnpermute):
981
)
982
983
@staticmethod
973
- def _supports_activation(activation: str) -> bool:
984
+ def _supports_activation(activation: MoEActivation) -> bool:
985
raise NotImplementedError(
986
"CutlassExpertsW4A8Fp8 is not yet used by an Oracle. "
987
"This method should not be called."
@@ -1005,7 +1016,7 @@ class CutlassExpertsW4A8Fp8(mk.FusedMoEPermuteExpertsUnpermute):
1016
global_num_experts: int,
1017
local_num_experts: int,
1018
expert_tokens_meta: mk.ExpertTokensMetadata | None,
1008
- activation: str,
1019
+ activation: MoEActivation,
1020
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
1021
activation_out_dim = self.adjust_N_for_activation(N, activation)
1022
workspace1 = (M * topk, max(N, K))
@@ -1021,7 +1032,7 @@ class CutlassExpertsW4A8Fp8(mk.FusedMoEPermuteExpertsUnpermute):
1032
w2: torch.Tensor,
1033
topk_weights: torch.Tensor,
1034
topk_ids: torch.Tensor,
1024
- activation: str,
1035
+ activation: MoEActivation,
1036
global_num_experts: int,
1037
expert_map: torch.Tensor | None,
1038
a1q_scale: torch.Tensor | None,
@@ -1094,7 +1105,7 @@ def cutlass_moe_w4a8_fp8(
1105
s_strides2: torch.Tensor,
1106
quant_config: FusedMoEQuantConfig,
1107
moe_config: FusedMoEConfig,
1097
- activation: str = "silu",
1108
+ activation: MoEActivation = MoEActivation.SILU,
1109
expert_map: torch.Tensor | None = None,
1110
apply_router_weight_on_input: bool = False,
1111
global_num_experts: int = -1,
@@ -1137,7 +1148,7 @@ def cutlass_moe_w4a8_fp8(
1148
dtype: torch.int64
1149
- per_act_token (Optional[bool]): Whether the scale is per-token or
1150
per-tensor.
1140
- - activation (str): The activation function to use.
1151
+ - activation (MoEActivation): The activation function to use.
1152
- expert_map (Optional[torch.Tensor]): In the case of Expert parallel,
1153
every Rank is responsible for a subset of experts. expert_map is a
1154
mapping from global expert-id to local expert-id. When expert_map[i]
vllm/model_executor/layers/fused_moe/deep_gemm_moe.py
+7
-6
@@ -5,6 +5,7 @@ import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm.logger import init_logger
8
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
9
from vllm.model_executor.layers.fused_moe.config import (
10
FusedMoEConfig,
11
FusedMoEParallelConfig,
@@ -145,8 +146,8 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
146
return (weight_key, activation_key) in SUPPORTED_W_A
147
148
@staticmethod
148
- def _supports_activation(activation: str) -> bool:
149
- return activation in ["silu", "swiglustep"]
149
+ def _supports_activation(activation: MoEActivation) -> bool:
150
+ return activation in [MoEActivation.SILU, MoEActivation.SWIGLUSTEP]
151
152
@staticmethod
153
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -171,7 +172,7 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
172
global_num_experts: int,
173
local_num_experts: int,
174
expert_tokens_meta: mk.ExpertTokensMetadata | None,
174
- activation: str,
175
+ activation: MoEActivation,
176
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
177
assert self.block_shape is not None
178
block_m = self.block_shape[0]
@@ -187,7 +188,7 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
188
return (workspace1, workspace2, output)
189
190
def _act_mul_quant(
190
- self, input: torch.Tensor, output: torch.Tensor, activation: str
191
+ self, input: torch.Tensor, output: torch.Tensor, activation: MoEActivation
192
) -> tuple[torch.Tensor, torch.Tensor]:
193
assert self.block_shape is not None
194
block_k = self.block_shape[1]
@@ -210,7 +211,7 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
211
return a2q, a2q_scale
212
213
# 2. Hopper / non‑E8M0: prefer the fused SiLU+mul+quant kernel
213
- if activation == "silu":
214
+ if activation == MoEActivation.SILU:
215
use_ue8m0 = scale_fmt == DeepGemmQuantScaleFMT.FLOAT32_CEIL_UE8M0
216
return silu_mul_per_token_group_quant_fp8_colmajor(
217
input=input,
@@ -235,7 +236,7 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
236
w2: torch.Tensor,
237
topk_weights: torch.Tensor,
238
topk_ids: torch.Tensor,
238
- activation: str,
239
+ activation: MoEActivation,
240
global_num_experts: int,
241
expert_map: torch.Tensor | None,
242
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/fallback.py
+4
-3
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod
6
import torch
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10
from vllm.model_executor.layers.fused_moe.config import FusedMoEParallelConfig
11
from vllm.model_executor.layers.quantization.utils.quant_utils import QuantKey
12
@@ -76,7 +77,7 @@ class FallbackExperts(mk.FusedMoEPermuteExpertsUnpermute, ABC):
77
) and fallback_cls._supports_quant_scheme(weight_key, activation_key)
78
79
@classmethod
79
- def _supports_activation(cls, activation: str) -> bool:
80
+ def _supports_activation(cls, activation: MoEActivation) -> bool:
81
experts_cls, fallback_cls = cls.get_clses()
82
return experts_cls._supports_activation(
83
activation
@@ -138,7 +139,7 @@ class FallbackExperts(mk.FusedMoEPermuteExpertsUnpermute, ABC):
139
global_num_experts: int,
140
local_num_experts: int,
141
expert_tokens_meta: mk.ExpertTokensMetadata | None,
141
- activation: str,
142
+ activation: MoEActivation,
143
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
144
raise NotImplementedError
145
@@ -159,7 +160,7 @@ class FallbackExperts(mk.FusedMoEPermuteExpertsUnpermute, ABC):
160
w2: torch.Tensor,
161
topk_weights: torch.Tensor,
162
topk_ids: torch.Tensor,
162
- activation: str,
163
+ activation: MoEActivation,
164
global_num_experts: int,
165
expert_map: torch.Tensor | None,
166
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/flashinfer_cutedsl_moe.py
+5
-4
@@ -6,6 +6,7 @@ import torch
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm import envs
8
from vllm.logger import init_logger
9
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10
from vllm.model_executor.layers.fused_moe.config import (
11
FusedMoEConfig,
12
FusedMoEParallelConfig,
@@ -72,8 +73,8 @@ class FlashInferCuteDSLExperts(mk.FusedMoEPermuteExpertsUnpermute):
73
return (weight_key, activation_key) in SUPPORTED_W_A
74
75
@staticmethod
75
- def _supports_activation(activation: str) -> bool:
76
- return activation in ["silu"]
76
+ def _supports_activation(activation: MoEActivation) -> bool:
77
+ return activation == MoEActivation.SILU
78
79
@staticmethod
80
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -101,7 +102,7 @@ class FlashInferCuteDSLExperts(mk.FusedMoEPermuteExpertsUnpermute):
102
global_num_experts: int,
103
local_num_experts: int,
104
expert_tokens_meta: mk.ExpertTokensMetadata | None,
104
- activation: str,
105
+ activation: MoEActivation,
106
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
107
# We use global_num_experts due to how moe_align_block_size handles
108
# expert_maps.
@@ -135,7 +136,7 @@ class FlashInferCuteDSLExperts(mk.FusedMoEPermuteExpertsUnpermute):
136
w2: torch.Tensor,
137
topk_weights: torch.Tensor,
138
topk_ids: torch.Tensor,
138
- activation: str,
139
+ activation: MoEActivation,
140
global_num_experts: int,
141
expert_map: torch.Tensor | None,
142
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
+7
-6
@@ -5,6 +5,7 @@ import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm.logger import init_logger
8
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
9
from vllm.model_executor.layers.fused_moe.config import (
10
FusedMoEParallelConfig,
11
FusedMoEQuantConfig,
@@ -130,8 +131,8 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
131
)
132
133
@staticmethod
133
- def _supports_activation(activation: str) -> bool:
134
- return activation in ["silu", "relu2_no_mul"]
134
+ def _supports_activation(activation: MoEActivation) -> bool:
135
+ return activation in [MoEActivation.SILU, MoEActivation.RELU2_NO_MUL]
136
137
@staticmethod
138
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -164,7 +165,7 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
165
global_num_experts: int,
166
local_num_experts: int,
167
expert_tokens_meta: mk.ExpertTokensMetadata | None,
167
- activation: str,
168
+ activation: MoEActivation,
169
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
170
# We use global_num_experts due to how moe_align_block_size handles
171
# expert_maps.
@@ -201,7 +202,7 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
202
w2: torch.Tensor,
203
topk_weights: torch.Tensor,
204
topk_ids: torch.Tensor,
204
- activation: str,
205
+ activation: MoEActivation,
206
global_num_experts: int,
207
expert_map: torch.Tensor | None,
208
a1q_scale: torch.Tensor | None,
@@ -214,8 +215,8 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
215
from flashinfer.fused_moe.core import ActivationType
216
217
activation_str_to_value_map = {
217
- "silu": ActivationType.Swiglu, # This is the default
218
- "relu2_no_mul": ActivationType.Relu2,
218
+ MoEActivation.SILU: ActivationType.Swiglu, # This is the default
219
+ MoEActivation.RELU2_NO_MUL: ActivationType.Relu2,
220
}
221
assert activation in activation_str_to_value_map, (
222
f"{activation=} missing from {activation_str_to_value_map.keys()=}"
vllm/model_executor/layers/fused_moe/flashinfer_trtllm_moe.py
+3
-2
@@ -4,6 +4,7 @@
4
import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
8
from vllm.model_executor.layers.fused_moe.config import (
9
FusedMoEConfig,
10
FusedMoEParallelConfig,
@@ -50,9 +51,9 @@ def _supports_quant_scheme(
51
return (weight_key, activation_key) in SUPPORTED_W_A
52
53
53
-def _supports_activation(activation: str) -> bool:
54
+def _supports_activation(activation: MoEActivation) -> bool:
55
"""Supports silu activation only."""
55
- return activation in ["silu"]
56
+ return activation == MoEActivation.SILU
57
58
59
def _supports_routing_method(
vllm/model_executor/layers/fused_moe/fused_batched_moe.py
+13
-12
@@ -5,6 +5,7 @@
5
import torch
6
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
9
from vllm.model_executor.layers.fused_moe.config import (
10
FusedMoEConfig,
11
FusedMoEParallelConfig,
@@ -698,7 +699,7 @@ class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
699
)
700
701
@staticmethod
701
- def _supports_activation(activation: str) -> bool:
702
+ def _supports_activation(activation: MoEActivation) -> bool:
703
raise NotImplementedError(
704
"NaiveBatchedExperts is not yet used by an Oracle. "
705
"This method should not be called."
@@ -730,7 +731,7 @@ class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
731
global_num_experts: int,
732
local_num_experts: int,
733
expert_tokens_meta: mk.ExpertTokensMetadata | None,
733
- activation: str,
734
+ activation: MoEActivation,
735
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
736
assert self.num_dispatchers is not None
737
assert self.max_num_tokens is not None
@@ -757,7 +758,7 @@ class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
758
w2: torch.Tensor,
759
topk_weights: torch.Tensor,
760
topk_ids: torch.Tensor,
760
- activation: str,
761
+ activation: MoEActivation,
762
global_num_experts: int,
763
expert_map: torch.Tensor | None,
764
a1q_scale: torch.Tensor | None,
@@ -942,14 +943,14 @@ class BatchedTritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
943
)
944
945
@staticmethod
945
- def _supports_activation(activation: str) -> bool:
946
+ def _supports_activation(activation: MoEActivation) -> bool:
947
return activation in [
947
- "silu",
948
- "gelu",
949
- "swigluoai",
950
- "silu_no_mul",
951
- "gelu_no_mul",
952
- "relu2_no_mul",
948
+ MoEActivation.SILU,
949
+ MoEActivation.GELU,
950
+ MoEActivation.SWIGLUOAI,
951
+ MoEActivation.SILU_NO_MUL,
952
+ MoEActivation.GELU_NO_MUL,
953
+ MoEActivation.RELU2_NO_MUL,
954
]
955
956
@staticmethod
@@ -975,7 +976,7 @@ class BatchedTritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
976
global_num_experts: int,
977
local_num_experts: int,
978
expert_tokens_meta: mk.ExpertTokensMetadata | None,
978
- activation: str,
979
+ activation: MoEActivation,
980
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
981
assert self.num_dispatchers is not None
982
assert self.max_num_tokens is not None
@@ -996,7 +997,7 @@ class BatchedTritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
997
w2: torch.Tensor,
998
topk_weights: torch.Tensor,
999
topk_ids: torch.Tensor,
999
- activation: str,
1000
+ activation: MoEActivation,
1001
global_num_experts: int,
1002
expert_map: torch.Tensor | None,
1003
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/fused_marlin_moe.py
+21
-18
@@ -8,6 +8,10 @@ import torch
8
9
import vllm._custom_ops as ops
10
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11
+from vllm.model_executor.layers.fused_moe.activation import (
12
+ MoEActivation,
13
+ apply_moe_activation,
14
+)
15
from vllm.model_executor.layers.fused_moe.config import (
16
FusedMoEConfig,
17
FusedMoEParallelConfig,
@@ -23,7 +27,6 @@ from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
27
)
28
from vllm.model_executor.layers.fused_moe.utils import (
29
_resize_cache,
26
- apply_moe_activation,
30
disable_inplace,
31
)
32
from vllm.model_executor.layers.quantization.utils.marlin_utils import (
@@ -59,9 +62,9 @@ def _fused_marlin_moe(
62
sorted_token_ids: torch.Tensor,
63
expert_ids: torch.Tensor,
64
num_tokens_post_padded: torch.Tensor,
62
- activation: str = "silu",
65
+ activation: MoEActivation = MoEActivation.SILU,
66
activation_func: Callable[
64
- [str, torch.Tensor, torch.Tensor], None
67
+ [MoEActivation, torch.Tensor, torch.Tensor], None
68
] = apply_moe_activation,
69
input_global_scale1: torch.Tensor | None = None,
70
input_global_scale2: torch.Tensor | None = None,
@@ -83,7 +86,7 @@ def _fused_marlin_moe(
86
assert hidden_states.ndim == 2
87
M, K = hidden_states.size()
88
N = marlin_moe_intermediate_size(w1, w2)
86
- w13_num_shards = 1 if "no_mul" in activation else 2
89
+ w13_num_shards = 2 if activation.is_gated else 1
90
if workspace is None:
91
workspace = marlin_make_workspace_new(hidden_states.device, 4)
92
@@ -215,9 +218,9 @@ def fused_marlin_moe(
218
quant_type_id: int,
219
apply_router_weight_on_input: bool = False,
220
global_num_experts: int = -1,
218
- activation: str = "silu",
221
+ activation: MoEActivation = MoEActivation.SILU,
222
activation_func: Callable[
220
- [str, torch.Tensor, torch.Tensor], None
223
+ [MoEActivation, torch.Tensor, torch.Tensor], None
224
] = apply_moe_activation,
225
moe_sum: Callable[[torch.Tensor, torch.Tensor], None] | None = None,
226
expert_map: torch.Tensor | None = None,
@@ -377,7 +380,7 @@ def batched_fused_marlin_moe(
380
quant_type_id: int,
381
apply_router_weight_on_input: bool = False,
382
global_num_experts: int = -1,
380
- activation: str | None = "silu",
383
+ activation: MoEActivation = MoEActivation.SILU,
384
expert_map: torch.Tensor | None = None,
385
global_scale1: torch.Tensor | None = None,
386
global_scale2: torch.Tensor | None = None,
@@ -579,14 +582,14 @@ class MarlinExpertsBase(mk.FusedMoEPermuteExpertsUnpermute):
582
return weight_key in SUPPORTED_W
583
584
@staticmethod
582
- def _supports_activation(activation: str) -> bool:
585
+ def _supports_activation(activation: MoEActivation) -> bool:
586
return activation in [
584
- "silu",
585
- "gelu",
586
- "swigluoai",
587
- "silu_no_mul",
588
- "gelu_no_mul",
589
- "relu2_no_mul",
587
+ MoEActivation.SILU,
588
+ MoEActivation.GELU,
589
+ MoEActivation.SWIGLUOAI,
590
+ MoEActivation.SILU_NO_MUL,
591
+ MoEActivation.GELU_NO_MUL,
592
+ MoEActivation.RELU2_NO_MUL,
593
]
594
595
@staticmethod
@@ -661,7 +664,7 @@ class MarlinExperts(MarlinExpertsBase):
664
global_num_experts: int,
665
local_num_experts: int,
666
expert_tokens_meta: mk.ExpertTokensMetadata | None,
664
- activation: str,
667
+ activation: MoEActivation,
668
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
669
# Modular Kernel provisions output buffer from workspace1. However in
670
# the fused_marlin_moe() function, the final torch.sum(), is defined
@@ -692,7 +695,7 @@ class MarlinExperts(MarlinExpertsBase):
695
w2: torch.Tensor,
696
topk_weights: torch.Tensor,
697
topk_ids: torch.Tensor,
695
- activation: str,
698
+ activation: MoEActivation,
699
global_num_experts: int,
700
expert_map: torch.Tensor | None,
701
a1q_scale: torch.Tensor | None,
@@ -788,7 +791,7 @@ class BatchedMarlinExperts(MarlinExpertsBase):
791
global_num_experts: int,
792
local_num_experts: int,
793
expert_tokens_meta: mk.ExpertTokensMetadata | None,
791
- activation: str,
794
+ activation: MoEActivation,
795
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
796
assert self.num_dispatchers is not None
797
assert self.max_num_tokens is not None
@@ -808,7 +811,7 @@ class BatchedMarlinExperts(MarlinExpertsBase):
811
w2: torch.Tensor,
812
topk_weights: torch.Tensor,
813
topk_ids: torch.Tensor,
811
- activation: str,
814
+ activation: MoEActivation,
815
global_num_experts: int,
816
expert_map: torch.Tensor | None,
817
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/fused_moe.py
+23
-11
@@ -17,6 +17,10 @@ from vllm.logger import init_logger
17
from vllm.model_executor.layers.batch_invariant import (
18
vllm_is_batch_invariant,
19
)
20
+from vllm.model_executor.layers.fused_moe.activation import (
21
+ MoEActivation,
22
+ apply_moe_activation,
23
+)
24
from vllm.model_executor.layers.fused_moe.config import (
25
FUSED_MOE_UNQUANTIZED_CONFIG,
26
FusedMoEConfig,
@@ -32,7 +36,6 @@ from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
36
)
37
from vllm.model_executor.layers.fused_moe.utils import (
38
_resize_cache,
35
- apply_moe_activation,
39
disable_inplace,
40
moe_kernel_quantize_input,
41
)
@@ -1468,6 +1471,7 @@ def outplace_fused_experts_fake(
1471
topk_weights: torch.Tensor,
1472
topk_ids: torch.Tensor,
1473
activation: str = "silu",
1474
+ apply_router_weight_on_input: bool = False,
1475
use_fp8_w8a8: bool = False,
1476
use_int8_w8a8: bool = False,
1477
use_int8_w8a16: bool = False,
@@ -1521,7 +1525,7 @@ def fused_experts(
1525
topk_weights: torch.Tensor,
1526
topk_ids: torch.Tensor,
1527
inplace: bool = False,
1524
- activation: str = "silu",
1528
+ activation: MoEActivation = MoEActivation.SILU,
1529
apply_router_weight_on_input: bool = False,
1530
global_num_experts: int = -1,
1531
expert_map: torch.Tensor | None = None,
@@ -1539,7 +1543,7 @@ def fused_experts(
1543
w2=w2,
1544
topk_weights=topk_weights,
1545
topk_ids=topk_ids,
1542
- activation=activation,
1546
+ activation=activation.value,
1547
apply_router_weight_on_input=apply_router_weight_on_input,
1548
use_fp8_w8a8=quant_config.use_fp8_w8a8,
1549
use_int8_w8a8=quant_config.use_int8_w8a8,
@@ -1618,6 +1622,9 @@ def fused_experts_impl(
1622
w1_bias: torch.Tensor | None = None,
1623
w2_bias: torch.Tensor | None = None,
1624
) -> torch.Tensor:
1625
+ # Convert string activation to enum for internal use
1626
+ activation_enum = MoEActivation.from_str(activation)
1627
+
1628
# Check constraints.
1629
if use_int4_w4a16:
1630
assert hidden_states.size(1) // 2 == w1.size(2), "Hidden size mismatch"
@@ -1692,7 +1699,7 @@ def fused_experts_impl(
1699
1700
# This needs separate memory since it's used concurrently with cache1
1701
activation_out_dim = mk.FusedMoEPermuteExpertsUnpermute.adjust_N_for_activation(
1695
- N, activation
1702
+ N, activation_enum
1703
)
1704
intermediate_cache2 = torch.empty(
1705
(M * top_k_num, activation_out_dim),
@@ -1832,7 +1839,7 @@ def fused_experts_impl(
1839
)
1840
1841
apply_moe_activation(
1835
- activation, intermediate_cache2, intermediate_cache1.view(-1, N)
1842
+ activation_enum, intermediate_cache2, intermediate_cache1.view(-1, N)
1843
)
1844
1845
qintermediate_cache2, a2q_scale = moe_kernel_quantize_input(
@@ -1932,8 +1939,13 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1939
return (weight_key, activation_key) in SUPPORTED_W_A
1940
1941
@staticmethod
1935
- def _supports_activation(activation: str) -> bool:
1936
- return activation in ["silu", "gelu", "swigluoai", "swiglustep"]
1942
+ def _supports_activation(activation: MoEActivation) -> bool:
1943
+ return activation in [
1944
+ MoEActivation.SILU,
1945
+ MoEActivation.GELU,
1946
+ MoEActivation.SWIGLUOAI,
1947
+ MoEActivation.SWIGLUSTEP,
1948
+ ]
1949
1950
@staticmethod
1951
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -1957,7 +1969,7 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1969
global_num_experts: int,
1970
local_num_experts: int,
1971
expert_tokens_meta: mk.ExpertTokensMetadata | None,
1960
- activation: str,
1972
+ activation: MoEActivation,
1973
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
1974
activation_out_dim = self.adjust_N_for_activation(N, activation)
1975
workspace1 = (M, topk, max(activation_out_dim, K))
@@ -1973,7 +1985,7 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1985
w2: torch.Tensor,
1986
topk_weights: torch.Tensor,
1987
topk_ids: torch.Tensor,
1976
- activation: str,
1988
+ activation: MoEActivation,
1989
global_num_experts: int,
1990
expert_map: torch.Tensor | None,
1991
a1q_scale: torch.Tensor | None,
@@ -2138,7 +2150,7 @@ class TritonWNA16Experts(TritonExperts):
2150
)
2151
2152
@staticmethod
2141
- def _supports_activation(activation: str) -> bool:
2153
+ def _supports_activation(activation: MoEActivation) -> bool:
2154
raise NotImplementedError(
2155
"TritonWNA16Experts is not yet used by an Oracle. "
2156
"This method should not be called."
@@ -2159,7 +2171,7 @@ class TritonWNA16Experts(TritonExperts):
2171
w2: torch.Tensor,
2172
topk_weights: torch.Tensor,
2173
topk_ids: torch.Tensor,
2162
- activation: str,
2174
+ activation: MoEActivation,
2175
global_num_experts: int,
2176
expert_map: torch.Tensor | None,
2177
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/gpt_oss_triton_kernels_moe.py
+11
-7
@@ -7,6 +7,7 @@ import torch
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm import _custom_ops as ops
9
from vllm.logger import init_logger
10
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
11
from vllm.model_executor.layers.fused_moe.config import (
12
FUSED_MOE_UNQUANTIZED_CONFIG,
13
FusedMoEParallelConfig,
@@ -172,7 +173,7 @@ def triton_kernel_moe_forward(
173
gating_output: torch.Tensor,
174
topk: int,
175
renormalize: bool,
175
- activation: str = "silu",
176
+ activation: MoEActivation = MoEActivation.SWIGLUOAI,
177
quant_config: FusedMoEQuantConfig | None = None,
178
apply_router_weight_on_input: bool = False,
179
global_num_experts: int = -1,
@@ -211,7 +212,7 @@ def triton_kernel_fused_experts(
212
gather_indx, # GatherIndx
213
scatter_indx, # ScatterIndx
214
topk: int,
214
- activation: str = "silu",
215
+ activation: MoEActivation = MoEActivation.SWIGLUOAI,
216
quant_config: FusedMoEQuantConfig | None = None,
217
swiglu_alpha: float = 1.702,
218
swiglu_limit: float = 7.0,
@@ -222,6 +223,9 @@ def triton_kernel_fused_experts(
223
a1q_scale: torch.Tensor | None = None,
224
) -> torch.Tensor:
225
"""Triton implementation of fused expert computation using OAI kernels."""
226
+ assert activation == MoEActivation.SWIGLUOAI, (
227
+ "Only SWIGLUOAI activation is supported"
228
+ )
229
if quant_config is None:
230
quant_config = FUSED_MOE_UNQUANTIZED_CONFIG
231
@@ -379,7 +383,7 @@ class BaseOAITritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
383
)
384
385
@staticmethod
382
- def _supports_activation(activation: str) -> bool:
386
+ def _supports_activation(activation: MoEActivation) -> bool:
387
raise NotImplementedError(
388
"OAITritonExperts is not yet used by an Oracle. "
389
"This method should not be called."
@@ -463,7 +467,7 @@ class OAITritonExperts(BaseOAITritonExperts):
467
global_num_experts: int,
468
local_num_experts: int,
469
expert_tokens_meta: mk.ExpertTokensMetadata | None,
466
- activation: str,
470
+ activation: MoEActivation,
471
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
472
# workspace are allocated inside the kernel
473
activation_out_dim = self.adjust_N_for_activation(N, activation)
@@ -480,7 +484,7 @@ class OAITritonExperts(BaseOAITritonExperts):
484
w2: torch.Tensor,
485
topk_weights: torch.Tensor,
486
topk_ids: torch.Tensor,
483
- activation: str,
487
+ activation: MoEActivation,
488
global_num_experts: int,
489
expert_map: torch.Tensor | None,
490
a1q_scale: torch.Tensor | None,
@@ -547,7 +551,7 @@ class UnfusedOAITritonExperts(BaseOAITritonExperts):
551
global_num_experts: int,
552
local_num_experts: int,
553
expert_tokens_meta: mk.ExpertTokensMetadata | None,
550
- activation: str,
554
+ activation: MoEActivation,
555
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
556
# workspace are allocated inside the kernel
557
activation_out_dim = self.adjust_N_for_activation(N, activation)
@@ -567,7 +571,7 @@ class UnfusedOAITritonExperts(BaseOAITritonExperts):
571
w2: torch.Tensor,
572
topk_weights: torch.Tensor,
573
topk_ids: torch.Tensor,
570
- activation: str,
574
+ activation: MoEActivation,
575
global_num_experts: int,
576
expert_map: torch.Tensor | None,
577
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/layer.py
+3
-2
@@ -20,6 +20,7 @@ from vllm.distributed import (
20
from vllm.distributed.eplb.eplb_state import EplbLayerState, EplbState
21
from vllm.logger import init_logger
22
from vllm.model_executor.custom_op import CustomOp
23
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
24
from vllm.model_executor.layers.fused_moe.config import (
25
FusedMoEConfig,
26
FusedMoEParallelConfig,
@@ -500,7 +501,7 @@ class FusedMoE(CustomOp):
501
# TODO(bnell): end attributes
502
503
self.apply_router_weight_on_input = apply_router_weight_on_input
503
- self.activation = activation
504
+ self.activation = MoEActivation.from_str(activation)
505
506
self.router = create_fused_moe_router(
507
top_k=top_k,
@@ -554,7 +555,7 @@ class FusedMoE(CustomOp):
555
has_bias=has_bias,
556
is_act_and_mul=is_act_and_mul,
557
is_lora_enabled=vllm_config.lora_config is not None,
557
- activation=activation,
558
+ activation=self.activation,
559
device=vllm_config.device_config.device,
560
routing_method=self.routing_method_type,
561
# TODO: in_dtype == out_dtype?
vllm/model_executor/layers/fused_moe/modular_kernel.py
+15
-13
@@ -12,6 +12,10 @@ import torch
12
import vllm.envs as envs
13
from vllm.forward_context import get_forward_context, is_forward_context_available
14
from vllm.logger import init_logger
15
+from vllm.model_executor.layers.fused_moe.activation import (
16
+ MoEActivation,
17
+ apply_moe_activation,
18
+)
19
from vllm.model_executor.layers.fused_moe.config import (
20
FusedMoEConfig,
21
FusedMoEParallelConfig,
@@ -19,7 +23,6 @@ from vllm.model_executor.layers.fused_moe.config import (
23
)
24
from vllm.model_executor.layers.fused_moe.utils import (
25
_resize_cache,
22
- apply_moe_activation,
26
count_expert_num_tokens,
27
disable_inplace,
28
)
@@ -536,7 +539,7 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
539
540
@staticmethod
541
@abstractmethod
539
- def _supports_activation(activation: str) -> bool:
542
+ def _supports_activation(activation: MoEActivation) -> bool:
543
"""
544
Whether the kernel supports a particular act function.
545
"""
@@ -658,7 +661,7 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
661
global_num_experts: int,
662
local_num_experts: int,
663
expert_tokens_meta: ExpertTokensMetadata | None,
661
- activation: str,
664
+ activation: MoEActivation,
665
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
666
"""
667
Compute the shapes for the temporary and final outputs of the two gemms
@@ -690,7 +693,7 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
693
raise NotImplementedError
694
695
@staticmethod
693
- def adjust_N_for_activation(N: int, activation: str) -> int:
696
+ def adjust_N_for_activation(N: int, activation: MoEActivation) -> int:
697
"""
698
Calculate the output dimension for the activation function.
699
@@ -702,16 +705,15 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
705
706
Args:
707
N: The intermediate size (width of w1/w3 weights).
705
- activation: The activation function name.
708
+ activation: The activation function enum.
709
710
Returns:
711
The output dimension after activation.
712
"""
710
- is_no_mul = activation.endswith("_no_mul")
711
- return N if is_no_mul else N // 2
713
+ return N if not activation.is_gated else N // 2
714
715
def activation(
714
- self, activation: str, output: torch.Tensor, input: torch.Tensor
716
+ self, activation: MoEActivation, output: torch.Tensor, input: torch.Tensor
717
) -> None:
718
apply_moe_activation(activation, output, input)
719
@@ -732,7 +734,7 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
734
w2: torch.Tensor,
735
topk_weights: torch.Tensor,
736
topk_ids: torch.Tensor,
735
- activation: str,
737
+ activation: MoEActivation,
738
global_num_experts: int,
739
expert_map: torch.Tensor | None,
740
a1q_scale: torch.Tensor | None,
@@ -892,7 +894,7 @@ class FusedMoEModularKernel(torch.nn.Module):
894
global_num_experts: int,
895
local_num_experts: int,
896
expert_tokens_meta: ExpertTokensMetadata | None,
895
- activation: str,
897
+ activation: MoEActivation,
898
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
899
"""
900
Allocate temporary and output buffers for the fused experts op.
@@ -1135,7 +1137,7 @@ class FusedMoEModularKernel(torch.nn.Module):
1137
w2: torch.Tensor,
1138
topk_weights: torch.Tensor,
1139
topk_ids: torch.Tensor,
1138
- activation: str,
1140
+ activation: MoEActivation,
1141
global_num_experts: int,
1142
local_num_experts: int,
1143
expert_map: torch.Tensor | None,
@@ -1309,7 +1311,7 @@ class FusedMoEModularKernel(torch.nn.Module):
1311
w2: torch.Tensor,
1312
topk_weights: torch.Tensor,
1313
topk_ids: torch.Tensor,
1312
- activation: str = "silu",
1314
+ activation: MoEActivation = MoEActivation.SILU,
1315
global_num_experts: int = -1,
1316
expert_map: torch.Tensor | None = None,
1317
apply_router_weight_on_input: bool = False,
@@ -1326,7 +1328,7 @@ class FusedMoEModularKernel(torch.nn.Module):
1328
- topk_weights (torch.Tensor): The topk weights applied at the end of
1329
the layer.
1330
- topk_ids (torch.Tensor): A map of row to expert id.
1329
- - activation (str): The activation function to apply after the first
1331
+ - activation (MoEActivation): The activation function to apply after the first
1332
MoE layer.
1333
- global_num_experts (int): The total number of experts in the global
1334
expert space.
vllm/model_executor/layers/fused_moe/rocm_aiter_fused_moe.py
+13
-8
@@ -7,6 +7,7 @@ import torch
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm._aiter_ops import rocm_aiter_ops
10
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
11
from vllm.model_executor.layers.fused_moe.config import (
12
FUSED_MOE_UNQUANTIZED_CONFIG,
13
FusedMoEParallelConfig,
@@ -184,7 +185,7 @@ def rocm_aiter_fused_experts(
185
w2: torch.Tensor,
186
topk_weights: torch.Tensor,
187
topk_ids: torch.Tensor,
187
- activation: str = "silu",
188
+ activation: MoEActivation = MoEActivation.SILU,
189
apply_router_weight_on_input: bool = False,
190
expert_map: torch.Tensor | None = None,
191
quant_config: FusedMoEQuantConfig | None = None,
@@ -196,9 +197,13 @@ def rocm_aiter_fused_experts(
197
if quant_config is None:
198
quant_config = FUSED_MOE_UNQUANTIZED_CONFIG
199
199
- activation_method = (
200
- ActivationMethod.SILU if activation == "silu" else ActivationMethod.GELU
201
- )
200
+ if activation == MoEActivation.SILU:
201
+ activation_method = ActivationMethod.SILU
202
+ elif activation == MoEActivation.GELU:
203
+ activation_method = ActivationMethod.GELU
204
+ else:
205
+ raise ValueError(f"Unsupported activation: {activation}")
206
+
207
# All AITER Fused MoE kernels are expecting the following datatypes
208
topk_weights = topk_weights.to(torch.float32)
209
topk_ids = topk_ids.to(torch.int32)
@@ -322,8 +327,8 @@ class AiterExperts(mk.FusedMoEPermuteExpertsUnpermute):
327
return (weight_key, activation_key) in SUPPORTED_W_A
328
329
@staticmethod
325
- def _supports_activation(activation: str) -> bool:
326
- return activation in ["silu", "gelu"]
330
+ def _supports_activation(activation: MoEActivation) -> bool:
331
+ return activation in [MoEActivation.SILU, MoEActivation.GELU]
332
333
@staticmethod
334
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -347,7 +352,7 @@ class AiterExperts(mk.FusedMoEPermuteExpertsUnpermute):
352
global_num_experts: int,
353
local_num_experts: int,
354
expert_tokens_meta: mk.ExpertTokensMetadata | None,
350
- activation: str,
355
+ activation: MoEActivation,
356
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
357
# Workspaces are managed internally by AITER.
358
workspace1 = (0,)
@@ -363,7 +368,7 @@ class AiterExperts(mk.FusedMoEPermuteExpertsUnpermute):
368
w2: torch.Tensor,
369
topk_weights: torch.Tensor,
370
topk_ids: torch.Tensor,
366
- activation: str,
371
+ activation: MoEActivation,
372
global_num_experts: int,
373
expert_map: torch.Tensor | None,
374
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/triton_cutlass_moe.py
+2
-1
@@ -5,6 +5,7 @@
5
import torch
6
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
9
from vllm.model_executor.layers.fused_moe.config import (
10
FusedMoEConfig,
11
FusedMoEQuantConfig,
@@ -45,7 +46,7 @@ class TritonOrCutlassExperts(FallbackExperts):
46
global_num_experts: int,
47
local_num_experts: int,
48
expert_tokens_meta: mk.ExpertTokensMetadata | None,
48
- activation: str,
49
+ activation: MoEActivation,
50
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
51
# Small batch fallback for sm100.
52
if self.is_sm100 and M <= 8:
vllm/model_executor/layers/fused_moe/triton_deep_gemm_moe.py
+2
-1
@@ -4,6 +4,7 @@
4
import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
8
from vllm.model_executor.layers.fused_moe.config import (
9
FusedMoEConfig,
10
FusedMoEQuantConfig,
@@ -45,7 +46,7 @@ class TritonOrDeepGemmExperts(FallbackExperts):
46
global_num_experts: int,
47
local_num_experts: int,
48
expert_tokens_meta: mk.ExpertTokensMetadata | None,
48
- activation: str,
49
+ activation: MoEActivation,
50
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
51
# Note: the deep gemm workspaces are strictly larger than the triton
52
# workspaces so we can be pessimistic here and allocate for DeepGemm
vllm/model_executor/layers/fused_moe/trtllm_moe.py
+4
-3
@@ -4,6 +4,7 @@
4
import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
8
from vllm.model_executor.layers.fused_moe.config import (
9
FusedMoEConfig,
10
FusedMoEParallelConfig,
@@ -64,7 +65,7 @@ class TrtLlmGenExperts(mk.FusedMoEPermuteExpertsUnpermute):
65
)
66
67
@staticmethod
67
- def _supports_activation(activation: str) -> bool:
68
+ def _supports_activation(activation: MoEActivation) -> bool:
69
raise NotImplementedError(
70
"TrtLlmGenExperts is not yet used by an Oracle. "
71
"This method should not be called."
@@ -95,7 +96,7 @@ class TrtLlmGenExperts(mk.FusedMoEPermuteExpertsUnpermute):
96
global_num_experts: int,
97
local_num_experts: int,
98
expert_tokens_meta: mk.ExpertTokensMetadata | None,
98
- activation: str,
99
+ activation: MoEActivation,
100
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
101
# The workspaces for this implementation are managed by flashinfer.
102
workspace1 = (0,)
@@ -111,7 +112,7 @@ class TrtLlmGenExperts(mk.FusedMoEPermuteExpertsUnpermute):
112
w2: torch.Tensor,
113
topk_weights: torch.Tensor,
114
topk_ids: torch.Tensor,
114
- activation: str,
115
+ activation: MoEActivation,
116
global_num_experts: int,
117
expert_map: torch.Tensor | None,
118
a1q_scale: torch.Tensor | None,
vllm/model_executor/layers/fused_moe/utils.py
-60
@@ -4,7 +4,6 @@ import functools
4
from math import prod
5
6
import torch
7
-import torch.nn.functional as F
7
8
from vllm import _custom_ops as ops
9
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
@@ -341,65 +340,6 @@ def _validate_scale_shape(
340
assert a_scale.shape == expected, f"{a_scale.shape} == {expected}"
341
342
344
-def activation_without_mul(activation: str) -> str:
345
- return activation + "_no_mul"
346
-
347
-
348
-RELU2_NO_MUL: str = activation_without_mul("relu2")
349
-SILU_NO_MUL: str = activation_without_mul("silu")
350
-GELU_NO_MUL: str = activation_without_mul("gelu")
351
-
352
-
353
-def apply_moe_activation(
354
- activation: str,
355
- output: torch.Tensor,
356
- input: torch.Tensor,
357
-) -> torch.Tensor:
358
- """
359
- Apply MoE activation function.
360
-
361
- For *_and_mul activations (silu, gelu, swigluoai):
362
- - Expects output.size(-1) * 2 == input.size(-1)
363
-
364
- For *_no_mul activations (silu_no_mul, gelu_no_mul, relu2_no_mul):
365
- - Expects output.size(-1) == input.size(-1)
366
- """
367
- is_no_mul = activation.endswith("_no_mul")
368
- if is_no_mul:
369
- assert output.size(-1) == input.size(-1), (
370
- f"{activation} expects equal sizes: {output.size(-1)} vs {input.size(-1)}"
371
- )
372
- else:
373
- assert output.size(-1) * 2 == input.size(-1), (
374
- f"{activation} expects 2x ratio: {output.size(-1) * 2} vs {input.size(-1)}"
375
- )
376
-
377
- # Activations with gated multiplication (gate × activation(up))
378
- if activation == "silu":
379
- torch.ops._C.silu_and_mul(output, input)
380
- elif activation == "gelu":
381
- torch.ops._C.gelu_and_mul(output, input)
382
- elif activation == "swigluoai":
383
- torch.ops._C.swigluoai_and_mul(output, input)
384
- elif activation == "swiglustep":
385
- from vllm.model_executor.layers.activation import swiglustep_and_mul_triton
386
-
387
- swiglustep_and_mul_triton(output, input)
388
-
389
- # Activations without gated multiplication
390
- elif activation == SILU_NO_MUL:
391
- output.copy_(F.silu(input))
392
- elif activation == GELU_NO_MUL:
393
- output.copy_(F.gelu(input))
394
- elif activation == RELU2_NO_MUL:
395
- F.relu(input, inplace=True)
396
- torch.square(input, out=output)
397
- else:
398
- raise ValueError(f"Unsupported FusedMoe activation: {activation}")
399
-
400
- return output
401
-
402
-
343
# Torch custom ops can't deal with outputs aliasing inputs so we need to
344
# disable inplace for torch >= 2.9.
345
# See https://github.com/vllm-project/vllm/issues/26378
vllm/model_executor/layers/fused_moe/xpu_fused_moe.py
+10
-5
@@ -3,6 +3,7 @@
3
import torch
4
5
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
6
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
7
from vllm.model_executor.layers.fused_moe.config import (
8
FusedMoEConfig,
9
FusedMoEParallelConfig,
@@ -55,8 +56,12 @@ class XPUExperts(mk.FusedMoEPermuteExpertsUnpermute):
56
return False
57
58
@staticmethod
58
- def _supports_activation(activation: str) -> bool:
59
- return activation in ["silu", "gelu", "swigluoai"]
59
+ def _supports_activation(activation: MoEActivation) -> bool:
60
+ return activation in [
61
+ MoEActivation.SILU,
62
+ MoEActivation.GELU,
63
+ MoEActivation.SWIGLUOAI,
64
+ ]
65
66
@staticmethod
67
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
@@ -92,7 +97,7 @@ class XPUExperts(mk.FusedMoEPermuteExpertsUnpermute):
97
global_num_experts: int,
98
local_num_experts: int,
99
expert_tokens_meta: mk.ExpertTokensMetadata | None,
95
- activation: str,
100
+ activation: MoEActivation,
101
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
102
workspace1 = (0,)
103
workspace2 = (0,)
@@ -107,7 +112,7 @@ class XPUExperts(mk.FusedMoEPermuteExpertsUnpermute):
112
w2: torch.Tensor,
113
topk_weights: torch.Tensor,
114
topk_ids: torch.Tensor,
110
- activation: str,
115
+ activation: MoEActivation,
116
global_num_experts: int,
117
expert_map: torch.Tensor | None,
118
a1q_scale: torch.Tensor | None,
@@ -129,7 +134,7 @@ class XPUExperts(mk.FusedMoEPermuteExpertsUnpermute):
134
topk_weights=topk_weights,
135
topk_ids=topk_ids,
136
n_experts_per_token=topk,
132
- activation=activation,
137
+ activation=activation.value,
138
num_experts=self.moe_config.num_local_experts,
139
ep_rank=self.moe_config.ep_rank,
140
ep_size=self.moe_config.ep_size,
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe.py
+19
-10
@@ -24,6 +24,7 @@ from vllm.model_executor.layers.fused_moe import (
24
FusedMoeWeightScaleSupported,
25
UnquantizedFusedMoEMethod,
26
)
27
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
28
from vllm.model_executor.layers.fused_moe.config import (
29
FusedMoEConfig,
30
FusedMoEQuantConfig,
@@ -622,7 +623,9 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
623
router_logits: torch.Tensor,
624
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
625
assert self.is_monolithic
625
- assert layer.activation == "silu", "Only SiLU activation is supported."
626
+ assert layer.activation == MoEActivation.SILU, (
627
+ f"Only SiLU activation is supported, not {layer.activation}."
628
+ )
629
assert (
630
self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM
631
and not layer.enable_eplb
@@ -649,7 +652,9 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
652
shared_experts_input: torch.Tensor | None,
653
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
654
assert not self.is_monolithic
652
- assert layer.activation == "silu", "Only SiLU activation is supported."
655
+ assert layer.activation == MoEActivation.SILU, (
656
+ f"Only SiLU activation is supported, not {layer.activation}."
657
+ )
658
659
# EPLB path
660
if self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
@@ -1025,7 +1030,9 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
1030
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1031
assert self.is_monolithic
1032
assert self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM
1028
- assert layer.activation == "silu"
1033
+ assert layer.activation == MoEActivation.SILU, (
1034
+ f"Only SiLU activation is supported, not {layer.activation}."
1035
+ )
1036
1037
if self.block_quant:
1038
import vllm.model_executor.layers.fused_moe.flashinfer_trtllm_moe # noqa: E501, F401
@@ -2271,19 +2278,21 @@ class CompressedTensorsW4A8Int8MoEMethod(CompressedTensorsMoEMethod):
2278
router_logits: torch.Tensor,
2279
) -> torch.Tensor:
2280
assert not layer.enable_eplb, "EPLB not supported for W4A8-int MoE yet."
2274
- assert layer.activation in ("silu", "swigluoai", "swiglu"), (
2275
- "Only SiLU/SwiGLUGU/SwiGLUUG are supported."
2276
- )
2281
+ assert layer.activation in (
2282
+ MoEActivation.SILU,
2283
+ MoEActivation.SWIGLUOAI,
2284
+ MoEActivation.SWIGLUSTEP,
2285
+ ), "Only SiLU/SwiGLUGU/SwiGLUUG are supported."
2286
assert layer.expert_map is None, """expert_map/EP not implemented
2287
for CPU dyn-4bit MoE."""
2288
2280
- def _act_kind(s: str) -> int:
2289
+ def _act_kind(s: MoEActivation) -> int:
2290
# 0 = SwiGLU_Gu (SiLU(g)*u), 1 = SwiGLU_Ug (SiLU(u)*g), 2 = SiLU
2282
- if s == "swiglu":
2291
+ if s == MoEActivation.SWIGLUSTEP:
2292
return 0
2284
- if s == "swigluoai":
2293
+ if s == MoEActivation.SWIGLUOAI:
2294
return 1
2286
- if s == "silu":
2295
+ if s == MoEActivation.SILU:
2296
return 2
2297
raise ValueError(f"Unknown activation '{s}'")
2298
vllm/model_executor/layers/quantization/fp8.py
+2
-1
@@ -23,6 +23,7 @@ from vllm.model_executor.layers.fused_moe import (
23
FusedMoEPermuteExpertsUnpermute,
24
FusedMoEPrepareAndFinalize,
25
FusedMoeWeightScaleSupported,
26
+ MoEActivation,
27
)
28
from vllm.model_executor.layers.fused_moe.config import (
29
FusedMoEQuantConfig,
@@ -965,7 +966,7 @@ class Fp8MoEMethod(FusedMoEMethodBase):
966
# TODO(rob): convert this to MK.
967
if layer.enable_eplb:
968
raise NotImplementedError("EPLB not supported for `Fp8MoEMethod` yet.")
968
- assert layer.activation == "silu", (
969
+ assert layer.activation == MoEActivation.SILU, (
970
f"Expected 'silu' activation but got {layer.activation}"
971
)
972
vllm/model_executor/layers/quantization/gguf.py
+8
-8
@@ -12,6 +12,10 @@ from torch.nn.parameter import Parameter, UninitializedParameter
12
13
from vllm import _custom_ops as ops
14
from vllm.logger import init_logger
15
+from vllm.model_executor.layers.fused_moe.activation import (
16
+ MoEActivation,
17
+ apply_moe_activation,
18
+)
19
from vllm.model_executor.layers.fused_moe.config import (
20
FusedMoEConfig,
21
FusedMoEQuantConfig,
@@ -246,16 +250,13 @@ def _fused_moe_gguf(
250
qweight_type2: int,
251
activation: str,
252
) -> torch.Tensor:
253
+ activation_enum = MoEActivation.from_str(activation)
254
+
255
def act(x: torch.Tensor):
256
d = x.shape[-1] // 2
257
output_shape = x.shape[:-1] + (d,)
258
out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
253
- if activation == "silu":
254
- torch.ops._C.silu_and_mul(out, x)
255
- elif activation == "gelu":
256
- torch.ops._C.gelu_and_mul(out, x)
257
- else:
258
- raise ValueError(f"Unsupported activation: {activation}")
259
+ apply_moe_activation(activation_enum, out, x)
260
return out
261
262
# lazy import to avoid triggering triton import in CPU backend
@@ -637,7 +638,6 @@ class GGUFMoEMethod(FusedMoEMethodBase):
638
topk_ids: torch.Tensor,
639
shared_experts_input: torch.Tensor | None,
640
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
640
- assert layer.activation == "silu", "Only SiLU activation is supported."
641
if layer.apply_router_weight_on_input:
642
raise NotImplementedError(
643
"Apply router weight on input is not supported for"
@@ -652,7 +652,7 @@ class GGUFMoEMethod(FusedMoEMethodBase):
652
topk_ids,
653
layer.w13_qweight_type.weight_type,
654
layer.w2_qweight_type.weight_type,
655
- layer.activation,
655
+ layer.activation.value,
656
)
657
658
vllm/model_executor/layers/quantization/modelopt.py
+6
-2
@@ -10,6 +10,7 @@ from torch.nn.parameter import Parameter
10
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11
from vllm.logger import init_logger
12
from vllm.model_executor.layers.attention import Attention
13
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
14
from vllm.model_executor.layers.fused_moe.config import (
15
FusedMoEConfig,
16
FusedMoEQuantConfig,
@@ -936,7 +937,7 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
937
)
938
# TODO(rob): this validation should happen at kernel selection
939
# time in the oracle rather than here.
939
- assert layer.activation == "silu", (
940
+ assert layer.activation == MoEActivation.SILU, (
941
f"Expected 'silu' activation but got {layer.activation}"
942
)
943
assert not layer.renormalize
@@ -965,7 +966,10 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
966
# TODO(rob): this validation should happen at kernel selection
967
# time in the oracle rather than here.
968
if self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
968
- assert layer.activation in ("silu", "relu2_no_mul"), (
969
+ assert layer.activation in (
970
+ MoEActivation.SILU,
971
+ MoEActivation.RELU2_NO_MUL,
972
+ ), (
973
"Expected activation to be in ('silu', 'relu2_no_mul'),"
974
f"but got {layer.activation}"
975
)
vllm/model_executor/layers/quantization/moe_wna16.py
+4
-1
@@ -6,6 +6,7 @@ from typing import Any
6
import torch
7
8
from vllm.distributed import get_tensor_model_parallel_rank, get_tp_group
9
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10
from vllm.model_executor.layers.fused_moe.config import (
11
FusedMoEQuantConfig,
12
int4_w4a16_moe_quant_config,
@@ -371,7 +372,9 @@ class MoeWNA16Method(FusedMoEMethodBase):
372
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
373
from vllm.model_executor.layers.fused_moe import fused_experts
374
374
- assert layer.activation == "silu", "Only SiLU activation is supported."
375
+ assert layer.activation == MoEActivation.SILU, (
376
+ f"Only SiLU activation is supported, not {layer.activation}."
377
+ )
378
379
return fused_experts(
380
x,
vllm/model_executor/layers/quantization/mxfp4.py
+4
-2
@@ -13,6 +13,7 @@ from vllm.model_executor.layers.fused_moe import (
13
FusedMoE,
14
FusedMoEConfig,
15
FusedMoEMethodBase,
16
+ MoEActivation,
17
)
18
from vllm.model_executor.layers.fused_moe import modular_kernel as mk
19
from vllm.model_executor.layers.fused_moe.config import (
@@ -1141,8 +1142,9 @@ class XpuMxfp4MoEMethod(Mxfp4MoEMethod):
1142
x: torch.Tensor,
1143
router_logits: torch.Tensor,
1144
) -> torch.Tensor:
1144
- assert layer.activation == "swigluoai", (
1145
- "Only swiglu_oai activation is supported for XPU MXFP4 MoE"
1145
+ assert layer.activation == MoEActivation.SWIGLUOAI, (
1146
+ "Only swiglu_oai activation is supported for "
1147
+ f"XPU MXFP4 MoE, not {layer.activation}."
1148
)
1149
from vllm_xpu_kernels.fused_moe_interface import xpu_fused_moe
1150
vllm/model_executor/layers/quantization/quark/quark_moe.py
+2
-1
@@ -15,6 +15,7 @@ from vllm.model_executor.layers.fused_moe import (
15
FusedMoEConfig,
16
FusedMoEMethodBase,
17
FusedMoeWeightScaleSupported,
18
+ MoEActivation,
19
)
20
from vllm.model_executor.layers.fused_moe.config import (
21
FusedMoEQuantConfig,
@@ -438,7 +439,7 @@ class QuarkW8A8Fp8MoEMethod(QuarkMoEMethod):
439
expert_map=layer.expert_map,
440
)
441
elif self.use_marlin:
441
- assert layer.activation == "silu", (
442
+ assert layer.activation == MoEActivation.SILU, (
443
f"{layer.activation} not supported for Marlin MoE."
444
)
445
return fused_marlin_moe(
vllm/model_executor/layers/quantization/utils/flashinfer_fp4_moe.py
+7
-6
@@ -9,6 +9,7 @@ import torch
9
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
10
from vllm import _custom_ops as ops
11
from vllm.logger import init_logger
12
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
13
from vllm.model_executor.layers.fused_moe.config import (
14
FusedMoEConfig,
15
FusedMoEParallelConfig,
@@ -64,9 +65,9 @@ def _supports_quant_scheme(
65
return (weight_key, activation_key) in SUPPORTED_W_A
66
67
67
-def _supports_activation(activation: str) -> bool:
68
+def _supports_activation(activation: MoEActivation) -> bool:
69
"""Supports silu activation only."""
69
- return activation in ["silu"]
70
+ return activation in [MoEActivation.SILU]
71
72
73
def _supports_routing_method(
@@ -267,7 +268,7 @@ def flashinfer_trtllm_fp4_moe(
268
x: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
269
router_logits: torch.Tensor,
270
top_k: int,
270
- activation: str,
271
+ activation: MoEActivation,
272
global_num_experts: int,
273
num_expert_group: int | None,
274
topk_group: int | None,
@@ -297,7 +298,7 @@ def flashinfer_trtllm_fp4_moe(
298
from vllm.model_executor.models.llama4 import Llama4MoE
299
300
# https://github.com/flashinfer-ai/flashinfer/blob/f0277fd1bff90e309e5c19cab36c5dae056d685d/flashinfer/fused_moe/core.py#L2404
300
- assert activation == "silu", (
301
+ assert activation == MoEActivation.SILU, (
302
"Only SiLU activation is supported for FlashInfer TRTLLM FP4 MoE. "
303
f"{activation} found instead."
304
)
@@ -365,7 +366,7 @@ def flashinfer_trtllm_fp4_routed_moe(
366
topk_ids: torch.Tensor,
367
topk_weights: torch.Tensor,
368
top_k: int,
368
- activation: str,
369
+ activation: MoEActivation,
370
global_num_experts: int,
371
) -> torch.Tensor:
372
"""
@@ -387,7 +388,7 @@ def flashinfer_trtllm_fp4_routed_moe(
388
import flashinfer
389
390
# https://github.com/flashinfer-ai/flashinfer/blob/f0277fd1bff90e309e5c19cab36c5dae056d685d/flashinfer/fused_moe/core.py#L2535
390
- assert activation == "silu", (
391
+ assert activation == MoEActivation.SILU, (
392
"Only SiLU activation is supported for FlashInfer TRTLLM FP4 Routed MoE. "
393
f"{activation} found instead."
394
)
vllm/model_executor/layers/quantization/utils/mxfp4_utils.py
+3
-2
@@ -6,6 +6,7 @@ from typing import Any
6
import torch
7
8
from vllm.logger import init_logger
9
+from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10
from vllm.platforms import current_platform
11
from vllm.triton_utils import triton
12
from vllm.utils.import_utils import has_triton_kernels
@@ -88,7 +89,7 @@ def _can_support_mxfp4(
89
e_score_correction_bias: torch.Tensor | None = None,
90
apply_router_weight_on_input: bool = False,
91
scoring_func: str = "softmax",
91
- activation: str = "swigluoai",
92
+ activation: MoEActivation = MoEActivation.SWIGLUOAI,
93
expert_load_view: torch.Tensor | None = None,
94
logical_to_physical_map: torch.Tensor | None = None,
95
logical_replica_count: torch.Tensor | None = None,
@@ -101,7 +102,7 @@ def _can_support_mxfp4(
102
or e_score_correction_bias
103
or apply_router_weight_on_input
104
or scoring_func != "softmax"
104
- or activation != "swigluoai"
105
+ or activation != MoEActivation.SWIGLUOAI
106
or expert_load_view
107
or logical_to_physical_map
108
or logical_replica_count
vllm/model_executor/models/nemotron_h.py
+5
-2
@@ -33,8 +33,11 @@ from vllm.distributed.communication_op import tensor_model_parallel_all_gather
33
from vllm.distributed.parallel_state import get_pp_group
34
from vllm.model_executor.layers.activation import ReLUSquaredActivation
35
from vllm.model_executor.layers.attention import Attention
36
-from vllm.model_executor.layers.fused_moe import FusedMoE, SharedFusedMoE
37
-from vllm.model_executor.layers.fused_moe.utils import activation_without_mul
36
+from vllm.model_executor.layers.fused_moe import (
37
+ FusedMoE,
38
+ SharedFusedMoE,
39
+ activation_without_mul,
40
+)
41
from vllm.model_executor.layers.layernorm import RMSNorm
42
from vllm.model_executor.layers.linear import (
43
ColumnParallelLinear,