nvidia-modelopt 0.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. modelopt/__init__.py +20 -0
  2. modelopt/deploy/__init__.py +16 -0
  3. modelopt/deploy/llm/__init__.py +30 -0
  4. modelopt/deploy/llm/generate.py +345 -0
  5. modelopt/onnx/__init__.py +34 -0
  6. modelopt/onnx/autocast/__init__.py +19 -0
  7. modelopt/onnx/autocast/__main__.py +187 -0
  8. modelopt/onnx/autocast/convert.py +202 -0
  9. modelopt/onnx/autocast/graphsanitizer.py +422 -0
  10. modelopt/onnx/autocast/logging_config.py +78 -0
  11. modelopt/onnx/autocast/nodeclassifier.py +416 -0
  12. modelopt/onnx/autocast/precisionconverter.py +1032 -0
  13. modelopt/onnx/autocast/referencerunner.py +159 -0
  14. modelopt/onnx/autocast/utils.py +117 -0
  15. modelopt/onnx/llm_export_utils/__init__.py +16 -0
  16. modelopt/onnx/llm_export_utils/export_utils.py +162 -0
  17. modelopt/onnx/llm_export_utils/quantization_utils.py +122 -0
  18. modelopt/onnx/llm_export_utils/surgeon_utils.py +120 -0
  19. modelopt/onnx/logging_config.py +77 -0
  20. modelopt/onnx/op_types.py +300 -0
  21. modelopt/onnx/quantization/__init__.py +20 -0
  22. modelopt/onnx/quantization/__main__.py +291 -0
  23. modelopt/onnx/quantization/calib_utils.py +178 -0
  24. modelopt/onnx/quantization/extensions.py +35 -0
  25. modelopt/onnx/quantization/fp8.py +375 -0
  26. modelopt/onnx/quantization/graph_utils.py +1479 -0
  27. modelopt/onnx/quantization/gs_patching.py +112 -0
  28. modelopt/onnx/quantization/int4.py +1347 -0
  29. modelopt/onnx/quantization/int8.py +283 -0
  30. modelopt/onnx/quantization/operators.py +121 -0
  31. modelopt/onnx/quantization/ort_patching.py +1766 -0
  32. modelopt/onnx/quantization/ort_utils.py +346 -0
  33. modelopt/onnx/quantization/partitioning.py +439 -0
  34. modelopt/onnx/quantization/qdq_utils.py +1349 -0
  35. modelopt/onnx/quantization/quant_utils.py +283 -0
  36. modelopt/onnx/quantization/quantize.py +528 -0
  37. modelopt/onnx/quantization/src/modelopt_round_and_pack_ext.cpp +127 -0
  38. modelopt/onnx/trt_utils.py +424 -0
  39. modelopt/onnx/utils.py +753 -0
  40. modelopt/torch/__init__.py +41 -0
  41. modelopt/torch/_deploy/__init__.py +23 -0
  42. modelopt/torch/_deploy/_runtime/__init__.py +29 -0
  43. modelopt/torch/_deploy/_runtime/common.py +72 -0
  44. modelopt/torch/_deploy/_runtime/ort_client.py +193 -0
  45. modelopt/torch/_deploy/_runtime/registry.py +83 -0
  46. modelopt/torch/_deploy/_runtime/runtime_client.py +154 -0
  47. modelopt/torch/_deploy/_runtime/tensorrt/constants.py +108 -0
  48. modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py +324 -0
  49. modelopt/torch/_deploy/_runtime/tensorrt/hw_param_config.py +51 -0
  50. modelopt/torch/_deploy/_runtime/tensorrt/layerwise_profiling.py +178 -0
  51. modelopt/torch/_deploy/_runtime/tensorrt/parse_trtexec_log.py +151 -0
  52. modelopt/torch/_deploy/_runtime/tensorrt/tensorrt_utils.py +200 -0
  53. modelopt/torch/_deploy/_runtime/trt_client.py +219 -0
  54. modelopt/torch/_deploy/compilation.py +142 -0
  55. modelopt/torch/_deploy/device_model.py +157 -0
  56. modelopt/torch/_deploy/profiling.py +193 -0
  57. modelopt/torch/_deploy/utils/__init__.py +18 -0
  58. modelopt/torch/_deploy/utils/onnx_optimizer.py +120 -0
  59. modelopt/torch/_deploy/utils/onnx_utils.py +58 -0
  60. modelopt/torch/_deploy/utils/torch_onnx.py +593 -0
  61. modelopt/torch/distill/__init__.py +28 -0
  62. modelopt/torch/distill/config.py +130 -0
  63. modelopt/torch/distill/distillation.py +84 -0
  64. modelopt/torch/distill/distillation_model.py +318 -0
  65. modelopt/torch/distill/loss_balancers.py +140 -0
  66. modelopt/torch/distill/losses.py +275 -0
  67. modelopt/torch/distill/mode.py +223 -0
  68. modelopt/torch/distill/plugins/__init__.py +24 -0
  69. modelopt/torch/distill/plugins/huggingface.py +110 -0
  70. modelopt/torch/distill/plugins/megatron.py +476 -0
  71. modelopt/torch/distill/registry.py +23 -0
  72. modelopt/torch/export/__init__.py +24 -0
  73. modelopt/torch/export/convert_hf_config.py +117 -0
  74. modelopt/torch/export/distribute.py +300 -0
  75. modelopt/torch/export/hf_config_map.py +84 -0
  76. modelopt/torch/export/layer_utils.py +1893 -0
  77. modelopt/torch/export/mcore_config_map.py +25 -0
  78. modelopt/torch/export/model_config.py +623 -0
  79. modelopt/torch/export/model_config_export.py +552 -0
  80. modelopt/torch/export/model_config_utils.py +392 -0
  81. modelopt/torch/export/model_utils.py +71 -0
  82. modelopt/torch/export/plugins/__init__.py +21 -0
  83. modelopt/torch/export/plugins/mcore_common.py +67 -0
  84. modelopt/torch/export/plugins/mcore_custom.py +530 -0
  85. modelopt/torch/export/plugins/mcore_deepseek.py +143 -0
  86. modelopt/torch/export/plugins/mcore_gptoss.py +71 -0
  87. modelopt/torch/export/plugins/mcore_llama.py +164 -0
  88. modelopt/torch/export/plugins/mcore_nemotron.py +90 -0
  89. modelopt/torch/export/plugins/mcore_qwen.py +99 -0
  90. modelopt/torch/export/plugins/megatron_importer.py +647 -0
  91. modelopt/torch/export/postprocess.py +847 -0
  92. modelopt/torch/export/quant_utils.py +1069 -0
  93. modelopt/torch/export/tensorrt_llm_type.py +42 -0
  94. modelopt/torch/export/tensorrt_llm_utils.py +405 -0
  95. modelopt/torch/export/transformer_engine.py +94 -0
  96. modelopt/torch/export/unified_export_hf.py +537 -0
  97. modelopt/torch/export/unified_export_megatron.py +1164 -0
  98. modelopt/torch/nas/__init__.py +27 -0
  99. modelopt/torch/nas/algorithms.py +856 -0
  100. modelopt/torch/nas/autonas.py +808 -0
  101. modelopt/torch/nas/conversion.py +119 -0
  102. modelopt/torch/nas/hparams/__init__.py +19 -0
  103. modelopt/torch/nas/hparams/concat.py +319 -0
  104. modelopt/torch/nas/hparams/container.py +48 -0
  105. modelopt/torch/nas/modules/__init__.py +21 -0
  106. modelopt/torch/nas/modules/container.py +99 -0
  107. modelopt/torch/nas/modules/conv.py +254 -0
  108. modelopt/torch/nas/modules/linear.py +76 -0
  109. modelopt/torch/nas/modules/norm.py +193 -0
  110. modelopt/torch/nas/modules/utils.py +61 -0
  111. modelopt/torch/nas/patch.py +261 -0
  112. modelopt/torch/nas/plugins/__init__.py +29 -0
  113. modelopt/torch/nas/plugins/megatron.py +1497 -0
  114. modelopt/torch/nas/plugins/torch.py +71 -0
  115. modelopt/torch/nas/plugins/transformer_engine.py +42 -0
  116. modelopt/torch/nas/plugins/transformers.py +210 -0
  117. modelopt/torch/nas/registry.py +23 -0
  118. modelopt/torch/nas/search_space.py +260 -0
  119. modelopt/torch/nas/traced_hp.py +149 -0
  120. modelopt/torch/nas/utils.py +408 -0
  121. modelopt/torch/opt/__init__.py +41 -0
  122. modelopt/torch/opt/_hooks.py +98 -0
  123. modelopt/torch/opt/config.py +383 -0
  124. modelopt/torch/opt/conversion.py +620 -0
  125. modelopt/torch/opt/dynamic.py +1367 -0
  126. modelopt/torch/opt/hparam.py +275 -0
  127. modelopt/torch/opt/mode.py +353 -0
  128. modelopt/torch/opt/plugins/__init__.py +38 -0
  129. modelopt/torch/opt/plugins/diffusers.py +40 -0
  130. modelopt/torch/opt/plugins/huggingface.py +162 -0
  131. modelopt/torch/opt/plugins/mcore_dist_checkpointing.py +211 -0
  132. modelopt/torch/opt/plugins/megatron.py +142 -0
  133. modelopt/torch/opt/plugins/peft.py +108 -0
  134. modelopt/torch/opt/plugins/transformers.py +160 -0
  135. modelopt/torch/opt/searcher.py +363 -0
  136. modelopt/torch/opt/utils.py +125 -0
  137. modelopt/torch/prune/__init__.py +26 -0
  138. modelopt/torch/prune/fastnas.py +376 -0
  139. modelopt/torch/prune/gradnas.py +349 -0
  140. modelopt/torch/prune/plugins/__init__.py +24 -0
  141. modelopt/torch/prune/plugins/mcore_minitron.py +263 -0
  142. modelopt/torch/prune/plugins/transformers.py +41 -0
  143. modelopt/torch/prune/pruning.py +211 -0
  144. modelopt/torch/quantization/__init__.py +26 -0
  145. modelopt/torch/quantization/algorithms.py +671 -0
  146. modelopt/torch/quantization/backends/__init__.py +20 -0
  147. modelopt/torch/quantization/backends/fp8_per_tensor_gemm.py +204 -0
  148. modelopt/torch/quantization/backends/gemm_registry.py +156 -0
  149. modelopt/torch/quantization/backends/nvfp4_gemm.py +201 -0
  150. modelopt/torch/quantization/backends/utils.py +28 -0
  151. modelopt/torch/quantization/calib/__init__.py +25 -0
  152. modelopt/torch/quantization/calib/bias.py +172 -0
  153. modelopt/torch/quantization/calib/calibrator.py +63 -0
  154. modelopt/torch/quantization/calib/histogram.py +434 -0
  155. modelopt/torch/quantization/calib/max.py +110 -0
  156. modelopt/torch/quantization/compress.py +227 -0
  157. modelopt/torch/quantization/config.py +1180 -0
  158. modelopt/torch/quantization/conversion.py +389 -0
  159. modelopt/torch/quantization/export_onnx.py +639 -0
  160. modelopt/torch/quantization/extensions.py +89 -0
  161. modelopt/torch/quantization/mode.py +428 -0
  162. modelopt/torch/quantization/model_calib.py +949 -0
  163. modelopt/torch/quantization/model_quant.py +477 -0
  164. modelopt/torch/quantization/nn/__init__.py +26 -0
  165. modelopt/torch/quantization/nn/functional.py +109 -0
  166. modelopt/torch/quantization/nn/modules/__init__.py +16 -0
  167. modelopt/torch/quantization/nn/modules/quant_activations.py +22 -0
  168. modelopt/torch/quantization/nn/modules/quant_batchnorm.py +24 -0
  169. modelopt/torch/quantization/nn/modules/quant_conv.py +123 -0
  170. modelopt/torch/quantization/nn/modules/quant_instancenorm.py +39 -0
  171. modelopt/torch/quantization/nn/modules/quant_linear.py +258 -0
  172. modelopt/torch/quantization/nn/modules/quant_module.py +211 -0
  173. modelopt/torch/quantization/nn/modules/quant_pooling.py +99 -0
  174. modelopt/torch/quantization/nn/modules/quant_rnn.py +527 -0
  175. modelopt/torch/quantization/nn/modules/tensor_quantizer.py +1293 -0
  176. modelopt/torch/quantization/plugins/__init__.py +73 -0
  177. modelopt/torch/quantization/plugins/accelerate.py +214 -0
  178. modelopt/torch/quantization/plugins/apex.py +52 -0
  179. modelopt/torch/quantization/plugins/attention.py +312 -0
  180. modelopt/torch/quantization/plugins/custom.py +192 -0
  181. modelopt/torch/quantization/plugins/diffusers.py +237 -0
  182. modelopt/torch/quantization/plugins/fairscale.py +45 -0
  183. modelopt/torch/quantization/plugins/huggingface.py +736 -0
  184. modelopt/torch/quantization/plugins/megatron.py +462 -0
  185. modelopt/torch/quantization/plugins/peft.py +148 -0
  186. modelopt/torch/quantization/plugins/transformer_engine.py +60 -0
  187. modelopt/torch/quantization/plugins/transformers.py +52 -0
  188. modelopt/torch/quantization/plugins/transformers_trainer.py +426 -0
  189. modelopt/torch/quantization/plugins/trl.py +27 -0
  190. modelopt/torch/quantization/plugins/vllm.py +199 -0
  191. modelopt/torch/quantization/qtensor/__init__.py +24 -0
  192. modelopt/torch/quantization/qtensor/base_qtensor.py +370 -0
  193. modelopt/torch/quantization/qtensor/fp8_tensor.py +151 -0
  194. modelopt/torch/quantization/qtensor/int4_tensor.py +130 -0
  195. modelopt/torch/quantization/qtensor/int8_tensor.py +124 -0
  196. modelopt/torch/quantization/qtensor/mxfp4_tensor.py +144 -0
  197. modelopt/torch/quantization/qtensor/nf4_tensor.py +199 -0
  198. modelopt/torch/quantization/qtensor/nvfp4_tensor.py +295 -0
  199. modelopt/torch/quantization/src/tensor_quant.cpp +77 -0
  200. modelopt/torch/quantization/src/tensor_quant.h +69 -0
  201. modelopt/torch/quantization/src/tensor_quant_fp8.cpp +48 -0
  202. modelopt/torch/quantization/src/tensor_quant_gpu.cu +361 -0
  203. modelopt/torch/quantization/src/tensor_quant_gpu_fp8.cu +122 -0
  204. modelopt/torch/quantization/src/tensor_quant_mx.cu +411 -0
  205. modelopt/torch/quantization/src/tensor_quant_mx.h +247 -0
  206. modelopt/torch/quantization/tensor_quant.py +816 -0
  207. modelopt/torch/quantization/triton/__init__.py +36 -0
  208. modelopt/torch/quantization/triton/fp4_kernel.py +303 -0
  209. modelopt/torch/quantization/utils.py +443 -0
  210. modelopt/torch/sparsity/__init__.py +19 -0
  211. modelopt/torch/sparsity/config.py +51 -0
  212. modelopt/torch/sparsity/magnitude.py +148 -0
  213. modelopt/torch/sparsity/mode.py +203 -0
  214. modelopt/torch/sparsity/module.py +87 -0
  215. modelopt/torch/sparsity/plugins/__init__.py +27 -0
  216. modelopt/torch/sparsity/plugins/megatron.py +93 -0
  217. modelopt/torch/sparsity/searcher.py +84 -0
  218. modelopt/torch/sparsity/sparsegpt.py +276 -0
  219. modelopt/torch/sparsity/sparsification.py +123 -0
  220. modelopt/torch/speculative/__init__.py +20 -0
  221. modelopt/torch/speculative/config.py +100 -0
  222. modelopt/torch/speculative/eagle/__init__.py +20 -0
  223. modelopt/torch/speculative/eagle/conversion.py +67 -0
  224. modelopt/torch/speculative/eagle/default_config.py +50 -0
  225. modelopt/torch/speculative/eagle/eagle_model.py +51 -0
  226. modelopt/torch/speculative/eagle/utils.py +91 -0
  227. modelopt/torch/speculative/medusa/__init__.py +19 -0
  228. modelopt/torch/speculative/medusa/conversion.py +59 -0
  229. modelopt/torch/speculative/medusa/medusa_model.py +32 -0
  230. modelopt/torch/speculative/mode.py +86 -0
  231. modelopt/torch/speculative/plugins/__init__.py +33 -0
  232. modelopt/torch/speculative/plugins/megatron_eagle.py +2119 -0
  233. modelopt/torch/speculative/plugins/megatron_medusa.py +312 -0
  234. modelopt/torch/speculative/plugins/transformers.py +1138 -0
  235. modelopt/torch/speculative/speculative_decoding.py +59 -0
  236. modelopt/torch/speculative/utils.py +364 -0
  237. modelopt/torch/trace/__init__.py +25 -0
  238. modelopt/torch/trace/analyzer.py +1368 -0
  239. modelopt/torch/trace/modules/__init__.py +19 -0
  240. modelopt/torch/trace/modules/concat.py +384 -0
  241. modelopt/torch/trace/modules/nn.py +153 -0
  242. modelopt/torch/trace/plugins/__init__.py +34 -0
  243. modelopt/torch/trace/plugins/megatron.py +36 -0
  244. modelopt/torch/trace/plugins/transformers.py +58 -0
  245. modelopt/torch/trace/symbols.py +545 -0
  246. modelopt/torch/trace/tracer.py +331 -0
  247. modelopt/torch/utils/__init__.py +28 -0
  248. modelopt/torch/utils/_pytree.py +133 -0
  249. modelopt/torch/utils/cpp_extension.py +91 -0
  250. modelopt/torch/utils/dataset_utils.py +484 -0
  251. modelopt/torch/utils/distributed.py +311 -0
  252. modelopt/torch/utils/graph.py +123 -0
  253. modelopt/torch/utils/image_processor.py +112 -0
  254. modelopt/torch/utils/import_utils.py +35 -0
  255. modelopt/torch/utils/list.py +54 -0
  256. modelopt/torch/utils/logging.py +127 -0
  257. modelopt/torch/utils/memory_monitor.py +146 -0
  258. modelopt/torch/utils/network.py +658 -0
  259. modelopt/torch/utils/perf.py +174 -0
  260. modelopt/torch/utils/plugins/__init__.py +27 -0
  261. modelopt/torch/utils/plugins/megatron_generate.py +299 -0
  262. modelopt/torch/utils/plugins/megatron_mmlu.py +152 -0
  263. modelopt/torch/utils/plugins/megatron_preprocess_data.py +200 -0
  264. modelopt/torch/utils/random.py +163 -0
  265. modelopt/torch/utils/speech_dataset_utils.py +134 -0
  266. modelopt/torch/utils/tensor.py +87 -0
  267. modelopt/torch/utils/vlm_dataset_utils.py +110 -0
  268. nvidia_modelopt-0.35.0.dist-info/METADATA +171 -0
  269. nvidia_modelopt-0.35.0.dist-info/RECORD +272 -0
  270. nvidia_modelopt-0.35.0.dist-info/WHEEL +5 -0
  271. nvidia_modelopt-0.35.0.dist-info/licenses/LICENSE +14 -0
  272. nvidia_modelopt-0.35.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,151 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Implements FP8 quantization for efficient tensor storage and computation."""
17
+
18
+ import math
19
+
20
+ import torch
21
+
22
+ from ..qtensor.base_qtensor import BaseQuantizedTensor
23
+ from ..utils import (
24
+ convert_quantization_axis_to_reduce_axis,
25
+ reduce_amax,
26
+ reduce_block_amax,
27
+ reduce_block_padding,
28
+ )
29
+
30
+ __all__ = ["FP8QTensor"]
31
+
32
+
33
+ class FP8QTensor(BaseQuantizedTensor):
34
+ """Implements the FP8 quantization on tensors for more efficient storage or computation.
35
+
36
+ Attributes:
37
+ quantized_data (torch.Tensor): The quantized data stored as a packed fp8 tensor.
38
+ """
39
+
40
+ @classmethod
41
+ def quantize(
42
+ cls,
43
+ input: torch.Tensor,
44
+ scales: torch.Tensor = None,
45
+ axis: tuple | int | None = None,
46
+ block_sizes: dict | None = None,
47
+ ) -> tuple:
48
+ """Converting a tensor to a quantized format based on FP8 quantization. Only E4M3 is supported.
49
+
50
+ Args:
51
+ input (torch.Tensor): The input tensor to be quantized.
52
+ scales (torch.Tensor): The scales for quantization.
53
+ axis: The dimensions to reduce for quantization. None or int or tuple of ints.
54
+ block_sizes (dict): A dictionary specifying the block size for each dimension.
55
+
56
+ Note: One can only provide axis or block_sizes for FP8 quantization.
57
+
58
+ Returns:
59
+ tuple: FP8QTensor, scales
60
+ """
61
+ original_input = input
62
+
63
+ # If block_sizes is provided, pad the input so that each dimension is divisible by the block size.
64
+ if block_sizes:
65
+ input = reduce_block_padding(input, block_sizes)
66
+
67
+ # Compute scales if not provided
68
+ if scales is None:
69
+ if block_sizes:
70
+ amax = reduce_block_amax(input, block_sizes)
71
+ else:
72
+ reduce_axis = convert_quantization_axis_to_reduce_axis(input, axis)
73
+ amax = reduce_amax(input, axis=reduce_axis)
74
+ scales = amax / 448.0 # Consider parameterizing the divisor if needed
75
+
76
+ # Determine the expected scales shape from the (possibly padded) input
77
+ expected_shape = list(input.shape)
78
+ expanded_scales = scales.clone()
79
+ if block_sizes:
80
+ for dim, block_size in block_sizes.items():
81
+ # Convert negative indices to positive ones.
82
+ dim = dim if dim >= 0 else len(input.shape) + dim
83
+ # After padding, this should always hold.
84
+ assert input.shape[dim] % block_size == 0, (
85
+ f"Tensor dimension {dim}, {input.shape[dim]} is not divisible by {block_size} even after padding."
86
+ )
87
+ # The scales tensor is expected to have size equal to input.shape[dim] // block_size.
88
+ expected_shape[dim] = input.shape[dim] // block_size
89
+
90
+ # If we get amax from tensor_quantizer, it might not be the expected shape but has the
91
+ # same number of elements, reshape it.
92
+ if scales.shape != tuple(expected_shape) and scales.numel() == math.prod(
93
+ expected_shape
94
+ ):
95
+ scales = scales.reshape(expected_shape)
96
+ expanded_scales = expanded_scales.reshape(expected_shape)
97
+
98
+ assert scales.shape == tuple(expected_shape), (
99
+ f"Mismatch in expected scale shape: {scales.shape} vs {tuple(expected_shape)}"
100
+ )
101
+
102
+ # Expand scales along each block dimension for broadcasting.
103
+ for dim, block_size in block_sizes.items():
104
+ expanded_scales = expanded_scales.repeat_interleave(block_size, dim=dim)
105
+
106
+ # Perform quantization using FP8 (E4M3) format.
107
+ quantized_data = (input / expanded_scales).to(torch.float8_e4m3fn)
108
+
109
+ # Crop quantized_data back to the original shape (if padding was added).
110
+ slices = tuple(slice(0, dim) for dim in original_input.shape)
111
+ quantized_data_cropped = quantized_data[slices]
112
+
113
+ return cls(original_input.shape, original_input.dtype, quantized_data_cropped), scales
114
+
115
+ def dequantize(self, dtype: torch.dtype = None, **kwarg):
116
+ """Dequantze FP8 packed tensor to a target dtype."""
117
+ if dtype is None:
118
+ dtype = self.metadata["dtype"]
119
+ assert "scale" in kwarg, "Require scale for FP8 dequantization."
120
+
121
+ # get args
122
+ scales = kwarg["scale"]
123
+ block_sizes = kwarg.get("block_sizes")
124
+
125
+ quantized_data = self._quantized_data
126
+ if block_sizes:
127
+ # pad the weight if needed
128
+ quantized_data = reduce_block_padding(quantized_data, block_sizes)
129
+ shape = quantized_data.shape
130
+
131
+ # Compute expanded shape for broadcasting scales
132
+ expanded_shape = list(shape)
133
+ for dim, block_size in block_sizes.items():
134
+ assert shape[dim] % block_size == 0, (
135
+ f"Dimension {shape[dim]} is not divisible by {block_size}."
136
+ )
137
+ expanded_shape[dim] //= block_size # Reduce the dimension size for blocks
138
+
139
+ assert tuple(expanded_shape) == scales.shape, (
140
+ f"Scales shape {scales.shape} must match expected {tuple(expanded_shape)}."
141
+ )
142
+
143
+ # Expand scales for broadcasting
144
+ for dim, block_size in block_sizes.items():
145
+ scales = scales.repeat_interleave(block_size, dim=dim)
146
+
147
+ # handle padded tensors
148
+ slices = tuple(slice(0, dim) for dim in self.metadata["shape"])
149
+ scales = scales.to(self._quantized_data.device)
150
+
151
+ return (quantized_data.to(dtype) * scales.to(dtype))[slices]
@@ -0,0 +1,130 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Implements INT4 quantization for efficient tensor storage and computation."""
17
+
18
+ import numpy as np
19
+ import torch
20
+
21
+ from ..extensions import get_cuda_ext
22
+ from ..qtensor.base_qtensor import BaseQuantizedTensor
23
+ from ..utils import reduce_amax, reduce_block_padding
24
+
25
+ __all__ = ["INT4QTensor"]
26
+
27
+
28
+ class INT4QTensor(BaseQuantizedTensor):
29
+ """Implements the INT4 quantization on tensors for more efficient storage or computation.
30
+
31
+ Attributes:
32
+ quantized_data (torch.Tensor): The quantized data stored as a packed uint8 tensor.
33
+ """
34
+
35
+ @staticmethod
36
+ def _get_quant_maxbound(num_bits):
37
+ return 2 ** (num_bits - 1) - 1
38
+
39
+ @classmethod
40
+ def quantize(cls, input: torch.Tensor, block_size: int) -> tuple:
41
+ """Converting a tensor to a quantized format based on INT4 (AWQ) quantization.
42
+
43
+ Args:
44
+ input (torch.Tensor): The input tensor to be quantized.
45
+ block_size (int): The size of each block for quantization.
46
+
47
+ Returns:
48
+ tuple: Contains quantized data, input quantization config, and scale quantization config.
49
+ """
50
+ cuda_ext = get_cuda_ext()
51
+
52
+ scale_quant_maxbound = cls._get_quant_maxbound(num_bits=4)
53
+
54
+ assert input.shape[-1] % 2 == 0, "Input tensor must have even number on last dimension."
55
+
56
+ # pad the input if needed
57
+ original_input = input
58
+ input = reduce_block_padding(input.view(-1), block_sizes={-1: block_size})
59
+
60
+ # get scales for each block
61
+ block_input = input.view(-1, block_size)
62
+ scales = scale_quant_maxbound / reduce_amax(block_input, -1)
63
+ # expand scalers to match shape of input
64
+ scales = scales.view(block_input.shape[0], -1) # shape: (block_input.shape[0], 1)
65
+
66
+ if cuda_ext and input.is_cuda:
67
+ # use a custom cuda kernel if available
68
+ packed_output_uint8 = cuda_ext.INT4_quantize(input, scales, block_size)
69
+ else:
70
+ scaled_blocks = block_input * scales
71
+ flattened = scaled_blocks.flatten()
72
+ # uint4: 0 - 15
73
+ flattened = flattened.round().clamp(
74
+ -(scale_quant_maxbound + 1), scale_quant_maxbound
75
+ ) + (scale_quant_maxbound + 1)
76
+ flattened = flattened.to(torch.uint8)
77
+
78
+ packed_output_uint8 = torch.empty(
79
+ input.numel() // 2, dtype=torch.uint8, device=input.device
80
+ )
81
+ # pack the int4 weights into a uint8 tensor
82
+ # packing format: w0, w1, w2, w3, w4, w5, ...
83
+ # | byte | byte | byte |
84
+ packed_output_uint8 = flattened[::2] << 4 | flattened[1::2]
85
+
86
+ # reshape the packed_output_uint8 to the original dimensions for sharding
87
+ packed_output_uint8 = packed_output_uint8.reshape(*original_input.shape[:-1], -1)
88
+ return cls(original_input.shape, input.dtype, packed_output_uint8), scales
89
+
90
+ def dequantize(self, dtype: torch.dtype = None, **kwarg):
91
+ """Dequantze INT4 packed tensor to a target dtype."""
92
+ if dtype is None:
93
+ dtype = self.metadata["dtype"]
94
+ cuda_ext = get_cuda_ext()
95
+
96
+ # get kwargs
97
+ scales = kwarg["scale"]
98
+ block_sizes = kwarg["block_sizes"]
99
+ scale_quant_maxbound = self._get_quant_maxbound(num_bits=4)
100
+
101
+ if cuda_ext and self._quantized_data.is_cuda:
102
+ # use a custom cuda kernel if available
103
+ scales = scales.to(self._quantized_data.device)
104
+ output = cuda_ext.INT4_dequantize(
105
+ self._quantized_data.view(-1), scales, block_sizes[-1]
106
+ )
107
+ return (
108
+ output.view(-1)[: np.prod(self.metadata["shape"])] # handle padding
109
+ .view(self.metadata["shape"])
110
+ .to(dtype)
111
+ )
112
+ else:
113
+ # indexing in torch required long dtype, we may need to optimize this with customized kernels
114
+ # convert (0, 15) -> (-8, 7)
115
+ first_half = (self._quantized_data >> 4).to(torch.long) - (scale_quant_maxbound + 1)
116
+ second_half = (self._quantized_data & 0x0F).to(torch.long) - (scale_quant_maxbound + 1)
117
+
118
+ # de-quantize tensor
119
+ first_half = first_half.view(-1, block_sizes[-1] // 2) / scales.view(-1, 1)
120
+ second_half = second_half.view(-1, block_sizes[-1] // 2) / scales.view(-1, 1)
121
+
122
+ # merge the interleaving elements
123
+ first_half = first_half.flatten().unsqueeze(-1).transpose(0, 1)
124
+ second_half = second_half.flatten().unsqueeze(-1).transpose(0, 1)
125
+ return (
126
+ torch.stack([first_half, second_half], dim=-1)
127
+ .view(-1)[: np.prod(self.metadata["shape"])] # handle padding
128
+ .reshape(self.metadata["shape"])
129
+ .to(dtype)
130
+ )
@@ -0,0 +1,124 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Implements INT8 quantization for efficient tensor storage and computation."""
16
+
17
+ import torch
18
+
19
+ from ..qtensor.base_qtensor import BaseQuantizedTensor
20
+ from ..utils import (
21
+ convert_quantization_axis_to_reduce_axis,
22
+ reduce_amax,
23
+ reduce_block_amax,
24
+ reduce_block_padding,
25
+ )
26
+
27
+
28
+ class INT8QTensor(BaseQuantizedTensor):
29
+ """Implements the INT8 quantization on tensors for more efficient storage or computation.
30
+
31
+ Attributes:
32
+ quantized_data (torch.Tensor): The quantized data stored as an INT8 tensor.
33
+ """
34
+
35
+ @classmethod
36
+ def quantize(
37
+ cls,
38
+ input: torch.Tensor,
39
+ scales: torch.Tensor | None = None,
40
+ axis: tuple | int | None = None,
41
+ block_sizes: dict | None = None,
42
+ ) -> tuple:
43
+ """Converting a tensor to a quantized format based on INT8 quantization.
44
+
45
+ Args:
46
+ input (torch.Tensor): The input tensor to be quantized.
47
+ scales (torch.Tensor): The scales for quantization.
48
+ axis: The dimensions to reduce for quantization. None or int or tuple of ints.
49
+ block_sizes (dict): A dictionary specifying the block size for each dimension.
50
+ Note: One can only provide axis or block_sizes for INT8 quantization.
51
+
52
+ Returns:
53
+ tuple: INT8QTensor, scales
54
+ """
55
+ original_input = input
56
+ if scales is None:
57
+ if block_sizes:
58
+ input = reduce_block_padding(input, block_sizes)
59
+ amax = reduce_block_amax(input, block_sizes)
60
+ else:
61
+ reduce_axis = convert_quantization_axis_to_reduce_axis(input, axis)
62
+ amax = reduce_amax(input, axis=reduce_axis)
63
+ scales = amax / 127.0
64
+
65
+ # Calculate the scale shape and make sure it aligns with input and block_sizes
66
+ expected_shape = list(input.shape)
67
+ expanded_scales = scales.clone()
68
+ if block_sizes:
69
+ for dim, block_size in block_sizes.items():
70
+ dim = dim if dim >= 0 else len(input.shape) + dim # Convert negative index
71
+ assert input.shape[dim] % block_size == 0, (
72
+ f"Tensor dimension {dim}, {input.shape[dim]} is not divisible by {block_size}."
73
+ )
74
+ expected_shape[dim] = (
75
+ input.shape[dim] // block_size
76
+ ) # Adjust expected shape for blocks
77
+
78
+ # Assert the shape of `scales` matches expected reduced dimensions
79
+ assert scales.shape == tuple(expected_shape), (
80
+ f"Mismatch in expected scale shape: {scales.shape} vs {tuple(expected_shape)}"
81
+ )
82
+
83
+ # Expand scales for broadcasting
84
+ for dim, block_size in block_sizes.items():
85
+ expanded_scales = expanded_scales.repeat_interleave(block_size, dim=dim)
86
+
87
+ # Quantization
88
+ quantized_data = (input / expanded_scales).round().clamp(-128, 127).to(torch.int8)
89
+
90
+ return cls(original_input.shape, original_input.dtype, quantized_data), scales
91
+
92
+ def dequantize(self, dtype: torch.dtype = None, **kwarg):
93
+ """Dequantize INT8 packed tensor to a target dtype."""
94
+ if dtype is None:
95
+ dtype = self.metadata["dtype"]
96
+ assert "scale" in kwarg, "Require scale for INT8 dequantization."
97
+
98
+ # Get args
99
+ scales = kwarg["scale"]
100
+ block_sizes = kwarg.get("block_sizes")
101
+
102
+ shape = self._quantized_data.shape
103
+ if block_sizes:
104
+ # Compute expanded shape for broadcasting scales
105
+ expanded_shape = list(shape)
106
+ for dim, block_size in block_sizes.items():
107
+ assert shape[dim] % block_size == 0, (
108
+ f"Dimension {shape[dim]} is not divisible by {block_size}."
109
+ )
110
+ expanded_shape[dim] //= block_size # Reduce the dimension size for blocks
111
+
112
+ assert tuple(expanded_shape) == scales.shape, (
113
+ f"Scales shape {scales.shape} must match expected {tuple(expanded_shape)}."
114
+ )
115
+
116
+ # Expand scales for broadcasting
117
+ for dim, block_size in block_sizes.items():
118
+ scales = scales.repeat_interleave(block_size, dim=dim)
119
+
120
+ # Handle padded tensors
121
+ slices = tuple(slice(0, dim) for dim in self.metadata["shape"])
122
+ scales = scales.to(self._quantized_data.device)
123
+
124
+ return (self._quantized_data.view(torch.int8).to(dtype) * scales.to(dtype))[slices]
@@ -0,0 +1,144 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Implements MXFP4 quantization for efficient tensor storage and computation."""
17
+
18
+ import torch
19
+
20
+ from ..qtensor.base_qtensor import BaseQuantizedTensor
21
+
22
+ __all__ = ["MXFP4QTensor"]
23
+
24
+
25
+ class MXFP4QTensor(BaseQuantizedTensor):
26
+ """Implements the MXFP4 quantization on tensors for more efficient storage or computation.
27
+
28
+ Attributes:
29
+ quantized_data (torch.Tensor): The quantized data stored as a packed fp8 tensor.
30
+ """
31
+
32
+ E2M1_max = 6.0
33
+
34
+ E2M1_values = [0, 0.5, 1, 1.5, 2, 3, 4, 6]
35
+ E2M1_bounds = torch.tensor([0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5])
36
+
37
+ @classmethod
38
+ def quantize(cls, input: torch.Tensor, block_size: int | None) -> tuple:
39
+ """Converting a tensor to a quantized format based on MXFP4 quantization. Only E4M3 is supported.
40
+
41
+ Args:
42
+ input (torch.Tensor): The input tensor to be quantized.
43
+ block_sizes (dict | None): The block sizes for quantization.
44
+ """
45
+
46
+ def cast_fp4(x):
47
+ sign = torch.sign(x)
48
+ sign_bit = (2 - sign) // 2
49
+ ord_ = torch.sum(
50
+ (x.abs().unsqueeze(-1) - MXFP4QTensor.E2M1_bounds.to(x.device)) > 0, dim=-1
51
+ )
52
+ fp4_val = (sign_bit * 0b1000 + ord_).to(torch.uint8)
53
+ return fp4_val
54
+
55
+ def fuse_uint4_to_uint8(x):
56
+ # If the last dimension is odd, pad with zeros
57
+ # If this behavior is not desired, please modify the code accordingly
58
+ left_side = x[..., 0::2] # Even indices (0, 2, 4...)
59
+ right_side = x[..., 1::2] # Odd indices (1, 3, 5...)
60
+ new_data = right_side.clone() << 4 # Put odd indices (higher addresses) in high bits
61
+ new_data[..., : left_side.shape[-1]] += left_side # Put even indices in low bits
62
+ return new_data
63
+
64
+ if block_size is None:
65
+ block_size = 32
66
+
67
+ original_shape = input.shape
68
+ original_dtype = input.dtype
69
+ input = input.view(-1, block_size)
70
+ # get scales
71
+ # Casting scales to float yields better match with fakequant kernel (needs further investigation)
72
+ input_amax = input.float().abs().max(dim=-1, keepdim=True).values
73
+ descale = input_amax / cls.E2M1_max
74
+ min_value = torch.tensor(-127.0, device=descale.device)
75
+ e8m0_scale = torch.ceil(torch.maximum(torch.log2(descale), min_value))
76
+
77
+ input = (input / torch.exp2(e8m0_scale)).view(original_shape)
78
+ input_q = cast_fp4(input)
79
+ input_q = fuse_uint4_to_uint8(input_q)
80
+ e8m0_scale = (e8m0_scale + 127).to(torch.uint8)
81
+ return cls(original_shape, original_dtype, input_q), e8m0_scale
82
+
83
+ def dequantize(self, dtype: torch.dtype = None, **kwarg):
84
+ """Dequantze MXFP4 packed tensor to a target dtype."""
85
+
86
+ def unfuse_uint8_to_uint4(x):
87
+ """Unfuse uint8 values back to uint4 values.
88
+
89
+ This is the inverse operation of fuse_uint4_to_uint8.
90
+ """
91
+ # Extract the lower 4 bits (even indices)
92
+ left_side = x & 0x0F
93
+
94
+ # Extract the upper 4 bits (odd indices)
95
+ right_side = (x >> 4) & 0x0F
96
+
97
+ # Create a new tensor with alternating values
98
+ shape = list(x.shape)
99
+ shape[-1] = shape[-1] * 2
100
+ result = torch.zeros(shape, dtype=torch.uint8, device=x.device)
101
+
102
+ # Fill in the values - even indices get low bits, odd indices get high bits
103
+ result[..., 0::2] = left_side # Even indices from low bits
104
+ result[..., 1::2] = right_side # Odd indices from high bits
105
+
106
+ return result
107
+
108
+ e8m0_scale = kwarg["scale"]
109
+ block_size = kwarg["block_sizes"][-1]
110
+ if dtype is None:
111
+ dtype = self.metadata["dtype"]
112
+
113
+ # Unfuse the uint8 values back to uint4
114
+ x_unfused = unfuse_uint8_to_uint4(self._quantized_data)
115
+ # Extract sign and magnitude
116
+ sign = 1 - 2 * ((x_unfused & 0b1000) >> 3).to(
117
+ torch.float32
118
+ ) # Extract sign bit and convert to +1/-1
119
+ magnitude = x_unfused & 0b0111 # Extract magnitude bits
120
+ magnitude = magnitude.to(torch.long)
121
+
122
+ # Create a tensor with the E2M1 values
123
+ values = torch.tensor(MXFP4QTensor.E2M1_values, device=self._quantized_data.device)
124
+
125
+ # Use gather to index the values tensor properly
126
+ # We need to reshape magnitude to match the dimensions we want to gather along
127
+ original_shape = magnitude.shape
128
+ x_float = values[magnitude.reshape(-1)].reshape(original_shape)
129
+
130
+ # Apply sign and scale
131
+ x_float = sign.float() * x_float
132
+
133
+ # Reshape to apply block-wise scaling
134
+ x_float = x_float.reshape(-1, block_size)
135
+
136
+ # Apply the E8M0 scale
137
+ scale_factor = torch.exp2(e8m0_scale.float() - 127)
138
+ scale_factor = scale_factor.reshape(-1, 1) # Reshape for proper broadcasting
139
+
140
+ # Apply scaling and reshape back to original shape
141
+ x_float = x_float * scale_factor
142
+
143
+ # Reshape back to the original shape
144
+ return x_float.reshape(original_shape).to(dtype)