main
py 150 lines 4.83 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Tests for MXFP6 linear kernel selection logic (CPU-only).
4
5 Run `pytest tests/kernels/quantization/test_mxfp6_kernel_selection.py`.
6 """
7
8 from unittest.mock import patch
9
10 import pytest
11 import torch
12
13 from vllm.model_executor.kernels.linear import (
14 EmulationMxfp6LinearKernel,
15 MxFp6LinearKernel,
16 MxFp6LinearLayerConfig,
17 init_mxfp6_linear_kernel,
18 register_linear_kernel,
19 )
20 from vllm.model_executor.layers.quantization.utils.quant_utils import (
21 kMxfp4Dynamic,
22 kMxfp4Static,
23 kMxfp6E2M3Dynamic,
24 kMxfp6E2M3Static,
25 kMxfp6E3M2Dynamic,
26 kMxfp6E3M2Static,
27 )
28 from vllm.platforms import PlatformEnum
29
30 pytestmark = pytest.mark.cpu_test
31
32 # The only implementation available at the moment is software emulation.
33 _WEIGHT_QUANT_KEYS = [kMxfp6E3M2Static, kMxfp6E2M3Static]
34
35
36 def test_can_implement_is_abstract():
37 """Test that can_implement()/is_supported() are properly defined."""
38 assert hasattr(MxFp6LinearKernel, "can_implement")
39 assert hasattr(MxFp6LinearKernel, "is_supported")
40
41
42 def test_emulation_kernel_rejects_non_mxfp6_weights():
43 """EmulationMxfp6LinearKernel must not implement a non-MXFP6 weight
44 format."""
45 config = MxFp6LinearLayerConfig(weight_quant_key=kMxfp4Static)
46 can_implement, reason = EmulationMxfp6LinearKernel.can_implement(config)
47 assert not can_implement
48 assert reason
49
50
51 @pytest.mark.parametrize("weight_quant_key", _WEIGHT_QUANT_KEYS)
52 @pytest.mark.parametrize(
53 "activation_quant_key",
54 [None, kMxfp4Dynamic, kMxfp6E3M2Dynamic, kMxfp6E2M3Dynamic],
55 )
56 def test_emulation_kernel_accepts_any_supported_config(
57 weight_quant_key, activation_quant_key
58 ):
59 """EmulationMxfp6LinearKernel is the only backend today: it must accept
60 every supported weight/activation format combination."""
61 config = MxFp6LinearLayerConfig(
62 weight_quant_key=weight_quant_key, activation_quant_key=activation_quant_key
63 )
64 can_implement, reason = EmulationMxfp6LinearKernel.can_implement(config)
65 assert can_implement, reason
66
67
68 @pytest.mark.parametrize("weight_quant_key", _WEIGHT_QUANT_KEYS)
69 def test_emulation_kernel_rejects_non_mxfp4_or_mxfp6_activation(weight_quant_key):
70 config = MxFp6LinearLayerConfig(
71 weight_quant_key=weight_quant_key, activation_quant_key=kMxfp4Static
72 )
73 can_implement, reason = EmulationMxfp6LinearKernel.can_implement(config)
74 assert not can_implement
75 assert reason
76
77
78 class OOTMxFp6LinearKernel(MxFp6LinearKernel):
79 @classmethod
80 def is_supported(
81 cls, compute_capability: int | None = None
82 ) -> tuple[bool, str | None]:
83 return True, None
84
85 @classmethod
86 def can_implement(cls, config: MxFp6LinearLayerConfig) -> tuple[bool, str | None]:
87 return True, None
88
89 def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
90 pass
91
92 def apply_weights(
93 self,
94 layer: torch.nn.Module,
95 x: torch.Tensor,
96 bias: torch.Tensor | None = None,
97 ) -> torch.Tensor:
98 pass
99
100
101 @patch("vllm.model_executor.kernels.linear.current_platform")
102 def test_init_mxfp6_linear_kernel_dispatches_to_registered_kernel(platform_mock):
103 """init_mxfp6_linear_kernel should select a registered kernel that
104 reports itself as supported/able to implement the given config, and
105 construct it with that exact config."""
106 platform_mock._enum = PlatformEnum.OOT
107 register_linear_kernel(OOTMxFp6LinearKernel, PlatformEnum.OOT, "mxfp6")
108
109 kernel = init_mxfp6_linear_kernel(
110 weight_quant_key=kMxfp6E3M2Static, activation_quant_key=kMxfp6E3M2Dynamic
111 )
112
113 assert isinstance(kernel, OOTMxFp6LinearKernel)
114 assert kernel.config == MxFp6LinearLayerConfig(
115 weight_quant_key=kMxfp6E3M2Static, activation_quant_key=kMxfp6E3M2Dynamic
116 )
117
118
119 class UnsupportedMxFp6LinearKernel(MxFp6LinearKernel):
120 @classmethod
121 def is_supported(
122 cls, compute_capability: int | None = None
123 ) -> tuple[bool, str | None]:
124 return False, "never supported"
125
126 @classmethod
127 def can_implement(cls, config: MxFp6LinearLayerConfig) -> tuple[bool, str | None]:
128 return True, None
129
130 def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
131 pass
132
133 def apply_weights(
134 self,
135 layer: torch.nn.Module,
136 x: torch.Tensor,
137 bias: torch.Tensor | None = None,
138 ) -> torch.Tensor:
139 pass
140
141
142 @patch("vllm.model_executor.kernels.linear.current_platform")
143 def test_init_mxfp6_linear_kernel_raises_when_no_kernel_matches(platform_mock):
144 platform_mock._enum = PlatformEnum.UNSPECIFIED
145 register_linear_kernel(
146 UnsupportedMxFp6LinearKernel, PlatformEnum.UNSPECIFIED, "mxfp6"
147 )
148
149 with pytest.raises(ValueError, match="Failed to find a kernel"):
150 init_mxfp6_linear_kernel(weight_quant_key=kMxfp6E3M2Static)