Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / components / quantization / quantizers / mxfp8.py: 84%
44 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""MXFP8 quantizer for NPU dynamic block quantization."""
17from typing import Optional
19import torch # pylint: disable=forbidden-backend-import
21from hyper_parallel.components.quantization.config import (
22 GROUP_LIST_COUNTS,
23 GROUP_LIST_OFFSETS,
24)
25from hyper_parallel.components.quantization.ops.npu_mxfp8 import (
26 MXFP8NpuOps,
27 _get_npu_ops,
28)
29from hyper_parallel.components.quantization.quantizers.base import Quantizer
30from hyper_parallel.components.quantization.tensor import (
31 MXFP8Tensor,
32 QuantizedTensor,
33)
36class MXFP8Quantizer(Quantizer):
37 """Build E4M3 data and E8M0 scales for MX block computation."""
39 def __init__(
40 self,
41 quant_dtype: Optional[torch.dtype] = None,
42 *,
43 npu_ops: Optional[MXFP8NpuOps] = None,
44 ) -> None:
45 """Create a quantizer with lazy NPU operator resolution."""
47 self.quant_dtype = quant_dtype or torch.float8_e4m3fn
48 self._npu_ops = npu_ops
50 @property
51 def npu_ops(self) -> MXFP8NpuOps:
52 """Return the injected or lazily resolved NPU operator adapter."""
54 if self._npu_ops is None:
55 self._npu_ops = _get_npu_ops()
56 return self._npu_ops
58 def quantize(
59 self,
60 tensor: torch.Tensor,
61 *,
62 group_list: Optional[torch.Tensor] = None,
63 group_list_type: int = GROUP_LIST_OFFSETS,
64 rowwise: bool = False,
65 colwise: bool = False,
66 ) -> MXFP8Tensor:
67 """Quantize a tensor into the requested MXFP8 directions."""
69 if not isinstance(tensor, torch.Tensor) or isinstance(
70 tensor,
71 QuantizedTensor,
72 ):
73 raise TypeError(
74 "MXFP8Quantizer expects a high-precision torch.Tensor."
75 )
76 if tensor.ndim < 2:
77 raise ValueError(
78 "MXFP8Quantizer expects at least two dimensions, but got "
79 f"shape {tuple(tensor.shape)}."
80 )
81 if not rowwise and not colwise:
82 raise ValueError(
83 "MXFP8Quantizer requires rowwise=True or colwise=True."
84 )
85 if group_list_type not in (GROUP_LIST_OFFSETS, GROUP_LIST_COUNTS):
86 raise ValueError(
87 "MXFP8Quantizer group_list_type must be 0 or 1, "
88 f"but got {group_list_type}."
89 )
90 if group_list is not None and (
91 not isinstance(group_list, torch.Tensor) or group_list.ndim != 1
92 ):
93 raise ValueError(
94 "MXFP8Quantizer group_list must be a one-dimensional Tensor."
95 )
97 row_data = None
98 row_scale = None
99 col_data = None
100 col_scale = None
101 if group_list is not None:
102 if not colwise:
103 raise ValueError(
104 "Grouped MXFP8 quantization requires colwise=True."
105 )
106 quant_group_list = (
107 group_list
108 if group_list_type == GROUP_LIST_OFFSETS
109 else torch.cumsum(group_list, dim=0)
110 )
111 if rowwise:
112 row_data, row_scale = self.npu_ops.dynamic_mx_quant(
113 tensor,
114 axis=-1,
115 quant_dtype=self.quant_dtype,
116 )
117 col_data, col_scale = self.npu_ops.grouped_dynamic_mx_quant(
118 tensor,
119 quant_group_list,
120 quant_dtype=self.quant_dtype,
121 )
122 elif rowwise and colwise:
123 (
124 row_data,
125 row_scale,
126 col_data,
127 col_scale,
128 ) = self.npu_ops.dynamic_mx_quant_dual_axis(
129 tensor,
130 quant_dtype=self.quant_dtype,
131 )
132 elif rowwise:
133 row_data, row_scale = self.npu_ops.dynamic_mx_quant(
134 tensor,
135 axis=-1,
136 quant_dtype=self.quant_dtype,
137 )
138 elif colwise:
139 col_data, col_scale = self.npu_ops.dynamic_mx_quant(
140 tensor,
141 axis=-2,
142 quant_dtype=self.quant_dtype,
143 )
144 return MXFP8Tensor(
145 shape=tensor.shape,
146 dtype=tensor.dtype,
147 quantizer=self,
148 row_data=row_data,
149 row_scale=row_scale,
150 col_data=col_data,
151 col_scale=col_scale,
152 device=tensor.device,
153 )