Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / components / quantization / tensor / mxfp8_tensor.py: 81%
52 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +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 directional storage and logical Tensor wrapper."""
17from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional
19import torch # pylint: disable=forbidden-backend-import
21from hyper_parallel.components.quantization.tensor.base import (
22 QuantizedTensorStorage,
23)
24from hyper_parallel.components.quantization.tensor.quantized_tensor import (
25 QuantizedTensor,
26)
28if TYPE_CHECKING:
29 from hyper_parallel.components.quantization.quantizers.mxfp8 import (
30 MXFP8Quantizer,
31 )
34class MXFP8TensorStorage(QuantizedTensorStorage):
35 """Store row-wise and column-wise MXFP8 data with paired scales."""
37 def __init__(
38 self,
39 shape: Iterable[int],
40 dtype: torch.dtype,
41 *,
42 quantizer: "MXFP8Quantizer",
43 row_data: Optional[torch.Tensor] = None,
44 row_scale: Optional[torch.Tensor] = None,
45 col_data: Optional[torch.Tensor] = None,
46 col_scale: Optional[torch.Tensor] = None,
47 ) -> None:
48 """Initialize one typed MXFP8 representation."""
50 self._validate_pair("row", row_data, row_scale)
51 self._validate_pair("column", col_data, col_scale)
52 if row_data is None and col_data is None:
53 raise ValueError(
54 "MXFP8TensorStorage requires a row-wise or column-wise "
55 "representation."
56 )
57 if not isinstance(self, torch.Tensor):
58 self.shape = tuple(shape)
59 self.dtype = dtype
60 self.quantizer = quantizer
61 self.row_data = row_data
62 self.row_scale = row_scale
63 self.col_data = col_data
64 self.col_scale = col_scale
66 @staticmethod
67 def _validate_pair(
68 direction: str,
69 data: Optional[torch.Tensor],
70 scale: Optional[torch.Tensor],
71 ) -> None:
72 if (data is None) != (scale is None):
73 raise ValueError(
74 f"MXFP8 {direction}-wise data and scale must either both be "
75 "present or both be absent."
76 )
78 def update_usage(self, rowwise: bool = True, colwise: bool = True) -> None:
79 """Release directional data that subsequent computation will not use."""
81 if not rowwise:
82 self.row_data = None
83 self.row_scale = None
84 if not colwise:
85 self.col_data = None
86 self.col_scale = None
88 def is_rowwise(self) -> bool:
89 """Return whether row-wise data and scale are available."""
91 return self.row_data is not None and self.row_scale is not None
93 def is_colwise(self) -> bool:
94 """Return whether column-wise data and scale are available."""
96 return self.col_data is not None and self.col_scale is not None
98 def get_metadata(self) -> dict[str, Any]:
99 """Return directional storage and quantizer metadata."""
101 return {
102 "row_data": self.row_data,
103 "row_scale": self.row_scale,
104 "col_data": self.col_data,
105 "col_scale": self.col_scale,
106 "quantizer": self.quantizer,
107 }
110class MXFP8Tensor(MXFP8TensorStorage, QuantizedTensor):
111 """Expose MXFP8 storage as a logical Tensor with the original dtype."""
113 def __new__(
114 cls,
115 shape: Iterable[int],
116 dtype: torch.dtype,
117 *,
118 quantizer: "MXFP8Quantizer",
119 row_data: Optional[torch.Tensor] = None,
120 row_scale: Optional[torch.Tensor] = None,
121 col_data: Optional[torch.Tensor] = None,
122 col_scale: Optional[torch.Tensor] = None,
123 device: Optional[torch.device] = None,
124 requires_grad: bool = False,
125 ) -> "MXFP8Tensor":
126 """Create an MXFP8 wrapper from at least one directional pair."""
128 del quantizer, row_scale, col_scale
129 physical_data = row_data if row_data is not None else col_data
130 if physical_data is None:
131 raise ValueError("MXFP8Tensor requires row_data or col_data.")
132 wrapper_device = physical_data.device if device is None else device
133 return QuantizedTensor.__new__(
134 cls,
135 shape,
136 dtype,
137 device=wrapper_device,
138 requires_grad=requires_grad,
139 )
141 def __init__(
142 self,
143 shape: Iterable[int],
144 dtype: torch.dtype,
145 *,
146 quantizer: "MXFP8Quantizer",
147 row_data: Optional[torch.Tensor] = None,
148 row_scale: Optional[torch.Tensor] = None,
149 col_data: Optional[torch.Tensor] = None,
150 col_scale: Optional[torch.Tensor] = None,
151 device: Optional[torch.device] = None,
152 requires_grad: bool = False,
153 ) -> None:
154 """Initialize directional storage after wrapper allocation."""
156 del device, requires_grad
157 MXFP8TensorStorage.__init__(
158 self,
159 shape,
160 dtype,
161 quantizer=quantizer,
162 row_data=row_data,
163 row_scale=row_scale,
164 col_data=col_data,
165 col_scale=col_scale,
166 )
168 def _new_with_transformed_data(
169 self,
170 transform: Callable[[torch.Tensor], torch.Tensor],
171 *,
172 requires_grad: bool,
173 ) -> "MXFP8Tensor":
174 def _transform_optional(
175 value: Optional[torch.Tensor],
176 ) -> Optional[torch.Tensor]:
177 return None if value is None else transform(value)
179 return type(self)(
180 shape=self.shape,
181 dtype=self.dtype,
182 quantizer=self.quantizer,
183 row_data=_transform_optional(self.row_data),
184 row_scale=_transform_optional(self.row_scale),
185 col_data=_transform_optional(self.col_data),
186 col_scale=_transform_optional(self.col_scale),
187 requires_grad=requires_grad,
188 )
190 def __repr__(self) -> str:
191 """Return logical shape and directional storage state."""
193 return (
194 "MXFP8Tensor("
195 f"shape={tuple(self.shape)}, dtype={self.dtype}, "
196 f"device={self.device}, rowwise={self.is_rowwise()}, "
197 f"colwise={self.is_colwise()})"
198 )