main
py 93 lines 3.03 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Callable
5 from functools import partial
6
7 import torch
8 import torch.nn.functional as F
9 from torch.nn.parameter import Parameter
10
11 from vllm.model_executor.layers.quantization.utils.mxfp4_utils import (
12 quant_dequant_mxfp4,
13 )
14 from vllm.model_executor.layers.quantization.utils.mxfp6_utils import (
15 dequant_mxfp6,
16 quant_dequant_mxfp6,
17 )
18 from vllm.model_executor.layers.quantization.utils.quant_utils import (
19 QuantKey,
20 kMxfp4Dynamic,
21 kMxfp6E2M3Dynamic,
22 kMxfp6E2M3Static,
23 kMxfp6E3M2Dynamic,
24 kMxfp6E3M2Static,
25 )
26
27 from .base import MxFp6LinearKernel, MxFp6LinearLayerConfig
28
29 _WEIGHT_DEQUANT_FUNCS: dict[QuantKey, Callable[..., torch.Tensor]] = {
30 kMxfp6E3M2Static: partial(dequant_mxfp6, quant_dtype="fp6_e3m2"),
31 kMxfp6E2M3Static: partial(dequant_mxfp6, quant_dtype="fp6_e2m3"),
32 }
33
34 _ACTIVATION_QUANT_DEQUANT_FUNCS: dict[
35 QuantKey, Callable[[torch.Tensor], torch.Tensor]
36 ] = {
37 kMxfp4Dynamic: quant_dequant_mxfp4,
38 kMxfp6E3M2Dynamic: partial(quant_dequant_mxfp6, quant_dtype="fp6_e3m2"),
39 kMxfp6E2M3Dynamic: partial(quant_dequant_mxfp6, quant_dtype="fp6_e2m3"),
40 }
41
42
43 class EmulationMxfp6LinearKernel(MxFp6LinearKernel):
44 """Software emulation fallback for OCP MXFP4/MXFP6 (dequant + F.linear)."""
45
46 def __init__(self, config: MxFp6LinearLayerConfig) -> None:
47 super().__init__(config)
48 self.dequant_func = _WEIGHT_DEQUANT_FUNCS[config.weight_quant_key]
49 if config.activation_quant_key is None:
50 # no input Q/DQ for weight-only
51 self.quant_dequant_func: Callable[[torch.Tensor], torch.Tensor] = (
52 lambda x: x
53 )
54 else:
55 self.quant_dequant_func = _ACTIVATION_QUANT_DEQUANT_FUNCS[
56 config.activation_quant_key
57 ]
58
59 @classmethod
60 def is_supported(
61 cls, compute_capability: int | None = None
62 ) -> tuple[bool, str | None]:
63 return True, None
64
65 @classmethod
66 def can_implement(cls, config: MxFp6LinearLayerConfig) -> tuple[bool, str | None]:
67 if config.weight_quant_key not in (
68 kMxfp6E2M3Static,
69 kMxfp6E3M2Static,
70 ):
71 return False, "only supports MXFP6 weights"
72 if config.activation_quant_key not in (
73 None,
74 kMxfp4Dynamic,
75 kMxfp6E3M2Dynamic,
76 kMxfp6E2M3Dynamic,
77 ):
78 return False, "only supports MXFP4 or MXFP6 or unquantized activations"
79
80 return True, None
81
82 def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
83 layer.weight_scale = Parameter(layer.weight_scale.data, requires_grad=False)
84
85 def apply_weights(
86 self,
87 layer: torch.nn.Module,
88 x: torch.Tensor,
89 bias: torch.Tensor | None = None,
90 ) -> torch.Tensor:
91 dq_w = self.dequant_func(layer.weight, layer.weight_scale, x.dtype)
92 qdq_x = self.quant_dequant_func(x)
93 return F.linear(qdq_x, dq_w, bias)