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,159 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 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
+
16
+ """Reference runner module for ONNX model execution.
17
+
18
+ This module provides functionality for running ONNX models using ONNXRuntime as a reference
19
+ implementation. It supports both random input generation and user-provided inputs through
20
+ NPZ or Polygraphy JSON files. The runner is used to analyze model behavior and validate
21
+ outputs during precision conversion.
22
+ """
23
+
24
+ import copy
25
+ import io
26
+ import sys
27
+ from collections import OrderedDict
28
+
29
+ import numpy as np
30
+ import onnx
31
+
32
+ from modelopt.onnx.autocast.logging_config import configure_logging, logger
33
+ from modelopt.onnx.quantization.ort_utils import _prepare_ep_list
34
+
35
+ configure_logging()
36
+
37
+
38
+ class ReferenceRunner:
39
+ """A class to run ONNX models with ONNXRuntime for reference inference."""
40
+
41
+ def __init__(
42
+ self, model: onnx.ModelProto, providers: list[str] = ["cpu"], trt_plugins: list[str] = []
43
+ ):
44
+ """Initialize with ONNX model path."""
45
+ self.model = model
46
+ self.input_names = [input.name for input in self.model.graph.input]
47
+ self.providers = self._prepare_ep_list_with_trt_plugin_path(providers, trt_plugins)
48
+
49
+ def _prepare_ep_list_with_trt_plugin_path(self, providers, trt_plugins):
50
+ providers = _prepare_ep_list(providers) or providers
51
+ if "TensorrtExecutionProvider" in providers:
52
+ providers.remove("TensorrtExecutionProvider")
53
+ # Ensure that the TRT EP is the first in the providers list to avoid fallback issues
54
+ trt_ep_options = (
55
+ {"trt_extra_plugin_lib_paths": ";".join(trt_plugins)} if trt_plugins else {}
56
+ )
57
+ providers.insert(0, ("TensorrtExecutionProvider", trt_ep_options))
58
+ logger.info(f"Successfully updated EPs for ORT: {providers}")
59
+ return providers
60
+
61
+ def _load_inputs_from_json(self, input_data_path):
62
+ """Load inputs from Polygraphy JSON format."""
63
+ from polygraphy.json import load_json
64
+
65
+ return load_json(input_data_path, description="input data")
66
+
67
+ def _load_inputs_from_npz(self, input_data_path):
68
+ """Load inputs from NPZ format."""
69
+ return [np.load(input_data_path)]
70
+
71
+ def _validate_inputs(self, data_loader):
72
+ """Validate that input names match the model."""
73
+ if isinstance(data_loader, list) and (
74
+ isinstance(data_loader[0], (dict, np.lib.npyio.NpzFile))
75
+ ):
76
+ if sorted(self.input_names) != sorted(data_loader[0].keys()):
77
+ raise ValueError("Input names from ONNX model do not match provided input names.")
78
+ else:
79
+ raise ValueError("Invalid input file.")
80
+
81
+ def _load_inputs(self, inputs):
82
+ """Get data loader from inputs or create random data loader if no inputs provided."""
83
+ from polygraphy.comparator import DataLoader
84
+
85
+ # If no inputs are provided, use random inputs
86
+ data_loader = DataLoader(val_range={"": (-1, 1)})
87
+
88
+ if inputs is not None:
89
+ if isinstance(inputs, str):
90
+ if inputs.endswith(".json"):
91
+ data_loader = self._load_inputs_from_json(inputs)
92
+ elif inputs.endswith(".npz"):
93
+ data_loader = self._load_inputs_from_npz(inputs)
94
+ else:
95
+ raise ValueError(
96
+ f"Invalid input file: {inputs}. Supported input file types: .json (Polygraphy JSON format), "
97
+ ".npz (Numpy)"
98
+ )
99
+ elif isinstance(inputs, (dict, OrderedDict)):
100
+ data_loader = [inputs]
101
+ else:
102
+ raise ValueError(
103
+ f"Invalid input type: {type(inputs)}. Supported input types: dict, OrderedDict, or a path to a "
104
+ "JSON or NPZ file."
105
+ )
106
+ self._validate_inputs(data_loader)
107
+
108
+ return data_loader
109
+
110
+ def run(self, inputs=None):
111
+ """Run FP32 inference with provided or random inputs."""
112
+ import onnxruntime as ort
113
+ from polygraphy import constants
114
+ from polygraphy.backend.onnx import BytesFromOnnx
115
+ from polygraphy.backend.onnx import ModifyOutputs as ModifyOnnxOutputs
116
+ from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx
117
+ from polygraphy.comparator import Comparator
118
+
119
+ logger.info("Running ONNX Runtime to obtain reference outputs (this may take a while)...")
120
+ # Set ONNX Runtime log level to ERROR to suppress warnings
121
+ ort.set_default_logger_severity(3)
122
+
123
+ model_copy = copy.deepcopy(self.model)
124
+ modify_outputs = ModifyOnnxOutputs(model_copy, outputs=constants.MARK_ALL)
125
+ serialize_onnx = BytesFromOnnx(modify_outputs)
126
+ build_onnxrt_session = SessionFromOnnx(serialize_onnx, providers=self.providers)
127
+ runners = [OnnxrtRunner(build_onnxrt_session)]
128
+
129
+ # Comparator is used despite the fact that we are using ONNXRuntime
130
+ # because it provides the ability to generate random inputs using DataLoader
131
+ data_loader = self._load_inputs(inputs)
132
+
133
+ # Temporarily redirect stdout to suppress Comparator.run() output
134
+ stdout = sys.stdout
135
+ string_buffer = io.StringIO()
136
+ sys.stdout = string_buffer
137
+ try:
138
+ results = Comparator.run(runners, data_loader=data_loader)
139
+ finally:
140
+ # Capture the output before restoring stdout
141
+ captured_output = string_buffer.getvalue()
142
+ sys.stdout = stdout
143
+
144
+ if not results:
145
+ logger.error(f"ONNXRuntime execution failed with output:\n{captured_output}")
146
+ raise Exception("ONNXRuntime failed to run, see logs for details")
147
+
148
+ # Get the output results
149
+ output_dict = OrderedDict(results[0][1][0])
150
+
151
+ # Include input data for completeness
152
+ input_data = next(iter(data_loader))
153
+
154
+ # Combine inputs and outputs in the returned dictionary
155
+ combined_dict = OrderedDict()
156
+ combined_dict.update(input_data)
157
+ combined_dict.update(output_dict)
158
+
159
+ return combined_dict
@@ -0,0 +1,117 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 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
+
16
+ """Utility functions for AutoCast.
17
+
18
+ This module provides common utility functions used across the AutoCast package.
19
+ It includes functions for graph traversal, tensor type inference, model validation,
20
+ and mapping setup between nodes, initializers, and value info. These utilities
21
+ support the core functionality of model precision conversion.
22
+ """
23
+
24
+ import onnx
25
+
26
+
27
+ def setup_mappings(model: onnx.ModelProto) -> tuple[dict, dict, dict]:
28
+ """Setup and return mappings for model components.
29
+
30
+ Args:
31
+ model: ONNX model to create mappings for.
32
+
33
+ Returns:
34
+ Tuple containing:
35
+ - value_info_map: Mapping of names to value infos.
36
+ - initializer_map: Mapping of names to initializers.
37
+ - node_to_init_map: Mapping of node names to their initializer inputs.
38
+ """
39
+ value_info_map = {}
40
+ for container in (model.graph.value_info, model.graph.input, model.graph.output):
41
+ value_info_map.update((vi.name, vi) for vi in container)
42
+
43
+ initializer_map = {init.name: init for init in model.graph.initializer}
44
+
45
+ node_to_init_map = {
46
+ node.name: [
47
+ initializer_map[input_name]
48
+ for input_name in node.input
49
+ if input_name in initializer_map
50
+ ]
51
+ for node in model.graph.node
52
+ }
53
+
54
+ return value_info_map, initializer_map, node_to_init_map
55
+
56
+
57
+ def get_consumer_nodes(model: onnx.ModelProto, tensor_name: str) -> list[onnx.NodeProto]:
58
+ """Get all consumer nodes for a given tensor name.
59
+
60
+ Args:
61
+ model: The ONNX model to search.
62
+ tensor_name: Name of the tensor to find consumers for.
63
+
64
+ Returns:
65
+ list[onnx.NodeProto]: List of nodes that consume the tensor.
66
+ """
67
+ return [n for n in model.graph.node if tensor_name in n.input]
68
+
69
+
70
+ def get_producer_nodes(model: onnx.ModelProto, tensor_name: str) -> list[onnx.NodeProto]:
71
+ """Get all producer nodes for a given tensor name.
72
+
73
+ Args:
74
+ model: The ONNX model to search.
75
+ tensor_name: Name of the tensor to find producers for.
76
+
77
+ Returns:
78
+ list[onnx.NodeProto]: List of nodes that produce the tensor.
79
+ """
80
+ return [n for n in model.graph.node if tensor_name in n.output]
81
+
82
+
83
+ def get_unique_consumer_node(model: onnx.ModelProto, tensor_name: str) -> onnx.NodeProto:
84
+ """Get a single consumer node and raise exception if there are multiple consumers.
85
+
86
+ Args:
87
+ model: The ONNX model to search.
88
+ tensor_name: Name of the tensor to find consumer for.
89
+
90
+ Returns:
91
+ onnx.NodeProto: The single consumer node.
92
+
93
+ Raises:
94
+ Exception: If there is not exactly one consumer node.
95
+ """
96
+ consumers = get_consumer_nodes(model, tensor_name)
97
+ if len(consumers) != 1:
98
+ raise Exception(f"Expected single consumer for {tensor_name}, found {len(consumers)}")
99
+ return consumers[0]
100
+
101
+
102
+ def get_cast_to_type(cast_node: onnx.NodeProto) -> int:
103
+ """Get the target type from a Cast node.
104
+
105
+ Args:
106
+ cast_node: The Cast node to extract type from.
107
+
108
+ Returns:
109
+ int: The target type value from the Cast node's 'to' attribute.
110
+
111
+ Raises:
112
+ ValueError: If the Cast node does not have a 'to' attribute.
113
+ """
114
+ for attr in cast_node.attribute:
115
+ if attr.name == "to":
116
+ return attr.i
117
+ raise ValueError("Cast node does not have 'to' attribute")
@@ -0,0 +1,16 @@
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
+
16
+ """Utilities for exporting LLM models to ONNX."""
@@ -0,0 +1,162 @@
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
+
16
+ """Utilities for exporting LLM models to ONNX."""
17
+
18
+ import json
19
+ import os
20
+ import time
21
+ from enum import Enum
22
+
23
+ import torch
24
+ from transformers import DynamicCache
25
+
26
+
27
+ class RopeType(Enum):
28
+ """Rope type enum."""
29
+
30
+ K_NONE = 0
31
+ K_ROPE_ROTATE_GPTJ = 1
32
+ K_ROPE_ROTATE_NEOX = 2
33
+ K_MROPE = 3
34
+
35
+
36
+ class ModelLoader:
37
+ """A class to handle HuggingFace model loading and configuration."""
38
+
39
+ def __init__(self, torch_dir, config_path):
40
+ """Initialize the ModelLoader."""
41
+ self.config_path = config_path
42
+ self.torch_dir = torch_dir
43
+ self.model_type = self.get_model_type()
44
+ self.hf_model = None
45
+ self.rope_type = RopeType.K_ROPE_ROTATE_NEOX
46
+
47
+ def get_model_type(self):
48
+ """Get model type from config file."""
49
+ with open(self.config_path) as f:
50
+ return json.load(f).get("model_type")
51
+
52
+ def load_model(self):
53
+ """Load HuggingFace model based on model type."""
54
+ print(f"Loading HF model from {self.torch_dir} with model type {self.model_type}")
55
+ from transformers import AutoModelForCausalLM
56
+
57
+ self.hf_model = AutoModelForCausalLM.from_pretrained(
58
+ self.torch_dir, torch_dtype=torch.float16, trust_remote_code=True
59
+ )
60
+
61
+ return self.hf_model.eval().cuda()
62
+
63
+ def get_rope_type(self):
64
+ """Get rope type."""
65
+ return self.rope_type
66
+
67
+
68
+ class WrapperModelForCausalLM(torch.nn.Module):
69
+ """Wrapper Model to ensure all models have the same I/O."""
70
+
71
+ def __init__(self, model):
72
+ """Initialize the WrapperModelForCausalLM."""
73
+ super().__init__()
74
+ try:
75
+ self.model = model.model
76
+ except Exception:
77
+ self.model = model
78
+ self.lm_head = model.lm_head
79
+ self.config = model.config
80
+
81
+ def forward(
82
+ self,
83
+ input_ids,
84
+ past_key_values,
85
+ ):
86
+ """Forward pass."""
87
+ past_key_values = DynamicCache.from_legacy_cache(past_key_values)
88
+ outputs = self.model(input_ids=input_ids, past_key_values=past_key_values, use_cache=True)
89
+ hidden_states = outputs[0]
90
+ past_key_values = outputs.past_key_values.to_legacy_cache()
91
+ logits = self.lm_head(hidden_states)
92
+ return logits, past_key_values
93
+
94
+
95
+ def llm_to_onnx(model, output_dir, extra_inputs={}, extra_dyn_axes={}):
96
+ """Export the WrapperModelForCausalLM to ONNX with fixed I/O names and shape definitions and save to `output_dir`.
97
+
98
+ Parameters:
99
+ model: torch.Module
100
+ output_dir: str, the output_dir of the original ONNX.
101
+ extra_inputs: dict, append additional inputs after kv_cache. Usually for VL models
102
+ extra_dyn_axes: dict. Usually for VL models
103
+ """
104
+ start_time = time.time()
105
+ config = model.config
106
+ num_layers = config.num_hidden_layers
107
+ num_attention_heads = config.num_attention_heads
108
+ num_key_value_heads = config.num_key_value_heads
109
+ hidden_size = config.hidden_size
110
+ hidden_size_per_layer = hidden_size // num_attention_heads
111
+
112
+ dummy_bs = 1
113
+ dummy_len = 10
114
+ dummy_input_ids = torch.randint(100, (dummy_bs, dummy_len), dtype=torch.int64).cuda()
115
+ input_names = ["input_ids"]
116
+ output_names = ["logits"]
117
+ dynamic_axes = {"input_ids": {0: "batch_size", 1: "seq_len"}}
118
+ dummy_kv_cache = ()
119
+ for i in range(num_layers):
120
+ dummy_k = torch.rand(
121
+ (dummy_bs, num_key_value_heads, dummy_len, hidden_size_per_layer), dtype=torch.float16
122
+ ).cuda()
123
+ dummy_v = torch.rand(
124
+ (dummy_bs, num_key_value_heads, dummy_len, hidden_size_per_layer), dtype=torch.float16
125
+ ).cuda()
126
+ dummy_kv_cache = (*dummy_kv_cache, (dummy_k, dummy_v))
127
+ input_names.extend([f"past_key_values.{i}.key", f"past_key_values.{i}.value"])
128
+ output_names.extend([f"present_key_values.{i}.key", f"present_key_values.{i}.value"])
129
+ input_dynamic_axes = {0: "batch_size", 2: "past_len"}
130
+ dynamic_axes[f"past_key_values.{i}.key"] = input_dynamic_axes
131
+ dynamic_axes[f"past_key_values.{i}.value"] = input_dynamic_axes
132
+
133
+ torch_to_onnx(
134
+ model,
135
+ (dummy_input_ids, {"past_key_values": dummy_kv_cache, **extra_inputs}),
136
+ output_dir,
137
+ "model.onnx",
138
+ input_names=input_names + list(extra_inputs.keys()),
139
+ output_names=output_names,
140
+ dynamic_axes=dynamic_axes | extra_dyn_axes,
141
+ )
142
+
143
+ end_time = time.time()
144
+ print(
145
+ f"Native ONNX Export from torch completed in {end_time - start_time}s. ONNX file is saved to {output_dir}."
146
+ )
147
+
148
+
149
+ def torch_to_onnx(model, inputs, onnx_dir, onnx_name, input_names, output_names, dynamic_axes):
150
+ """Export the model to ONNX."""
151
+ os.makedirs(onnx_dir, exist_ok=True)
152
+ with torch.inference_mode():
153
+ torch.onnx.export(
154
+ model,
155
+ inputs,
156
+ f"{onnx_dir}/{onnx_name}",
157
+ input_names=input_names,
158
+ output_names=output_names,
159
+ dynamic_axes=dynamic_axes,
160
+ opset_version=19,
161
+ do_constant_folding=True,
162
+ )
@@ -0,0 +1,122 @@
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
+
16
+ """Quantization utilities for LLM models."""
17
+
18
+ import time
19
+
20
+ import modelopt.torch.quantization as mtq
21
+ from modelopt.torch.utils.dataset_utils import get_dataset_dataloader
22
+
23
+
24
+ def _quantize_model(model, quant_config, calib_dataloader=None):
25
+ """The calibration loop for the model can be setup using the modelopt API.
26
+
27
+ Example usage:
28
+ from modelopt.torch.utils.dataset_utils import create_forward_loop
29
+ model = ... # Initialize the model
30
+ tokenizer = ... # Initialize the tokenizer
31
+ quant_cfg = ... # Setup quantization configuration
32
+ forward_loop = create_forward_loop(model=model, dataset_name="cnn_dailymail", tokenizer=tokenizer)
33
+ mtq.quantize(model, quant_cfg, forward_loop=forward_loop)
34
+ """
35
+
36
+ def calibrate_loop(model):
37
+ """Adjusts weights and scaling factors based on selected algorithms."""
38
+ for idx, data in enumerate(calib_dataloader):
39
+ if idx % 10 == 0:
40
+ print(f"Calibrating batch {idx}...")
41
+ if isinstance(data, dict):
42
+ data = {k: v.to(model.device) for k, v in data.items()}
43
+ model(**data)
44
+ else:
45
+ data = data.to(model.device)
46
+ model(data)
47
+
48
+ print("Starting quantization...")
49
+ start_time = time.time()
50
+ mtq.quantize(model, quant_config, forward_loop=calibrate_loop)
51
+ end_time = time.time()
52
+ print(f"Quantization finishes in {end_time - start_time}s.")
53
+
54
+ return model
55
+
56
+
57
+ def get_quant_config(precision, lm_head_precision="fp16"):
58
+ """Get the quantization configuration."""
59
+ if precision == "fp8":
60
+ quant_cfg = mtq.FP8_DEFAULT_CFG
61
+
62
+ elif precision == "nvfp4":
63
+ quant_cfg = mtq.NVFP4_DEFAULT_CFG
64
+
65
+ elif precision == "int4_awq":
66
+ quant_cfg = mtq.INT4_AWQ_CFG
67
+
68
+ else:
69
+ raise ValueError(f"Unsupported precision: {precision}")
70
+
71
+ config_dict = quant_cfg["quant_cfg"] # type: dict
72
+
73
+ if lm_head_precision == "fp8":
74
+ config_dict["*lm_head.input_quantizer"] = {"num_bits": (4, 3), "axis": None}
75
+ config_dict["*lm_head.weight_quantizer"] = {"num_bits": (4, 3), "axis": None}
76
+ elif lm_head_precision == "nvfp4":
77
+ config_dict["*lm_head.input_quantizer"] = {
78
+ "num_bits": (2, 1),
79
+ "block_sizes": {-1: 16, "type": "dynamic", "scale_bits": (4, 3)},
80
+ "axis": None,
81
+ "enable": True,
82
+ }
83
+ config_dict["*lm_head.weight_quantizer"] = {
84
+ "num_bits": (2, 1),
85
+ "block_sizes": {-1: 16, "type": "dynamic", "scale_bits": (4, 3)},
86
+ "axis": None,
87
+ "enable": True,
88
+ }
89
+ return quant_cfg
90
+
91
+
92
+ def quantize(
93
+ model, tokenizer, precision, lm_head_precision="fp16", dataset_dir=None, calib_size=512
94
+ ):
95
+ """Quantize the PyTorch model to fp8 or int4_awq."""
96
+ assert precision in [
97
+ "fp8",
98
+ "int4_awq",
99
+ "nvfp4",
100
+ ], (
101
+ f"Only fp8(W8A8), int4_awq(W4A16), nvfp4(W4A4) is supported. You passed an unsupported precision: {precision}."
102
+ )
103
+
104
+ assert lm_head_precision in ["fp16"], (
105
+ f"Only fp16(unquantized) is supported for lm_head. You passed an unsupported precision: {lm_head_precision}."
106
+ )
107
+
108
+ if tokenizer.pad_token != "<unk>": # nosec B105
109
+ tokenizer.pad_token = tokenizer.eos_token
110
+ if tokenizer.pad_token is None:
111
+ tokenizer.pad_token = tokenizer.eos_token
112
+ if not dataset_dir:
113
+ dataset_dir = "cnn_dailymail"
114
+
115
+ batch_size = 1
116
+ data_loader = get_dataset_dataloader(
117
+ dataset_name=dataset_dir, tokenizer=tokenizer, batch_size=batch_size, num_samples=calib_size
118
+ )
119
+ quant_config = get_quant_config(precision, lm_head_precision)
120
+ quantized_model = _quantize_model(model, quant_config, data_loader)
121
+ mtq.print_quant_summary(quantized_model)
122
+ return quantized_model