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,816 @@
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
+ """Basic tensor quantization functions."""
17
+
18
+ import warnings
19
+
20
+ import torch
21
+ from torch.autograd import Function
22
+ from torch.onnx import symbolic_helper
23
+
24
+ import modelopt.torch.quantization.triton as triton_kernel
25
+
26
+ from .config import QuantizerAttributeConfig
27
+ from .extensions import get_cuda_ext, get_cuda_ext_fp8, get_cuda_ext_mx
28
+
29
+ mx_format_map = {
30
+ (4, 3): "E4M3",
31
+ (5, 2): "E5M2",
32
+ (3, 2): "E3M2",
33
+ (2, 3): "E2M3",
34
+ 8: "INT8",
35
+ (8, 0): "E8M0",
36
+ (2, 1): "E2M1",
37
+ (1, 2): "E1M2",
38
+ (0, 3): "E0M3",
39
+ (3, 0): "E3M0",
40
+ }
41
+
42
+ DISABLE_TRITON_KERNEL = False
43
+
44
+
45
+ def scaled_e4m3_impl(
46
+ inputs: torch.Tensor, # TODO: check support for multiple inputs
47
+ amax: torch.Tensor,
48
+ disable_fused_kernel=True,
49
+ ) -> torch.Tensor:
50
+ """Implementation of fake quantizing input to FP8.
51
+
52
+ Args:
53
+ inputs: Torch tensor.
54
+ amax: Absolute max range of the input tensor.
55
+
56
+ Returns:
57
+ Input tensors faked quantized to FP8.
58
+ """
59
+ cuda_ext_fp8 = get_cuda_ext_fp8(raise_if_failed=True)
60
+
61
+ def is_fusable():
62
+ # ignore no scaling and shape([]) cases
63
+ if amax is None or len(amax.shape) == 0:
64
+ return False
65
+ else:
66
+ # can't have amax.shape = [1, 1, 4, 1] and the like
67
+ amax_last_dim_only = amax.numel() == amax.shape[-1]
68
+ # must be cuda
69
+ all_cuda = inputs.is_cuda and amax.is_cuda
70
+
71
+ # also check explicit disable.
72
+ return amax_last_dim_only and all_cuda and (not disable_fused_kernel)
73
+
74
+ with torch.cuda.device(
75
+ None if inputs.device.index == torch.cuda.current_device() else inputs.device.index
76
+ ):
77
+ # differentiate between fused & unfused cases
78
+ if is_fusable():
79
+ zero_threshold = 1.0 / (1 << 24)
80
+ outputs = cuda_ext_fp8.fused_fake_e4m3fy(inputs, amax.float(), zero_threshold)
81
+ else:
82
+ zero_mask = inputs.abs() < 1.0 / (1 << 24)
83
+
84
+ if amax is None:
85
+ outputs = cuda_ext_fp8.fake_e4m3fy(inputs)
86
+ else:
87
+ scale = 448.0 / amax
88
+ outputs = cuda_ext_fp8.fake_e4m3fy(inputs * scale) / scale
89
+
90
+ # Zero out values that are tiny.
91
+ # Tiny values could lead to tiny amax and then large scale which cause overflow/saturation
92
+ # and won't go back to normal value after dividing by scale. The right behavior is to mark them
93
+ # as zero which also get rid of inf/nan
94
+ outputs[zero_mask] = 0.0
95
+
96
+ return outputs
97
+
98
+
99
+ def fake_quant_impl(
100
+ inputs: torch.Tensor,
101
+ amax: torch.Tensor,
102
+ num_bits=8,
103
+ unsigned=False,
104
+ narrow_range=True,
105
+ ):
106
+ """Implementation of fake quantizing input according to number of bits."""
107
+ cuda_ext = get_cuda_ext()
108
+
109
+ with torch.cuda.device(
110
+ None if inputs.device.index == torch.cuda.current_device() else inputs.device.index
111
+ ):
112
+ if amax.numel() == 1:
113
+ outputs = cuda_ext.fake_tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
114
+ else:
115
+ axis = amax.shape.index(amax.numel())
116
+ outputs = cuda_ext.fake_tensor_quant_with_axis(
117
+ inputs, amax.squeeze(), axis, num_bits, unsigned, narrow_range
118
+ )
119
+ return outputs
120
+
121
+
122
+ def _quantize_impl(
123
+ inputs: torch.Tensor,
124
+ amax: torch.Tensor,
125
+ num_bits: int = 8,
126
+ exponent_bits: int = 0,
127
+ unsigned: bool = False,
128
+ narrow_range: bool = True,
129
+ ):
130
+ if num_bits == 8 and exponent_bits == 4:
131
+ return scaled_e4m3_impl(inputs=inputs, amax=amax)
132
+ elif isinstance(num_bits, int):
133
+ return fake_quant_impl(
134
+ inputs=inputs,
135
+ amax=amax,
136
+ num_bits=num_bits,
137
+ unsigned=unsigned,
138
+ narrow_range=narrow_range,
139
+ )
140
+ else:
141
+ raise ValueError(
142
+ f"Invalid combination of (num_bits, exponent_bits): ({num_bits}, {exponent_bits})."
143
+ )
144
+
145
+
146
+ def _quantize_impl_abstract(
147
+ input: torch.Tensor,
148
+ amax: torch.Tensor,
149
+ num_bits: int = 8,
150
+ exponent_bits: int = 0,
151
+ unsigned: bool = False,
152
+ narrow_range: bool = True,
153
+ ) -> torch.Tensor:
154
+ """Register an abstract implementation for quantizing tensor.
155
+
156
+ This abstract function returns an empty tensor with the same shape and dtype.
157
+ """
158
+ output = torch.empty_like(input)
159
+
160
+ return output
161
+
162
+
163
+ # Argument types: Tensor, int, NoneType, int, int, int, int,
164
+ def _dynamic_block_quantize_impl(
165
+ inputs: torch.Tensor,
166
+ block_size: int,
167
+ amax: torch.Tensor,
168
+ num_bits: int,
169
+ exponent_bits: int,
170
+ scale_num_bits: int,
171
+ scale_exponent_bits: int,
172
+ ):
173
+ scale_bits = (scale_exponent_bits, scale_num_bits - scale_exponent_bits - 1)
174
+ if exponent_bits != 0:
175
+ num_bits = (exponent_bits, num_bits - exponent_bits - 1)
176
+ if num_bits in mx_format_map:
177
+ assert scale_bits in mx_format_map, f"Scale bits should be in {mx_format_map.keys()}"
178
+ if scale_bits != (8, 0):
179
+ assert amax.is_cuda, "amax must be a CUDA tensor for dynamic block quantization."
180
+ if amax.numel() != 1:
181
+ amax = amax.amax()
182
+ with torch.cuda.device(
183
+ None if inputs.device.index == torch.cuda.current_device() else inputs.device.index
184
+ ):
185
+ if (
186
+ num_bits == (2, 1) # type: ignore[comparison-overlap]
187
+ and scale_bits == (4, 3)
188
+ and triton_kernel.IS_AVAILABLE
189
+ and not DISABLE_TRITON_KERNEL
190
+ and amax is not None
191
+ ):
192
+ return triton_kernel.fp4_fake_quant_block(inputs, amax)
193
+ cuda_ext_mx = get_cuda_ext_mx(raise_if_failed=True)
194
+ return cuda_ext_mx.fused_amax_convert(
195
+ inputs,
196
+ block_size,
197
+ getattr(cuda_ext_mx.Types, mx_format_map[num_bits]),
198
+ getattr(cuda_ext_mx.Types, mx_format_map[scale_bits]),
199
+ amax,
200
+ )
201
+ else:
202
+ raise NotImplementedError(
203
+ f"Unsupported num_bits: {num_bits}, scale_bits: {scale_bits} for dynamic block quantization."
204
+ )
205
+
206
+
207
+ def _dynamic_block_quantize_impl_abstract(
208
+ inputs: torch.Tensor,
209
+ block_size: int,
210
+ amax: torch.Tensor,
211
+ num_bits: int,
212
+ exponent_bits: int,
213
+ scale_num_bits: int,
214
+ scale_exponent_bits: int,
215
+ ):
216
+ """Register an abstract implementation for dynamic block quantization.
217
+
218
+ This abstract function returns an empty tensor with the same shape and dtype.
219
+ """
220
+ output = torch.empty_like(inputs)
221
+
222
+ return output
223
+
224
+
225
+ quantize_op = _quantize_impl
226
+ dynamic_block_quantize_op = _dynamic_block_quantize_impl
227
+ # Define custom operators via torch.library if supported:
228
+ # 1. quantize_op: Applies static quantization to the input tensor using a specified amax.
229
+ # 2. dynamic_block_quantize_op: Performs blockwise double quantization with dynamically
230
+ # determined scales, governed by the given quantization format (scale_num_bits, scale_exponent_bits).
231
+ try:
232
+ torch.library.define(
233
+ "tensorrt::quantize_op",
234
+ "(Tensor input, Tensor amax, int num_bits, int exponent_bits, "
235
+ "bool unsigned, bool narrow_range) -> Tensor",
236
+ )
237
+ torch.library.define(
238
+ "tensorrt::dynamic_block_quantize_op",
239
+ "(Tensor input, int block_size, Tensor amax, int num_bits, int exponent_bits, "
240
+ "int scale_num_bits, int scale_exponent_bits) -> Tensor",
241
+ )
242
+ torch.library.define(
243
+ "tensorrt::dynamic_block_quantize_op.overload",
244
+ "(Tensor input, int block_size, None amax, int num_bits, int exponent_bits, "
245
+ "int scale_num_bits, int scale_exponent_bits) -> Tensor",
246
+ )
247
+
248
+ # Implement the None amax case
249
+ def _dynamic_block_quantize_impl_none_amax(
250
+ inputs: torch.Tensor,
251
+ block_size: int,
252
+ amax: None,
253
+ num_bits: int,
254
+ exponent_bits: int,
255
+ scale_num_bits: int,
256
+ scale_exponent_bits: int,
257
+ ):
258
+ return torch.empty_like(inputs)
259
+
260
+ # Register the implementation for both CPU and CUDA
261
+ torch.library.impl("tensorrt::quantize_op", ["cpu", "cuda"])(_quantize_impl)
262
+ torch.library.impl("tensorrt::dynamic_block_quantize_op", ["cpu", "cuda"])(
263
+ _dynamic_block_quantize_impl
264
+ )
265
+ torch.library.impl("tensorrt::dynamic_block_quantize_op.overload", ["cpu", "cuda"])(
266
+ _dynamic_block_quantize_impl_none_amax
267
+ )
268
+
269
+ # Register the fake implementation
270
+ torch.library.register_fake("tensorrt::quantize_op")(_quantize_impl_abstract)
271
+ torch.library.register_fake("tensorrt::dynamic_block_quantize_op")(
272
+ _dynamic_block_quantize_impl_abstract
273
+ )
274
+ torch.library.register_fake("tensorrt::dynamic_block_quantize_op.overload")(
275
+ _dynamic_block_quantize_impl_abstract
276
+ )
277
+
278
+ quantize_op = torch.ops.tensorrt.quantize_op
279
+ dynamic_block_quantize_op = torch.ops.tensorrt.dynamic_block_quantize_op
280
+ except (AttributeError, RuntimeError) as e:
281
+ # torch.library is an experimental feature, the function signatures may change overtime.
282
+ warnings.warn(
283
+ "Unable to register operators with torch.library. Exporting quantized models with"
284
+ f" torch.export will not be supported.\n{e}"
285
+ )
286
+
287
+ # Predefined descriptors
288
+ QUANT_DESC_8BIT_PER_TENSOR = QuantizerAttributeConfig(num_bits=8)
289
+ QUANT_DESC_UNSIGNED_8BIT_PER_TENSOR = QuantizerAttributeConfig(num_bits=8, unsigned=True)
290
+ QUANT_DESC_8BIT_CONV1D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
291
+ QUANT_DESC_8BIT_CONV2D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
292
+ QUANT_DESC_8BIT_CONV3D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
293
+ QUANT_DESC_8BIT_LINEAR_WEIGHT_PER_ROW = QuantizerAttributeConfig(num_bits=8, axis=(0))
294
+ QUANT_DESC_8BIT_CONVTRANSPOSE1D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
295
+ QUANT_DESC_8BIT_CONVTRANSPOSE2D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
296
+ QUANT_DESC_8BIT_CONVTRANSPOSE3D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
297
+
298
+
299
+ @torch.jit.script
300
+ def _fake_tensor_quant_backward(inputs, amax: torch.Tensor | None, grad_outputs):
301
+ # Skip clip for MX formats
302
+ if amax is None:
303
+ return grad_outputs
304
+
305
+ zero = grad_outputs.new_zeros(1)
306
+ grad_inputs = torch.where(inputs.abs() <= amax, grad_outputs, zero)
307
+ return grad_inputs
308
+
309
+
310
+ def _fake_quant_backward_function(ctx, grad_outputs, num_args=1):
311
+ saved_tensors = ctx.saved_tensors
312
+ if len(saved_tensors) == 0:
313
+ return (grad_outputs,) + (None,) * (num_args - 1)
314
+ inputs, amax = saved_tensors
315
+ return (_fake_tensor_quant_backward(inputs, amax, grad_outputs),) + (None,) * (num_args - 1)
316
+
317
+
318
+ def _save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax):
319
+ if not pass_through_bwd and amax is not None:
320
+ amax = (
321
+ amax
322
+ if isinstance(amax, torch.Tensor)
323
+ else torch.tensor(amax, device=inputs.device, dtype=inputs.dtype)
324
+ )
325
+ ctx.save_for_backward(inputs, amax)
326
+
327
+
328
+ class FakeTensorQuantFunction(Function):
329
+ """Fake version of TensorQuantFunction use CUDA extension."""
330
+
331
+ @staticmethod
332
+ @symbolic_helper.parse_args("v", "t", "t", "i", "b", "b", "s", "b", "i", "i")
333
+ def symbolic(
334
+ g,
335
+ inputs,
336
+ amax,
337
+ bias=None,
338
+ num_bits=8,
339
+ unsigned=False,
340
+ narrow_range=True,
341
+ trt_high_precision_dtype=None,
342
+ pass_through_bwd=False,
343
+ block_size=None,
344
+ axis=None,
345
+ ):
346
+ """ONNX symbolic function."""
347
+ from .export_onnx import export_int4, export_int8
348
+
349
+ if num_bits == 4:
350
+ return export_int4(
351
+ g, inputs, amax, num_bits, trt_high_precision_dtype, block_size, axis
352
+ )
353
+
354
+ return export_int8(
355
+ g, inputs, amax, num_bits, unsigned, narrow_range, trt_high_precision_dtype
356
+ )
357
+
358
+ @staticmethod
359
+ def forward(
360
+ ctx,
361
+ inputs,
362
+ amax,
363
+ bias=None,
364
+ num_bits=8,
365
+ unsigned=False,
366
+ narrow_range=True,
367
+ trt_high_precision_dtype=None,
368
+ pass_through_bwd=False,
369
+ block_size=None,
370
+ axis=None,
371
+ ):
372
+ """Forward method."""
373
+ if bias is not None:
374
+ inputs = inputs - bias
375
+
376
+ _save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax)
377
+
378
+ def legacy_quant_func():
379
+ # The LegacyFakeTensorQuantFunction support cpu and amax with any shape that can be broadcasted to inputs.
380
+ outputs, scale = _tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
381
+ return outputs / scale.to(inputs.dtype)
382
+
383
+ if not inputs.is_cuda:
384
+ outputs = legacy_quant_func()
385
+ else:
386
+ try:
387
+ outputs = quantize_op(
388
+ inputs,
389
+ amax,
390
+ num_bits=num_bits,
391
+ exponent_bits=0,
392
+ unsigned=unsigned,
393
+ narrow_range=narrow_range,
394
+ )
395
+ except (AttributeError, ValueError):
396
+ # AttributeError: cuda_ext is not imported, possibly due to CPU only installation
397
+ # ValueError: cuda_ext is installed, but trying to perform multidimensional quantization (amax dim > 1)
398
+ outputs = legacy_quant_func()
399
+
400
+ if bias is not None:
401
+ outputs = outputs + bias
402
+
403
+ return outputs
404
+
405
+ @staticmethod
406
+ def backward(ctx, grad_outputs):
407
+ """Implements straight through estimation with clipping."""
408
+ return _fake_quant_backward_function(ctx, grad_outputs, num_args=10)
409
+
410
+
411
+ class ScaledE4M3Function(Function):
412
+ """E4M3fy input with scale."""
413
+
414
+ @staticmethod
415
+ @symbolic_helper.parse_args("v", "t", "t", "i", "i", "s", "b")
416
+ def symbolic(
417
+ g,
418
+ inputs,
419
+ amax=None,
420
+ bias=None,
421
+ E=4, # noqa: N803
422
+ M=3, # noqa: N803
423
+ trt_high_precision_dtype=None,
424
+ pass_through_bwd=False,
425
+ ):
426
+ """ONNX symbolic function."""
427
+ from .export_onnx import export_fp8
428
+
429
+ return export_fp8(g, inputs, amax, trt_high_precision_dtype)
430
+
431
+ @staticmethod
432
+ # Default values could cause errors from TorchDynamo during torch.export
433
+ def forward(
434
+ ctx,
435
+ inputs,
436
+ amax,
437
+ bias,
438
+ E, # noqa: N803
439
+ M, # noqa: N803
440
+ trt_high_precision_dtype=None,
441
+ pass_through_bwd=False,
442
+ ):
443
+ """Forward method."""
444
+ if E != 4 or M != 3:
445
+ raise NotImplementedError("Only support E=4 & M=3 for now.")
446
+
447
+ if bias is not None:
448
+ inputs = inputs - bias
449
+
450
+ _save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax)
451
+
452
+ outputs = quantize_op(
453
+ inputs,
454
+ amax,
455
+ num_bits=8,
456
+ exponent_bits=4,
457
+ unsigned=False,
458
+ narrow_range=False,
459
+ )
460
+
461
+ if bias is not None:
462
+ outputs = outputs + bias
463
+
464
+ return outputs
465
+
466
+ @staticmethod
467
+ def backward(ctx, grad_outputs):
468
+ """Implements straight through estimation with clipping."""
469
+ return _fake_quant_backward_function(ctx, grad_outputs, num_args=7)
470
+
471
+
472
+ def _dynamic_block_quantize_forward(
473
+ ctx,
474
+ inputs,
475
+ block_size,
476
+ amax,
477
+ num_bits,
478
+ scale_bits,
479
+ trt_high_precision_dtype=None,
480
+ onnx_quantizer_type="dynamic",
481
+ pass_through_bwd=True,
482
+ ):
483
+ """Forward method."""
484
+ if isinstance(num_bits, int):
485
+ # special case for INT dynamic block quantization, e.g. MXINT8
486
+ exponent_bits = 0
487
+ else:
488
+ assert isinstance(num_bits, tuple) and len(num_bits) == 2
489
+ exponent_bits = num_bits[0]
490
+ num_bits = num_bits[0] + num_bits[1] + 1
491
+ assert isinstance(scale_bits, tuple) and len(scale_bits) == 2
492
+ scale_exponent_bits = scale_bits[0]
493
+ scale_num_bits = scale_bits[0] + scale_bits[1] + 1
494
+ outputs = dynamic_block_quantize_op(
495
+ inputs,
496
+ block_size,
497
+ amax,
498
+ num_bits,
499
+ exponent_bits,
500
+ scale_num_bits,
501
+ scale_exponent_bits,
502
+ )
503
+ return outputs
504
+
505
+
506
+ class DynamicBlockQuantizationFunction(Function):
507
+ """Dynamic block quantization functional."""
508
+
509
+ @staticmethod
510
+ @symbolic_helper.parse_args("v", "i", "v", "t", "is", "is", "s", "s", "b")
511
+ def symbolic(
512
+ g,
513
+ inputs,
514
+ block_size,
515
+ amax,
516
+ bias,
517
+ num_bits,
518
+ scale_bits,
519
+ trt_high_precision_dtype=None,
520
+ onnx_quantizer_type="dynamic",
521
+ pass_through_bwd=True,
522
+ ):
523
+ """ONNX symbolic function."""
524
+ from .export_onnx import export_fp4, export_mxfp8
525
+
526
+ if num_bits == (2, 1) and scale_bits == (4, 3):
527
+ return export_fp4(
528
+ g,
529
+ inputs,
530
+ block_size,
531
+ amax,
532
+ num_bits,
533
+ trt_high_precision_dtype,
534
+ onnx_quantizer_type,
535
+ )
536
+ if num_bits == (4, 3) and scale_bits == (8, 0):
537
+ return export_mxfp8(
538
+ g,
539
+ inputs,
540
+ onnx_quantizer_type,
541
+ block_size,
542
+ )
543
+ raise NotImplementedError(
544
+ f"Unsupported num_bits: {num_bits} and scale_bits: {scale_bits} for ONNX export."
545
+ )
546
+
547
+ @staticmethod
548
+ def forward(
549
+ ctx,
550
+ inputs,
551
+ block_size,
552
+ amax,
553
+ bias,
554
+ num_bits,
555
+ scale_bits,
556
+ trt_high_precision_dtype=None,
557
+ onnx_quantizer_type="dynamic",
558
+ pass_through_bwd=True,
559
+ ):
560
+ """Forward method."""
561
+ _save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax)
562
+ return _dynamic_block_quantize_forward(
563
+ ctx,
564
+ inputs,
565
+ block_size,
566
+ amax,
567
+ num_bits,
568
+ scale_bits,
569
+ trt_high_precision_dtype,
570
+ onnx_quantizer_type,
571
+ pass_through_bwd,
572
+ )
573
+
574
+ @staticmethod
575
+ def backward(ctx, grad_outputs):
576
+ """Implements straight through estimation with clipping."""
577
+ return _fake_quant_backward_function(ctx, grad_outputs, num_args=9)
578
+
579
+
580
+ class TensorQuantFunction(Function):
581
+ """A universal tensor quantization function.
582
+
583
+ Take an input tensor, output an quantized tensor. The granularity of scale can be interpreted from the
584
+ shape of amax.
585
+ output_dtype indicates whether the quantized value will be stored in integer or float. The reason we want to store
586
+ it in float is the pytorch function takes the quantized value may not accept integer input, e.g. Conv2D.
587
+
588
+ It uses 2^num_bits -1 values instead of 2^num_bits. e.g., for num_bits=8, it uses [-127, 127] instead of [-128, 127]
589
+ """
590
+
591
+ @staticmethod
592
+ @symbolic_helper.parse_args("v", "t", "t", "i", "b", "b", "s")
593
+ def symbolic(
594
+ g,
595
+ inputs,
596
+ amax,
597
+ bias=None,
598
+ num_bits=8,
599
+ unsigned=False,
600
+ narrow_range=True,
601
+ trt_high_precision_dtype=None,
602
+ ):
603
+ """ONNX symbolic function."""
604
+ from .export_onnx import export_int8
605
+
606
+ return export_int8(
607
+ g, inputs, amax, num_bits, unsigned, narrow_range, trt_high_precision_dtype
608
+ )
609
+
610
+ @staticmethod
611
+ def forward(
612
+ ctx,
613
+ inputs,
614
+ amax,
615
+ bias=None,
616
+ num_bits=8,
617
+ unsigned=False,
618
+ narrow_range=True,
619
+ trt_high_precision_dtype=None,
620
+ ):
621
+ """Forward method.
622
+
623
+ Follow tensorflow convention, max value is passed in and used to decide scale, instead of inputting scale
624
+ directly. Though inputting scale directly may be more natural to use.
625
+
626
+ Args:
627
+ ctx: A Context object to store tensors for backward.
628
+ inputs: A Tensor of type float32.
629
+ amax: A Tensor of type float32. Inputs will be quantized within range [-amax, amax]
630
+ amax will be broadcasted to inputs tensor.
631
+ num_bits: A integer used to calculate scaling factor, scale = (2^(num_bits-1) - 1) / max
632
+ Effectively, it indicates how many integer bits is used to represent the value. Default 8.
633
+ output_dtype: A type of Tensor. torch.int32 or torch.float32.
634
+ unsigned: A boolean. Use unsigned integer range. E.g. [0, 255] for num_bits=8. Default False.
635
+ narrow_range: A boolean. Use symmetric integer range for signed quantization
636
+ E.g. [-127,127] instead of [-128,127] for num_bits=8. Default True.
637
+
638
+ Returns:
639
+ outputs: A Tensor of type output_dtype.
640
+ scale: A Tensor of type float32. outputs / scale will dequantize outputs tensor.
641
+
642
+ Raises:
643
+ ValueError:
644
+ """
645
+ if bias is not None:
646
+ inputs = inputs - bias
647
+
648
+ ctx.save_for_backward(inputs, amax)
649
+
650
+ outputs, scale = _tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
651
+ # Check if scale overflows FP16
652
+ if outputs.dtype == torch.half and scale.max() > 65504:
653
+ raise ValueError(f"scale is too large for FP16 with amax={amax}")
654
+
655
+ if bias is not None:
656
+ outputs = outputs + bias
657
+
658
+ return outputs, scale.to(inputs.dtype)
659
+
660
+ @staticmethod
661
+ def backward(ctx, grad_outputs, grad_scale):
662
+ """Implements straight through estimation with clipping.
663
+
664
+ For -amax <= input <= amax the gradient passes straight through, otherwise the gradient is zero.
665
+
666
+ Args:
667
+ ctx: A Context object with saved tensors from forward.
668
+ grad_outputs: A tensor of gradient of outputs.
669
+ grad_scale: A tensor of gradient of scale.
670
+
671
+ Returns:
672
+ grad_inputs: A tensor of gradient.
673
+ """
674
+ inputs, amax = ctx.saved_tensors
675
+ zero = grad_outputs.new_zeros(1) # create a zero tensor with the same type and device
676
+ grad_inputs = torch.where(inputs.abs() <= amax, grad_outputs, zero)
677
+ return grad_inputs, None, None, None, None, None, None
678
+
679
+
680
+ class LegacyFakeTensorQuantFunction(Function):
681
+ """Fake version of TensorQuantFunction.
682
+
683
+ See comments of TensorQuantFunction, arguments are the same.
684
+ """
685
+
686
+ @staticmethod
687
+ def forward(ctx, inputs, amax, bias, num_bits=8, unsigned=False, narrow_range=True):
688
+ """Forward method."""
689
+ if bias is not None:
690
+ inputs = inputs - bias
691
+
692
+ ctx.save_for_backward(inputs, amax)
693
+
694
+ outputs, scale = _tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
695
+
696
+ if bias is not None:
697
+ outputs = outputs + bias
698
+
699
+ return outputs / scale.to(inputs.dtype)
700
+
701
+ @staticmethod
702
+ def backward(ctx, grad_outputs):
703
+ """Implements straight through estimation."""
704
+ inputs, amax = ctx.saved_tensors
705
+ zero = grad_outputs.new_zeros(1)
706
+ grad_inputs = torch.where(inputs.abs() <= amax, grad_outputs, zero)
707
+ return grad_inputs, None, None, None, None, None
708
+
709
+
710
+ def _tensor_quant(inputs, amax, num_bits=8, unsigned=False, narrow_range=True):
711
+ """Shared function body between TensorQuantFunction and FakeTensorQuantFunction."""
712
+ # Fine scale, per channel scale will be handled by broadcasting, which could be tricky. Pop a warning.
713
+ if unsigned and inputs.min() < 0.0:
714
+ raise TypeError("Negative values encountered in unsigned quantization.")
715
+
716
+ # Computation can be done in FP32 to prevent potential over flow.
717
+ input_dtype = inputs.dtype
718
+ if inputs.dtype == torch.half:
719
+ inputs = inputs.float()
720
+ if amax.dtype == torch.half:
721
+ amax = amax.float()
722
+
723
+ min_amax = amax.min()
724
+ if min_amax < 0:
725
+ raise ValueError("Negative values in amax")
726
+
727
+ max_bound = torch.tensor((2.0 ** (num_bits - 1 + int(unsigned))) - 1.0, device=amax.device)
728
+ if unsigned:
729
+ min_bound = 0
730
+ elif narrow_range:
731
+ min_bound = -max_bound
732
+ else:
733
+ min_bound = -max_bound - 1
734
+ scale = max_bound / amax
735
+
736
+ epsilon = 1.0 / (1 << 24)
737
+ if min_amax <= epsilon: # Treat amax smaller than minimum representable of fp16 0
738
+ zero_amax_mask = amax <= epsilon
739
+ scale[zero_amax_mask] = 0 # Value quantized with amax=0 should all be 0
740
+
741
+ outputs = torch.clamp((inputs * scale).round_(), min_bound, max_bound)
742
+
743
+ if min_amax <= epsilon:
744
+ scale[zero_amax_mask] = (
745
+ 1.0 # Return 1 makes more sense for values quantized to 0 with amax=0
746
+ )
747
+
748
+ if input_dtype == torch.half:
749
+ outputs = outputs.half()
750
+
751
+ return outputs, scale
752
+
753
+
754
+ class FakeAffineTensorQuantFunction(Function):
755
+ """Fake version of affine quantization.
756
+
757
+ gemmlowp style scale+shift quantization. See more details in
758
+ https://github.com/google/gemmlowp/blob/master/doc/quantization.md.
759
+
760
+ We DO NOT recommend affine quantization on weights for performance reason. There might be value to affine quantize
761
+ activation as it can be cancelled by bias and comes with no performance penalty. This functionality is only added
762
+ for experimental purpose.
763
+ """
764
+
765
+ @staticmethod
766
+ def forward(ctx, inputs, min_range, max_range, num_bits=8):
767
+ """As it will be only applied on activation with per tensor granularity, broadcast is not needed.
768
+
769
+ Args:
770
+ ctx: Pytorch convention.
771
+ inputs: A Tensor of type float32.
772
+ min_range: A float.
773
+ max_range: A float.
774
+ num_bits: An integer
775
+
776
+ Returns:
777
+ outputs: A Tensor of type output_dtype
778
+ """
779
+ ctx.save_for_backward(inputs, min_range, max_range)
780
+
781
+ step_size = (max_range - min_range) / (2.0**num_bits - 1)
782
+
783
+ min_bound = -(2.0 ** (num_bits - 1))
784
+ max_bound = 2.0 ** (num_bits - 1) - 1
785
+
786
+ quant_zero = torch.round(min_range / step_size) - min_bound
787
+ quantized = torch.round(inputs / step_size) - quant_zero
788
+ quantized = torch.clamp(quantized, min_bound, max_bound)
789
+
790
+ outputs = (quantized + quant_zero) * step_size
791
+
792
+ return outputs
793
+
794
+ @staticmethod
795
+ def backward(ctx, grad_outputs):
796
+ """Implements straight through estimation with clipping.
797
+
798
+ Args:
799
+ ctx: Pytorch convention.
800
+ grad_output: A tensor of gradient of outputs.
801
+
802
+ Returns:
803
+ grad_inputs: A tensor of gradient
804
+ """
805
+ inputs, min_range, max_range = ctx.saved_tensors
806
+ zero = grad_outputs.new_zeros(1)
807
+ grad_inputs = torch.where((inputs <= max_range) * (inputs >= min_range), grad_outputs, zero)
808
+ return grad_inputs, None, None, None
809
+
810
+
811
+ tensor_quant = TensorQuantFunction.apply
812
+ legacy_fake_tensor_quant = LegacyFakeTensorQuantFunction.apply
813
+ fake_tensor_quant = FakeTensorQuantFunction.apply
814
+ fake_affine_tensor_quant = FakeAffineTensorQuantFunction.apply
815
+ scaled_e4m3 = ScaledE4M3Function.apply
816
+ dynamic_block_quant = DynamicBlockQuantizationFunction.apply