@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 31f09c615f
Fork 0

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: