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 14, 05:30 UTC
[gpt-oss] flashinfer mxfp4 (#22339)
Signed-off-by: simon-mo <xmo@berkeley.edu> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-authored-by: simon-mo <xmo@berkeley.edu>
Yongye Zhu committed
Aug 6, 2025 at 12:37 UTC
31f09c615f4f067dba765ce5fe7d00d880212a6d
5 files changed
+453
-3
vllm/envs.py
+12
@@ -154,6 +154,8 @@ if TYPE_CHECKING:
154
VLLM_ENABLE_RESPONSES_API_STORE: bool = False
155
VLLM_USE_TRTLLM_CONTEXT_ATTENTION: bool = False
156
VLLM_USE_TRTLLM_DECODE_ATTENTION: bool = False
157
+ VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8: bool = False
158
+ VLLM_USE_FLASHINFER_MOE_MXFP4_BF16: bool = False
159
160
161
def get_default_cache_root():
@@ -932,6 +934,16 @@ environment_variables: dict[str, Callable[[], Any]] = {
934
"VLLM_USE_FLASHINFER_MOE_FP4":
935
lambda: bool(int(os.getenv("VLLM_USE_FLASHINFER_MOE_FP4", "0"))),
936
937
+ # If set to 1, use the FlashInfer
938
+ # MXFP8 (activation) x MXFP4 (weight) MoE backend.
939
+ "VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8":
940
+ lambda: bool(int(os.getenv("VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8", "0"))),
941
+
942
+ # If set to 1, use the FlashInfer
943
+ # BF16 (activation) x MXFP4 (weight) MoE backend.
944
+ "VLLM_USE_FLASHINFER_MOE_MXFP4_BF16":
945
+ lambda: bool(int(os.getenv("VLLM_USE_FLASHINFER_MOE_MXFP4_BF16", "0"))),
946
+
947
# Control the cache sized used by the xgrammar compiler. The default
948
# of 512 MB should be enough for roughly 1000 JSON schemas.
949
# It can be changed with this variable if needed for some reason.
vllm/model_executor/layers/fused_moe/layer.py
+29
-3
@@ -33,7 +33,8 @@ from vllm.model_executor.layers.quantization.base_config import (
33
from vllm.model_executor.utils import set_weight_attrs
34
from vllm.platforms import current_platform
35
from vllm.platforms.interface import CpuArchEnum
36
-from vllm.utils import direct_register_custom_op, has_deep_ep, has_pplx
36
+from vllm.utils import (direct_register_custom_op, has_deep_ep, has_pplx,
37
+ round_up)
38
from vllm.utils.flashinfer import has_flashinfer
39
40
if current_platform.is_cuda_alike():
@@ -719,6 +720,12 @@ class FusedMoE(torch.nn.Module):
720
721
self.global_num_experts = num_experts + num_redundant_experts
722
723
+ # we padding globally so EP buffer allocation works
724
+ if quant_config and quant_config.get_name() == "mxfp4" and (
725
+ envs.VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8
726
+ or envs.VLLM_USE_FLASHINFER_MOE_MXFP4_BF16):
727
+ hidden_size = round_up(hidden_size, 256)
728
+
729
# For smuggling this layer into the fused moe custom op
730
compilation_config = vllm_config.compilation_config
731
if prefix in compilation_config.static_forward_context:
@@ -1064,6 +1071,18 @@ class FusedMoE(torch.nn.Module):
1071
shard_id: str,
1072
expert_id: int,
1073
return_success: bool = False) -> Optional[bool]:
1074
+
1075
+ if self.quant_config and self.quant_config.get_name() == "mxfp4":
1076
+ # (FIXME) for gpt-oss all experts are combined
1077
+ if "bias" in weight_name:
1078
+ dim1 = loaded_weight.shape[1]
1079
+ param.data[:, :dim1].copy_(loaded_weight)
1080
+ else:
1081
+ dim1 = loaded_weight.shape[1]
1082
+ dim2 = loaded_weight.shape[2]
1083
+ param.data[:, :dim1, :dim2].copy_(loaded_weight)
1084
+ return True if return_success else None
1085
+
1086
expert_id = self._map_global_expert_id_to_local_expert_id(expert_id)
1087
if expert_id == -1:
1088
# Failed to load this param since it's not local to this rank
@@ -1476,13 +1495,20 @@ class FusedMoE(torch.nn.Module):
1495
1496
def forward(self, hidden_states: torch.Tensor,
1497
router_logits: torch.Tensor):
1498
+ og_hidden_states = hidden_states.shape[-1]
1499
+ if self.hidden_size != og_hidden_states:
1500
+ hidden_states = F.pad(hidden_states,
1501
+ (0, self.hidden_size - og_hidden_states),
1502
+ mode='constant',
1503
+ value=0.0)
1504
# TODO: Once the OOM issue for the TPU backend is resolved, we will
1505
# switch to using the moe_forward custom op.
1506
if current_platform.is_tpu():
1507
return self.forward_impl(hidden_states, router_logits)
1508
else:
1484
- return torch.ops.vllm.moe_forward(hidden_states, router_logits,
1485
- self.layer_name)
1509
+ return torch.ops.vllm.moe_forward(
1510
+ hidden_states, router_logits,
1511
+ self.layer_name)[..., :og_hidden_states]
1512
1513
def forward_impl_chunked(self, full_hidden_states: torch.Tensor,
1514
full_router_logits: torch.Tensor):
vllm/model_executor/layers/quantization/__init__.py
+3
@@ -37,6 +37,7 @@ QuantizationMethods = Literal[
37
"auto-round",
38
"rtn",
39
"inc",
40
+ "mxfp4",
41
]
42
QUANTIZATION_METHODS: list[str] = list(get_args(QuantizationMethods))
43
@@ -110,6 +111,7 @@ def get_quantization_config(quantization: str) -> type[QuantizationConfig]:
111
from .marlin import MarlinConfig
112
from .modelopt import ModelOptFp8Config, ModelOptNvFp4Config
113
from .moe_wna16 import MoeWNA16Config
114
+ from .mxfp4 import Mxfp4Config
115
from .neuron_quant import NeuronQuantConfig
116
from .ptpc_fp8 import PTPCFp8Config
117
from .qqq import QQQConfig
@@ -148,6 +150,7 @@ def get_quantization_config(quantization: str) -> type[QuantizationConfig]:
150
"auto-round": AutoRoundConfig,
151
"rtn": RTNConfig,
152
"inc": INCConfig,
153
+ "mxfp4": Mxfp4Config,
154
}
155
# Update the `method_to_config` with customized quantization methods.
156
method_to_config.update(_CUSTOMIZED_METHOD_TO_QUANT_CONFIG)
vllm/model_executor/layers/quantization/mxfp4.py
new
+387
@@ -0,0 +1,387 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+from typing import Callable, Optional
4
+
5
+import torch
6
+from torch.nn.parameter import Parameter
7
+
8
+from vllm import envs
9
+from vllm.model_executor.layers.fused_moe import (FusedMoE, FusedMoEConfig,
10
+ FusedMoEMethodBase)
11
+from vllm.model_executor.layers.linear import (LinearBase,
12
+ UnquantizedLinearMethod)
13
+from vllm.model_executor.layers.quantization import QuantizationMethods
14
+from vllm.model_executor.layers.quantization.base_config import (
15
+ QuantizationConfig, QuantizeMethodBase)
16
+from vllm.model_executor.layers.quantization.utils.mxfp4_utils import (
17
+ _can_support_mxfp4)
18
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
19
+ is_layer_skipped)
20
+from vllm.model_executor.utils import set_weight_attrs
21
+from vllm.utils import next_power_of_2, round_up
22
+
23
+if (envs.VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8
24
+ or envs.VLLM_USE_FLASHINFER_MOE_MXFP4_BF16):
25
+ # from flashinfer.fused_moe import cutlass_fused_moe
26
+ from flashinfer import (mxfp8_quantize, shuffle_matrix_a,
27
+ shuffle_matrix_sf_a, trtllm_fp4_block_scale_moe)
28
+
29
+
30
+class Mxfp4Config(QuantizationConfig):
31
+
32
+ def __init__(self, ignored_layers: Optional[list[str]] = None):
33
+ super().__init__()
34
+ self.ignored_layers = ignored_layers
35
+
36
+ @classmethod
37
+ def from_config(cls, config):
38
+ return cls()
39
+
40
+ @classmethod
41
+ def get_min_capability(cls) -> int:
42
+ return 100
43
+
44
+ @classmethod
45
+ def get_name(cls) -> QuantizationMethods:
46
+ return "mxfp4"
47
+
48
+ @classmethod
49
+ def get_supported_act_dtypes(cls) -> list[torch.dtype]:
50
+ return [torch.bfloat16]
51
+
52
+ @classmethod
53
+ def get_config_filenames(cls) -> list[str]:
54
+ return []
55
+
56
+ def get_quant_method(self, layer: torch.nn.Module,
57
+ prefix: str) -> Optional["QuantizeMethodBase"]:
58
+ from vllm.attention.layer import Attention # Avoid circular import
59
+
60
+ if isinstance(layer, LinearBase):
61
+ if self.ignored_layers and is_layer_skipped(
62
+ prefix=prefix,
63
+ ignored_layers=self.ignored_layers,
64
+ fused_mapping=self.packed_modules_mapping):
65
+ return UnquantizedLinearMethod()
66
+ raise NotImplementedError("Mxfp4 linear layer is not implemented")
67
+ elif isinstance(layer, FusedMoE):
68
+ return Mxfp4MoEMethod(layer.moe_config)
69
+ elif isinstance(layer, Attention):
70
+ raise NotImplementedError(
71
+ "Mxfp4 attention layer is not implemented")
72
+ return None
73
+
74
+
75
+class Mxfp4MoEMethod(FusedMoEMethodBase):
76
+
77
+ def __init__(self, moe: FusedMoEConfig):
78
+ super().__init__()
79
+ self.topk_indices_dtype = None
80
+ self.moe = moe
81
+
82
+ def create_weights(self, layer: torch.nn.Module, num_experts: int,
83
+ hidden_size: int, intermediate_size_per_partition: int,
84
+ params_dtype: torch.dtype, **extra_weight_attrs):
85
+ self.num_experts = num_experts
86
+ weight_dtype = torch.uint8
87
+ scale_dtype = torch.uint8
88
+
89
+ # FIXME (zyongye): ship after torch and safetensors support mxfp4
90
+ # is_torch_mxfp4_available = (
91
+ # hasattr(torch, "float4_e2m1fn_x2") and
92
+ # hasattr(torch, "float8_e8m0fnu"))
93
+ # if is_torch_mxfp4_available:
94
+ # weight_dtype = torch.float4_e2m1fn_x2
95
+ # scale_dtype = torch.float8_e8m0fnu
96
+
97
+ mxfp4_block = 32
98
+
99
+ intermediate_size_per_partition_after_pad = \
100
+ intermediate_size_per_partition
101
+ # pad the intermediate size to be a multiple of 2 * mxfp4_block
102
+ # for to hold non-uniform sharded tensor as well as swizzling
103
+ if (envs.VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8
104
+ or envs.VLLM_USE_FLASHINFER_MOE_MXFP4_BF16):
105
+ intermediate_size_per_partition_after_pad = round_up(
106
+ intermediate_size_per_partition, 256)
107
+ hidden_size = round_up(hidden_size, 256)
108
+
109
+ self.intermediate_size = intermediate_size_per_partition_after_pad
110
+ self.hidden_size = hidden_size
111
+ # Fused gate_up_proj (column parallel)
112
+ w13_weight = torch.nn.Parameter(torch.zeros(
113
+ num_experts,
114
+ 2 * intermediate_size_per_partition_after_pad,
115
+ hidden_size // 2,
116
+ dtype=weight_dtype),
117
+ requires_grad=False)
118
+ layer.register_parameter("w13_weight", w13_weight)
119
+ set_weight_attrs(w13_weight, extra_weight_attrs)
120
+
121
+ w13_weight_scale = torch.nn.Parameter(torch.zeros(
122
+ num_experts,
123
+ 2 * intermediate_size_per_partition_after_pad,
124
+ hidden_size // mxfp4_block,
125
+ dtype=scale_dtype),
126
+ requires_grad=False)
127
+ layer.register_parameter("w13_weight_scale", w13_weight_scale)
128
+ set_weight_attrs(w13_weight_scale, extra_weight_attrs)
129
+
130
+ w13_bias = torch.nn.Parameter(torch.zeros(
131
+ num_experts,
132
+ 2 * intermediate_size_per_partition_after_pad,
133
+ dtype=torch.bfloat16),
134
+ requires_grad=False)
135
+ layer.register_parameter("w13_bias", w13_bias)
136
+ set_weight_attrs(w13_bias, extra_weight_attrs)
137
+
138
+ # down_proj (row parallel)
139
+ w2_weight = torch.nn.Parameter(torch.zeros(
140
+ num_experts,
141
+ hidden_size,
142
+ intermediate_size_per_partition_after_pad // 2,
143
+ dtype=weight_dtype),
144
+ requires_grad=False)
145
+ layer.register_parameter("w2_weight", w2_weight)
146
+ set_weight_attrs(w2_weight, extra_weight_attrs)
147
+
148
+ w2_weight_scale = torch.nn.Parameter(torch.zeros(
149
+ num_experts,
150
+ hidden_size,
151
+ intermediate_size_per_partition_after_pad // mxfp4_block,
152
+ dtype=scale_dtype),
153
+ requires_grad=False)
154
+ layer.register_parameter("w2_weight_scale", w2_weight_scale)
155
+ set_weight_attrs(w2_weight_scale, extra_weight_attrs)
156
+
157
+ w2_bias = torch.nn.Parameter(torch.zeros(num_experts,
158
+ hidden_size,
159
+ dtype=torch.bfloat16),
160
+ requires_grad=False)
161
+ layer.register_parameter("w2_bias", w2_bias)
162
+ set_weight_attrs(w2_bias, extra_weight_attrs)
163
+
164
+ def process_weights_after_loading(self, layer):
165
+ if (envs.VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8
166
+ or envs.VLLM_USE_FLASHINFER_MOE_MXFP4_BF16):
167
+ layer.gemm1_alpha = Parameter(torch.tensor(
168
+ [1.702] * self.num_experts, dtype=torch.float32).cuda(),
169
+ requires_grad=False)
170
+ layer.gemm1_beta = Parameter(torch.tensor(
171
+ [1.0] * self.num_experts, dtype=torch.float32).cuda(),
172
+ requires_grad=False)
173
+ layer.gemm1_clamp_limit = Parameter(torch.tensor(
174
+ [7.0] * self.num_experts, dtype=torch.float32).cuda(),
175
+ requires_grad=False)
176
+ sf_block_size = 32 # mxfp4 block size
177
+
178
+ assert (layer.w13_weight.dim() == 3
179
+ and layer.w13_weight.shape[0] == self.num_experts
180
+ and layer.w13_weight.shape[1] == self.intermediate_size * 2
181
+ and layer.w13_weight.shape[2] == self.hidden_size // 2)
182
+ assert (layer.w13_weight_scale.dim() == 3
183
+ and layer.w13_weight_scale.shape[0] == self.num_experts
184
+ and layer.w13_weight_scale.shape[1]
185
+ == self.intermediate_size * 2
186
+ and layer.w13_weight_scale.shape[2]
187
+ == self.hidden_size // sf_block_size)
188
+ assert (layer.w2_weight.dim() == 3
189
+ and layer.w2_weight.shape[0] == self.num_experts
190
+ and layer.w2_weight.shape[1] == self.hidden_size and
191
+ layer.w2_weight.shape[2] == self.intermediate_size // 2)
192
+ assert (layer.w2_weight_scale.dim() == 3
193
+ and layer.w2_weight_scale.shape[1] == self.hidden_size
194
+ and layer.w2_weight_scale.shape[2]
195
+ == self.intermediate_size // sf_block_size)
196
+ assert (layer.w13_bias.dim() == 2
197
+ and layer.w13_bias.shape[0] == self.num_experts
198
+ and layer.w13_bias.shape[1] == self.intermediate_size * 2)
199
+ assert (layer.w2_bias.dim() == 2
200
+ and layer.w2_bias.shape[0] == self.num_experts
201
+ and layer.w2_bias.shape[1] == self.hidden_size)
202
+
203
+ w13_weight_scale = layer.w13_weight_scale.data
204
+ w2_weight_scale = layer.w2_weight_scale.data
205
+ w13_weight = layer.w13_weight.data
206
+ w2_weight = layer.w2_weight.data
207
+ w13_bias = layer.w13_bias.data.to(torch.float32)
208
+ w2_bias = layer.w2_bias.data.to(torch.float32)
209
+
210
+ # Swap w1 and w3 as the defenition of
211
+ # swiglu is different in the trtllm-gen
212
+ def swap_every_two_rows(x, axis=-1):
213
+ shape = x.shape
214
+ if axis < 0:
215
+ axis = len(shape) + axis
216
+
217
+ # Create a new shape with pairs swapped along specified axis
218
+ new_shape = list(shape)
219
+ new_shape[axis] = shape[axis] // 2
220
+ new_shape.insert(axis + 1, 2)
221
+
222
+ # Reshape to expose pairs, swap them, and reshape back
223
+ x = x.reshape(*new_shape)
224
+ x = x.flip(axis + 1)
225
+ new_shape = list(shape)
226
+ return x.reshape(*new_shape)
227
+
228
+ w13_weight_scale = swap_every_two_rows(w13_weight_scale, -2)
229
+ w13_weight = swap_every_two_rows(w13_weight, -2)
230
+ w13_bias = swap_every_two_rows(w13_bias, -1)
231
+
232
+ # Do not interleave as the checkpoint is already interleaved
233
+
234
+ # Shuffle weights and scaling factors for transposed mma output
235
+ gemm1_weights_mxfp4_shuffled = []
236
+ gemm1_scales_mxfp4_shuffled = []
237
+ gemm2_weights_mxfp4_shuffled = []
238
+ gemm2_scales_mxfp4_shuffled = []
239
+ gemm1_bias_shuffled = []
240
+ gemm2_bias_shuffled = []
241
+ epilogue_tile_m = 128 # FIXME: this depends on the kernel internals
242
+ for i in range(self.num_experts):
243
+ gemm1_weights_mxfp4_shuffled.append(
244
+ shuffle_matrix_a(w13_weight[i].view(torch.uint8),
245
+ epilogue_tile_m))
246
+ gemm1_scales_mxfp4_shuffled.append(
247
+ shuffle_matrix_sf_a(w13_weight_scale[i].view(torch.uint8),
248
+ epilogue_tile_m))
249
+ gemm1_bias_shuffled.append(
250
+ shuffle_matrix_a(w13_bias[i].clone().reshape(-1, 1),
251
+ epilogue_tile_m))
252
+
253
+ gemm2_weights_mxfp4_shuffled.append(
254
+ shuffle_matrix_a(w2_weight[i].view(torch.uint8),
255
+ epilogue_tile_m))
256
+ gemm2_scales_mxfp4_shuffled.append(
257
+ shuffle_matrix_sf_a(w2_weight_scale[i].view(torch.uint8),
258
+ epilogue_tile_m))
259
+ gemm2_bias_shuffled.append(
260
+ shuffle_matrix_a(w2_bias[i].clone().reshape(-1, 1),
261
+ epilogue_tile_m))
262
+
263
+ w13_weight = torch.stack(gemm1_weights_mxfp4_shuffled)
264
+ w13_weight_scale = torch.stack(
265
+ gemm1_scales_mxfp4_shuffled).reshape(
266
+ self.num_experts, 2 * self.intermediate_size,
267
+ self.hidden_size // sf_block_size).view(
268
+ torch.float8_e4m3fn)
269
+
270
+ w2_weight = torch.stack(gemm2_weights_mxfp4_shuffled)
271
+ w2_weight_scale = torch.stack(gemm2_scales_mxfp4_shuffled).reshape(
272
+ self.num_experts, self.hidden_size, self.intermediate_size //
273
+ sf_block_size).view(torch.float8_e4m3fn)
274
+
275
+ layer.w13_weight = Parameter(w13_weight, requires_grad=False)
276
+ layer.w13_weight_scale = Parameter(w13_weight_scale,
277
+ requires_grad=False)
278
+ layer.w2_weight = Parameter(w2_weight, requires_grad=False)
279
+ layer.w2_weight_scale = Parameter(w2_weight_scale,
280
+ requires_grad=False)
281
+ layer.w13_bias = Parameter(
282
+ torch.stack(gemm1_bias_shuffled).reshape(self.num_experts, -1),
283
+ requires_grad=False)
284
+ layer.w2_bias = Parameter(torch.stack(gemm2_bias_shuffled).reshape(
285
+ self.num_experts, -1),
286
+ requires_grad=False)
287
+ return
288
+
289
+ def _get_tile_tokens_dim(self, x: torch.Tensor, top_k: int):
290
+ # Number of tokens in the input tensor.
291
+ num_tokens = x.shape[0]
292
+ # Factor to account for the imbalance of the experts.
293
+ # factor equals to the
294
+ # max_real_num_tokens_per_expert / perfect_num_tokens_per_expert
295
+ # - 1.0 means perfect expert distribution.
296
+ # - > 1.0 means some experts have more
297
+ # tokens than the perfect distribution.
298
+ # - < 1.0 does not make sense.
299
+ imbalance_factor = 1.3
300
+ # Calculate the number of tokens per expert
301
+ # assuming perfect distribution.
302
+ num_tokens_per_expert = (num_tokens * top_k) // self.num_experts
303
+ # Apply the imbalance factor.
304
+ num_tokens_per_expert = int(num_tokens_per_expert * imbalance_factor)
305
+ # And pad the number to the next power of 2.
306
+ tile_tokens_dim = next_power_of_2(num_tokens_per_expert)
307
+ # Cap to 8-64 tokens per CTA tile
308
+ # as it's the range supported by the kernel.
309
+ tile_tokens_dim = min(max(tile_tokens_dim, 8), 64)
310
+
311
+ return tile_tokens_dim
312
+
313
+ def apply(
314
+ self,
315
+ layer: torch.nn.Module,
316
+ x: torch.Tensor,
317
+ router_logits: torch.Tensor,
318
+ top_k: int,
319
+ renormalize: bool,
320
+ use_grouped_topk: bool = False,
321
+ topk_group: Optional[int] = None,
322
+ num_expert_group: Optional[int] = None,
323
+ global_num_experts: int = -1,
324
+ expert_map: Optional[torch.Tensor] = None,
325
+ custom_routing_function: Optional[Callable] = None,
326
+ scoring_func: str = "softmax",
327
+ e_score_correction_bias: Optional[torch.Tensor] = None,
328
+ apply_router_weight_on_input: bool = False,
329
+ activation: str = "silu",
330
+ enable_eplb: bool = False,
331
+ expert_load_view: Optional[torch.Tensor] = None,
332
+ logical_to_physical_map: Optional[torch.Tensor] = None,
333
+ logical_replica_count: Optional[torch.Tensor] = None,
334
+ ) -> torch.Tensor:
335
+
336
+ if enable_eplb:
337
+ raise NotImplementedError("EPLB is not supported for mxfp4")
338
+
339
+ assert _can_support_mxfp4(
340
+ use_grouped_topk, topk_group, num_expert_group, expert_map,
341
+ custom_routing_function, e_score_correction_bias,
342
+ apply_router_weight_on_input, scoring_func, activation,
343
+ expert_load_view, logical_to_physical_map,
344
+ logical_replica_count), ("MXFP4 are not supported\
345
+ with this configuration.")
346
+
347
+ if (envs.VLLM_USE_FLASHINFER_MOE_MXFP4_MXFP8
348
+ or envs.VLLM_USE_FLASHINFER_MOE_MXFP4_BF16):
349
+ assert not self.moe.use_ep, (
350
+ "EP is not supported for flashinfer mxfp4 moe backend yet.")
351
+ if envs.VLLM_USE_FLASHINFER_MOE_MXFP4_BF16:
352
+ assert x.dtype == torch.bfloat16
353
+ x_quant = x
354
+ x_scale = None
355
+ else:
356
+ x_quant, x_scale = mxfp8_quantize(x, False) # to mxfp8
357
+ x_scale = x_scale.view(torch.float8_e4m3fn).reshape(-1)
358
+ trtllm_gen_output = trtllm_fp4_block_scale_moe(
359
+ router_logits.to(torch.bfloat16),
360
+ None, # routing_bias
361
+ x_quant,
362
+ x_scale,
363
+ layer.w13_weight, # uint8 (e2m1 x 2)
364
+ layer.w13_weight_scale, # uint8 (e4m3 x 2)
365
+ layer.w13_bias, # fp32 per expert per channel
366
+ layer.gemm1_alpha, # fp32 per expert
367
+ layer.gemm1_beta, # fp32 per expert
368
+ layer.gemm1_clamp_limit, # fp32 per expert
369
+ layer.w2_weight, # uint8 (e2m1 x 2)
370
+ layer.w2_weight_scale, # ue8m0
371
+ layer.w2_bias, # fp32 per expert per channel
372
+ None, # output1_scale_scalar
373
+ None, # output1_scale_gate_scalar
374
+ None, # output2_scale_scalar
375
+ self.num_experts,
376
+ top_k,
377
+ None, # n_group
378
+ None, # topk_group
379
+ self.intermediate_size, # padded to multiple of 256
380
+ 0, # local_expert_offset
381
+ self.num_experts, # local num experts
382
+ None,
383
+ self._get_tile_tokens_dim(x, top_k),
384
+ 1 if renormalize else 0, # routing_method_type, renormalize
385
+ True, # do finalize
386
+ )[0]
387
+ return trtllm_gen_output
vllm/model_executor/layers/quantization/utils/mxfp4_utils.py
+22
@@ -1,5 +1,7 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+from typing import Callable, Optional
4
+
5
import torch
6
7
from vllm.utils import direct_register_custom_op
@@ -7,6 +9,26 @@ from vllm.utils import direct_register_custom_op
9
OCP_MX_BLOCK_SIZE = 32
10
11
12
+def _can_support_mxfp4(use_grouped_topk: bool = False,
13
+ topk_group: Optional[int] = None,
14
+ num_expert_group: Optional[int] = None,
15
+ expert_map: Optional[torch.Tensor] = None,
16
+ custom_routing_function: Optional[Callable] = None,
17
+ e_score_correction_bias: Optional[torch.Tensor] = None,
18
+ apply_router_weight_on_input: bool = False,
19
+ scoring_func: str = "softmax",
20
+ activation: str = "silu",
21
+ expert_load_view: Optional[torch.Tensor] = None,
22
+ logical_to_physical_map: Optional[torch.Tensor] = None,
23
+ logical_replica_count: Optional[torch.Tensor] = None):
24
+ return not (use_grouped_topk or topk_group or num_expert_group
25
+ or expert_map or custom_routing_function
26
+ or e_score_correction_bias or apply_router_weight_on_input
27
+ or scoring_func != "softmax" or activation != "silu"
28
+ or expert_load_view or logical_to_physical_map
29
+ or logical_replica_count)
30
+
31
+
32
def _dequant_mxfp4(x: torch.Tensor, scale: torch.Tensor,
33
float_dtype: torch.dtype) -> torch.Tensor:
34
try: