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,620 @@
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
+ """Module to handle model converting and restoring for optimization methods.
17
+
18
+ When applying a model optimization algorithm, we usually need to modify the model in each step
19
+ (mode) of the algorithm. This module provides the state manager, which is a standardized interface
20
+ (class) to record and store state information in the model.
21
+
22
+ Op top of the state manager, this module provides utilities to save a history of these modifications
23
+ ("modelopt state dict") and restoring a unmodified model to the state indicated in the state dict.
24
+ """
25
+
26
+ import copy
27
+ import os
28
+ import warnings
29
+ from collections import deque
30
+ from collections.abc import Iterator
31
+ from typing import Any, BinaryIO
32
+
33
+ import torch
34
+ import torch.nn as nn
35
+
36
+ from modelopt import __version__
37
+ from modelopt.torch.utils import ModelLike, init_model_from_model_like, unwrap_model
38
+
39
+ from .config import ConfigDict, ModeloptBaseConfig
40
+ from .mode import (
41
+ MetadataDict,
42
+ ModeDescriptor,
43
+ ModeKwargsType,
44
+ ModeLike,
45
+ ModeState,
46
+ ModeType,
47
+ _ModeRegistryCls,
48
+ get_mode_config,
49
+ )
50
+
51
+ __all__ = [
52
+ "ModeloptStateManager",
53
+ "apply_mode",
54
+ "modelopt_state",
55
+ "restore",
56
+ "restore_from_modelopt_state",
57
+ "save",
58
+ ]
59
+
60
+ ModeloptStateList = list[tuple[str, ModeState]] # state data structure for multiple modes
61
+
62
+
63
+ class ModeloptStateManager:
64
+ """A class to handle the modelopt state stored for each mode corresponding to a task/mode."""
65
+
66
+ _state_key = "_modelopt_state"
67
+ _state_version_key = "_modelopt_state_version"
68
+
69
+ def __init__(self, model: nn.Module | None = None, init_state: bool = False) -> None:
70
+ """Initialize state manager.
71
+
72
+ Args:
73
+ model: Module that has modelopt_state stored. If None, a fake module is created to store
74
+ any state that might be added with the manager.
75
+ init_state: Whether to initialize the modelopt state for the model if it does not exist.
76
+ """
77
+ # just assume fake module for easy implementation below
78
+ if not model:
79
+ model = nn.Module()
80
+ init_state = True # always initialize fake module
81
+
82
+ # initialize modelopt state if desired. Note that by default we don't do that to avoid
83
+ # accidentally modifying a user-provided model.
84
+ if init_state:
85
+ assert not hasattr(model, self._state_key), "Model already has modelopt state!"
86
+ setattr(model, self._state_key, [])
87
+ setattr(model, self._state_version_key, [__version__])
88
+
89
+ # sanity check that root module has modelopt state now
90
+ assert self.is_converted(model, is_root=True), "Model must have modelopt state!"
91
+
92
+ # store reference to state
93
+ self._state: ModeloptStateList = getattr(model, self._state_key)
94
+ self._state_version = getattr(model, self._state_version_key)
95
+
96
+ @property
97
+ def has_state(self) -> bool:
98
+ """Return whether the model has a non-trivial modelopt state."""
99
+ return bool(self._state)
100
+
101
+ @classmethod
102
+ def is_converted(cls, model: nn.Module, is_root: bool = False) -> bool:
103
+ """Check if model is converted.
104
+
105
+ Args:
106
+ model: A model to be checked for state/metadata from the convert process.
107
+ is_root: Additionally check whether the module with state is the root module.
108
+
109
+ Returns:
110
+ True if the model contains modelopt state indicating that it has been converted.
111
+
112
+ This method raises an assertion when multiple modelopt_states are detected or when is_root is
113
+ set to True but the module with state is not the root module.
114
+ """
115
+ # check for submodules with state
116
+ mods_with_state = [name for name, m in model.named_modules() if hasattr(m, cls._state_key)]
117
+ # check if there is multiple submodules with state
118
+ assert len(mods_with_state) <= 1, "Model has multiple modelopt states!"
119
+ is_converted = bool(mods_with_state)
120
+
121
+ # check if mod with state is root module if desired
122
+ if is_converted:
123
+ assert not is_root or mods_with_state[0] == "", (
124
+ "Model has modelopt state but not the root!"
125
+ )
126
+
127
+ return is_converted
128
+
129
+ # TODO: consider renaming state_dict???
130
+ def state_dict(self) -> ModeloptStateList:
131
+ """Return the metadata of the model."""
132
+ return self._state
133
+
134
+ @property
135
+ def state_version(self) -> str:
136
+ """Return the version of modelopt state contained in the state manager."""
137
+ return self._state_version[0]
138
+
139
+ def load_state_dict(self, state_dict: ModeloptStateList, version: str) -> None:
140
+ """Load the provided ``state_dict`` to the modelopt_state."""
141
+ assert not self.has_state, "Cannot load state_dict if there is already one."
142
+
143
+ # Alert if the first 2 version numbers do not match, e.g., 0.3.2 vs 0.4.0.
144
+ if tuple(version.split(".")[:2]) != tuple(__version__.split(".")[:2]):
145
+ warnings.warn(
146
+ f"The checkpoint is stored with version {version}, but current version is"
147
+ f" {__version__}. Compatibility of checkpoint with current version is not guaranteed!"
148
+ )
149
+
150
+ # make sure we operate on deepcopy
151
+ state_dict = copy.deepcopy(state_dict)
152
+ # add modes one-by-one
153
+ for m_str, m_state in state_dict:
154
+ # adds config and metadata with sanity checks
155
+ config = self.get_config_class(m_str, m_state["config"])
156
+ self.add_mode(m_str, config, m_state["metadata"])
157
+
158
+ # overwrite state manually afterwards to ensure exact consistency with provided state_dict
159
+ self._state.clear()
160
+ self._state.extend(state_dict)
161
+ self._state_version[0] = version
162
+
163
+ @classmethod
164
+ def transfer_state_dict(cls, model_from: nn.Module, model_to: nn.Module) -> None:
165
+ """Transfer the state (same instance) from one model to another."""
166
+ manager_from = ModeloptStateManager(model_from, init_state=False) # state must exist
167
+ manager_to = ModeloptStateManager(model_to, init_state=True) # state must NOT exist
168
+
169
+ # transfer state_dict (this uses sanity checks + deepcopy)
170
+ manager_to.load_state_dict(manager_from.state_dict(), manager_from.state_version)
171
+
172
+ # manually set the state dict to be the exact same instance
173
+ setattr(model_to, cls._state_key, manager_from.state_dict())
174
+ manager_to = ModeloptStateManager(model_to, init_state=False) # state must exist now
175
+
176
+ # remove state from model_from
177
+ cls.remove_state(model_from)
178
+
179
+ @staticmethod
180
+ def has_state_for_mode_type(
181
+ mode_type: str,
182
+ *,
183
+ model: nn.Module | None = None,
184
+ state: dict[str, Any] | None = None,
185
+ ) -> bool:
186
+ """Check if the model or modelopt state contains state from any modes of a given type.
187
+
188
+ Args:
189
+ mode_type: The type of mode to check for.
190
+ Example types: ``quantization``, ``distill``, ``nas``, ``prune``, ``speculative``, etc.
191
+ model: A model to check for state.
192
+ state: A modelopt state extracted from a model.
193
+ """
194
+ if (model and state) or (not model and not state):
195
+ raise ValueError("Must provide either model or state!")
196
+ if model:
197
+ state = modelopt_state(model)
198
+ mode_registry = _ModeRegistryCls.get_registry_by_name(mode_type)
199
+ return any(m_str in mode_registry for m_str, _ in state["modelopt_state_dict"]) # type: ignore [index]
200
+
201
+ @classmethod
202
+ def remove_state(cls, model: nn.Module) -> None:
203
+ """Delete the state dict from the model."""
204
+ delattr(model, cls._state_key)
205
+
206
+ def modes_with_states(
207
+ self,
208
+ ) -> Iterator[tuple[ModeDescriptor, ModeloptBaseConfig, MetadataDict]]:
209
+ """Yield the mode together with the full config and metadata from the state."""
210
+ for m_str, m_state in self._state:
211
+ config = self.get_config_class(m_str, m_state["config"])
212
+ yield _ModeRegistryCls.get_from_any(m_str), config, m_state["metadata"]
213
+
214
+ @property
215
+ def last_mode(self) -> ModeDescriptor | None:
216
+ """Return the last mode applied to the model (last stored mode)."""
217
+ return _ModeRegistryCls.get_from_any(self._state[-1][0]) if self._state else None
218
+
219
+ @property
220
+ def _last_metadata(self) -> MetadataDict:
221
+ """Return the metadata of the last mode applied to the model (must exist!)."""
222
+ return self._state[-1][1]["metadata"]
223
+
224
+ @property
225
+ def _last_config(self) -> ModeloptBaseConfig:
226
+ """Return the config of the last mode applied to the model (must exist!)."""
227
+ return self.get_config_class(self._state[-1][0], self._state[-1][1]["config"])
228
+
229
+ @_last_config.setter
230
+ def _last_config(self, config: ModeloptBaseConfig) -> None:
231
+ """Set the config of the last mode applied to the model (must exist!)."""
232
+ self._state[-1][1]["config"] = config.model_dump()
233
+
234
+ @property
235
+ def _export_stack(self) -> deque[tuple[str, str]]:
236
+ """Infer the stack of export modes that still must be applied from existing modes.
237
+
238
+ Returns:
239
+ A deque of tuples of the form ``(mode_str, export_mode_str)`` representing the mode
240
+ which requires an export mode and the export mode itself.
241
+ """
242
+ stack = deque()
243
+ for m, _, _ in self.modes_with_states():
244
+ if m.export_mode is not None:
245
+ stack.append((str(m), m.export_mode))
246
+ elif m.is_export_mode:
247
+ assert str(m) == stack.pop()[1], "Inconsistent export stack!"
248
+ return stack
249
+
250
+ @staticmethod
251
+ def get_config_class(mode: ModeType, config: ConfigDict) -> ModeloptBaseConfig:
252
+ """Standardize the provided config to the corresponding config class."""
253
+ # validate and standardize to the config class and return
254
+ return _ModeRegistryCls.get_from_any(mode).config_class(**config)
255
+
256
+ def check_mode(self, mode: ModeType) -> None:
257
+ """Check if the proposed mode is compatible with the current state."""
258
+ # standardize mode to descriptor
259
+ mode_d = _ModeRegistryCls.get_from_any(mode)
260
+
261
+ # check for export mode compatibility
262
+ export_stack = self._export_stack
263
+ if mode_d.is_export_mode:
264
+ assert export_stack and str(mode_d) == export_stack[-1][1], (
265
+ f"Cannot add {mode_d} according to the current export stack: {export_stack}."
266
+ )
267
+
268
+ # sanity checks for next mode compatibilities according to the current last mode
269
+ last_mode = self.last_mode
270
+ if last_mode:
271
+ mode_d.assert_compatibility_as_next_mode_of(last_mode)
272
+
273
+ # sanity checks for next mode compatible with last mode in the stack. These
274
+ # compatibilities still apply since we did not apply corresponding export mode yet.
275
+ if export_stack:
276
+ # last_m_stack := last mode on the stack that has a corresponding export mode
277
+ last_m_stack = _ModeRegistryCls.get_from_any(export_stack[-1][0])
278
+ mode_d.assert_compatibility_as_next_mode_of(last_m_stack)
279
+
280
+ def add_mode(self, mode: ModeType, config: ModeloptBaseConfig, metadata: MetadataDict) -> None:
281
+ """Add mode and update state in-place.
282
+
283
+ Note that self._state is a list (preserves insertion order of keys) and we can therefore
284
+ recall the order of modes!
285
+ """
286
+ # standardize mode to descriptor
287
+ mode_d = _ModeRegistryCls.get_from_any(mode)
288
+
289
+ # sanity checks for mode incompatibilities
290
+ self.check_mode(mode_d)
291
+
292
+ # store mode information
293
+ m_state: ModeState = {"config": config.model_dump(), "metadata": metadata}
294
+
295
+ self._state.append((str(mode_d), m_state))
296
+
297
+ def update_last_state_before_new_mode(self, model: nn.Module) -> None:
298
+ """Update the metadata and config of the last mode applied to the model."""
299
+ last_mode = self.last_mode
300
+ if last_mode is not None:
301
+ last_config = self._last_config
302
+ last_mode.update_for_new_mode(model, last_config, self._last_metadata)
303
+ self._last_config = last_config
304
+
305
+ def update_last_state_before_save(self, model: nn.Module) -> None:
306
+ """Update the metadata and config of the last mode applied to the model."""
307
+ last_mode = self.last_mode
308
+ if last_mode is not None:
309
+ last_config = self._last_config
310
+ last_mode.update_for_save(model, last_config, self._last_metadata)
311
+ self._last_config = last_config
312
+
313
+
314
+ class ApplyModeError(RuntimeError):
315
+ """Error raised when applying a mode to a model fails."""
316
+
317
+
318
+ class ModelLikeModule(nn.Module):
319
+ """Just a temp module type to store the initialization recipe for the actual model."""
320
+
321
+ def __init__(self, modellike: ModelLike) -> None:
322
+ super().__init__()
323
+ assert not isinstance(modellike, nn.Module), "modellike should not be a nn.Module!"
324
+ self.modellike = modellike
325
+
326
+ def init_modellike(self) -> nn.Module:
327
+ """Initialize the modellike to be an actual model."""
328
+ model = init_model_from_model_like(self.modellike)
329
+ ModeloptStateManager.transfer_state_dict(self, model)
330
+ return model
331
+
332
+
333
+ def _check_init_modellike(model: nn.Module, mode: ModeDescriptor) -> nn.Module:
334
+ """Utility to initialize a ModelLikeModule if needed according to the mode."""
335
+ if mode.require_model_like:
336
+ assert isinstance(model, ModelLikeModule), "Model must be a ModelLikeModule!"
337
+ elif isinstance(model, ModelLikeModule):
338
+ model = model.init_modellike()
339
+ return model
340
+
341
+
342
+ def apply_mode(
343
+ model: ModelLike,
344
+ mode: ModeLike,
345
+ registry: _ModeRegistryCls | None = None,
346
+ init_state: bool | None = None,
347
+ mode_kwargs: ModeKwargsType | None = None,
348
+ ) -> nn.Module:
349
+ """Apply the provided modes the model, record the changes, and return the model.
350
+
351
+ Args:
352
+ model: A model-like object. Can be an nn.Module, a model class type, or a tuple.
353
+ tuple must be of the form ``(model_cls,)`` or ``(model_cls, args)`` or
354
+ ``(model_cls, args, kwargs)``. Model will be initialized as
355
+ ``model_cls(*args, **kwargs)``.
356
+ mode: A mode, a list of modes or a list of tuples containing the mode and its config. The
357
+ mode may be specified as a string or as the actual
358
+ :class:`ModeDescriptor<modelopt.torch.opt.mode.ModeDescriptor>` class such as
359
+ :class:`QuantizeModeDescriptor<modelopt.torch.opt.quantization.QuantizeModeDescriptor>` class.
360
+ registry: An optional mode registry from which to retrieve the mode. If not provided, all
361
+ registries will be searched.
362
+ init_state: Flag indicating whether we should initialize the state manager for the model. If
363
+ not provided, it will be inferred from the model. This flag can be used to enforce a
364
+ certain behavior. For example, for ``init_state=True`` the state manager will raise an
365
+ error if the model already contains state.
366
+ mode_kwargs: Optional keyword arguments for mode conversion. Can be a single dictionary
367
+ (applied to all modes) or a list of dictionaries (one per mode). These arguments
368
+ are passed to each mode's convert entrypoint but are not saved in modelopt state.
369
+ This is useful for passing non-serializable objects (like custom functions) that aren't
370
+ needed when restoring the model.
371
+
372
+ Returns:
373
+ The converted model after applying the desired modes.
374
+ """
375
+ # initialize ModelLikeModule if needed.
376
+ model = model if isinstance(model, nn.Module) else ModelLikeModule(model)
377
+
378
+ # vanilla case (just initialize+return)
379
+ if not mode:
380
+ return model.init_modellike() if isinstance(model, ModelLikeModule) else model
381
+
382
+ # check if the model is in a wrapper
383
+ model = unwrap_model(model, raise_error=True)
384
+
385
+ # standardize mode to ModeConfigList
386
+ mode_and_config = get_mode_config(mode)
387
+
388
+ # get or initialize the state manager for the model
389
+ manager = ModeloptStateManager(
390
+ model,
391
+ init_state=(
392
+ not ModeloptStateManager.is_converted(model) if init_state is None else init_state
393
+ ),
394
+ )
395
+
396
+ # get mode function based on registry argument
397
+ get_mode = registry.__getitem__ if registry else _ModeRegistryCls.get_from_any
398
+
399
+ # update metadata of currently last mode before adding new modes
400
+ manager.update_last_state_before_new_mode(model)
401
+
402
+ # check whether a ModelLike should be initialized
403
+ model = _check_init_modellike(model, get_mode(mode_and_config[0][0]))
404
+
405
+ if mode_kwargs is not None:
406
+ if isinstance(mode_kwargs, dict):
407
+ mode_kwargs = [mode_kwargs] * len(mode_and_config)
408
+ assert len(mode_kwargs) == len(mode_and_config), (
409
+ "Number of mode kwargs must match number of modes!"
410
+ )
411
+ else:
412
+ mode_kwargs = [{}] * len(mode_and_config)
413
+
414
+ # loop through modes and call convert entrypoint for each mode and record data in manager.
415
+ for (m, config), kwargs in zip(mode_and_config, mode_kwargs):
416
+ manager.check_mode(m)
417
+ config = manager.get_config_class(m, config)
418
+ model, metadata = get_mode(m).convert(model, config, **kwargs) # type: ignore [call-arg]
419
+ manager.add_mode(m, config, metadata)
420
+
421
+ # If the existing mode is empty, create an model instance from ModelLikeModule.
422
+ if not manager.has_state and isinstance(model, ModelLikeModule):
423
+ model = model.init_modellike()
424
+
425
+ # it cannot be a ModelLikeModule anymore at the end
426
+ assert not isinstance(model, ModelLikeModule), "Model must be a regular Module now!"
427
+
428
+ # return model with state recorded
429
+ return model
430
+
431
+
432
+ def get_mode(model: nn.Module) -> ModeDescriptor | None:
433
+ """Get mode of converted network.
434
+
435
+ model: A model that contains modelopt_state
436
+
437
+ The mode of the model is defined as the last mode activated during the convert process.
438
+ """
439
+ if ModeloptStateManager.is_converted(model):
440
+ return ModeloptStateManager(model).last_mode
441
+ return None
442
+
443
+
444
+ def modelopt_state(model: nn.Module) -> dict[str, Any]:
445
+ """Return the modelopt state dict describing the modifications to the model.
446
+
447
+ Note that the returned ``modelopt_state`` does not contain the model parameters such as weights and biases.
448
+ ``modelopt_state`` is useful for saving and loading various modelopt optimization states separately from the
449
+ model parameters. For example:
450
+
451
+ .. code-block::
452
+
453
+ import modelopt.torch.opt as mto
454
+
455
+ # Save the modelopt state and model weights separately
456
+ torch.save(mto.modelopt_state(model), "modelopt_state.pt") # Save the modelopt state
457
+ torch.save(model.state_dict(), "model_weights.pt") # Save the model weights
458
+
459
+ If you want to save the model weights and the modelopt state together, please use
460
+ :meth:`mto.save()<modelopt.torch.opt.conversion.save>`.
461
+
462
+ Args:
463
+ model: the modelopt-modified model.
464
+
465
+ Returns:
466
+ An modelopt state dictionary describing the modifications to the model.
467
+ """
468
+ # unwrap model
469
+ model = unwrap_model(model, force_unwrap=True)
470
+
471
+ # retrieve state manager
472
+ manager = ModeloptStateManager(
473
+ model=model if ModeloptStateManager.is_converted(model) else None
474
+ )
475
+
476
+ # update metadata of current mode as needed
477
+ manager.update_last_state_before_save(model)
478
+
479
+ # construct state dict and return it
480
+ objs = {
481
+ "modelopt_state_dict": (
482
+ manager.state_dict()
483
+ ), # empty state_dict is okay (saving regular models)
484
+ "modelopt_version": __version__,
485
+ }
486
+ return objs
487
+
488
+
489
+ def save(model: nn.Module, f: str | os.PathLike | BinaryIO, **kwargs) -> None:
490
+ """Save a model's state dict together with the modelopt state dict to restore its architecture.
491
+
492
+ Args:
493
+ model: Any model.
494
+ f: Target file location.
495
+ **kwargs: additional args for ``torch.save()``.
496
+
497
+ .. note::
498
+
499
+ If model is a wrapper such as DistributedDataParallel, it will be unwrapped for saving.
500
+ """
501
+ # unwrap model
502
+ model = unwrap_model(model, warn=True)
503
+
504
+ # store ckpt
505
+ ckpt_dict = {
506
+ "modelopt_state": modelopt_state(model),
507
+ "model_state_dict": model.state_dict(),
508
+ }
509
+
510
+ # store object
511
+ torch.save(ckpt_dict, f, **kwargs)
512
+
513
+
514
+ def restore_from_modelopt_state(model: ModelLike, modelopt_state: dict[str, Any]) -> nn.Module:
515
+ """Restore the model architecture from the modelopt state dictionary based on the user-provided model.
516
+
517
+ This method does not restore the model parameters such as weights, biases and quantization scales.
518
+ Please load the weights and biases with the original checkpoint loading method after restoring
519
+ modelopt states with `restore_from_modelopt_state`. For example:
520
+
521
+ .. code-block:: python
522
+
523
+ import modelopt.torch.opt as mto
524
+
525
+ model = ... # Create the model-like object
526
+
527
+ # Restore the previously saved modelopt state followed by model weights
528
+ mto.restore_from_modelopt_state(
529
+ model, torch.load("modelopt_state.pt", weights_only=False)
530
+ ) # Restore modelopt state
531
+ model.load_state_dict(torch.load("model_weights.pt"), ...) # Load the model weights
532
+
533
+ If you want to restore the model weights and the modelopt state with saved scales, please use
534
+ :meth:`mto.restore()<modelopt.torch.opt.conversion.restore>`.
535
+
536
+ Args:
537
+ model: A model-like object. Can be an nn.Module, a model class type, or a tuple.
538
+ tuple must be of the form ``(model_cls,)`` or ``(model_cls, args)`` or
539
+ ``(model_cls, args, kwargs)``. Model will be initialized as
540
+ ``model_cls(*args, **kwargs)``.
541
+ modelopt_state: The modelopt state dict describing the modelopt modifications to the model. The
542
+ ``modelopt_state`` can be generated via
543
+ :meth:`mto.modelopt_state()<modelopt.torch.opt.conversion.modelopt_state>`.
544
+
545
+ Returns:
546
+ A modified model architecture based on the restored modifications with the unmodified
547
+ weights as stored in the provided ``model`` argument.
548
+
549
+ .. note::
550
+
551
+ Note that wrappers such as DistributedDataParallel are `not` supported during the restore
552
+ process. Please wrap the model after the restore process.
553
+ """
554
+ # initialize ModelLikeModule if needed.
555
+ model = model if isinstance(model, nn.Module) else ModelLikeModule(model)
556
+
557
+ # initialize state manager and load state
558
+ manager = ModeloptStateManager(model=model, init_state=True)
559
+ manager.load_state_dict(
560
+ modelopt_state["modelopt_state_dict"], modelopt_state["modelopt_version"]
561
+ )
562
+
563
+ # apply restore entrypoints for each of the modes
564
+ for i, (m, config, metadata) in enumerate(manager.modes_with_states()):
565
+ if i == 0:
566
+ model = _check_init_modellike(model, m)
567
+ model = m.restore(model, config, metadata)
568
+
569
+ # If the existing mode is empty, create an model instance from ModelLikeModule.
570
+ if not manager.has_state and isinstance(model, ModelLikeModule):
571
+ model = model.init_modellike()
572
+
573
+ # it cannot be a ModelLikeModule anymore at the end
574
+ assert not isinstance(model, ModelLikeModule), "Model must be a regular Module now!"
575
+
576
+ return model
577
+
578
+
579
+ def restore(model: ModelLike, f: str | os.PathLike | BinaryIO, **kwargs) -> nn.Module:
580
+ """Load the checkpoint, restore the modelopt model modifications, and load the model's weights.
581
+
582
+ Args:
583
+ model: A model-like object. Can be an nn.Module, a model class type, or a tuple.
584
+ tuple must be of the form ``(model_cls,)`` or ``(model_cls, args)`` or ``(model_cls, args, kwargs)``.
585
+ Model will be initialized as ``model_cls(*args, **kwargs)``.
586
+ f: Target file location generated by :meth:`mto.save()<modelopt.torch.opt.conversion.save>`.
587
+ **kwargs: additional args for ``torch.load()``.
588
+
589
+ Returns:
590
+ The model with original weights and stored architecture.
591
+
592
+ .. note::
593
+
594
+ Note that wrappers such as DistributedDataParallel are `not` supported during the restore
595
+ process. Please wrap the model after the restore process.
596
+ """
597
+ # initialize ModelLikeModule if needed.
598
+ model = model if isinstance(model, nn.Module) else ModelLikeModule(model)
599
+
600
+ # load checkpoint
601
+ kwargs.setdefault("map_location", "cpu")
602
+ kwargs.setdefault("weights_only", False)
603
+ objs = torch.load(f, **kwargs)
604
+
605
+ # restore model architecture
606
+ model_restored = restore_from_modelopt_state(model, objs["modelopt_state"])
607
+
608
+ # load weights from checkpoint
609
+ model_restored.load_state_dict(objs["model_state_dict"])
610
+
611
+ # it cannot be a ModelLikeModule anymore at the end
612
+ assert not isinstance(model_restored, ModelLikeModule), "Model must be a regular Module now!"
613
+
614
+ return model_restored
615
+
616
+
617
+ # TODO: add a generic export function that will apply all remaining export modes.
618
+ def export(model: nn.Module) -> nn.Module:
619
+ """Fully export the model to a regular model and finalize any model modifications."""
620
+ raise NotImplementedError