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,443 @@
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
+ """Quantization utilities."""
17
+
18
+ from collections import namedtuple
19
+ from collections.abc import Generator
20
+ from contextlib import ExitStack, contextmanager, nullcontext
21
+
22
+ import torch
23
+ import torch.nn as nn
24
+ import torch.nn.functional as F
25
+ from torch.distributed.fsdp import FSDPModule
26
+ from torch.distributed.tensor import Replicate
27
+
28
+ from modelopt.torch.utils import print_rank_0
29
+
30
+ __all__ = [
31
+ "EXPORT_MODE",
32
+ "convert_quantization_axis_to_reduce_axis",
33
+ "export_torch_mode",
34
+ "is_quantized",
35
+ "is_quantized_column_parallel_linear",
36
+ "is_quantized_linear",
37
+ "is_quantized_row_parallel_linear",
38
+ "reduce_amax",
39
+ "replace_function",
40
+ "weight_attr_names",
41
+ ]
42
+
43
+
44
+ def reduce_block_amax(input_tensor: torch.Tensor, block_sizes: dict):
45
+ """Computes the amax of the input tensor using block-based reduction for each dimension.
46
+
47
+ Args:
48
+ input_tensor (torch.Tensor): The input tensor.
49
+ block_sizes (dict): A dictionary specifying the block size for each dimension.
50
+ Example: `{-1: 128, -2: 128}` reduces over 2D blocks.
51
+
52
+ Returns:
53
+ torch.Tensor: The reduced tensor with amax computed per block.
54
+
55
+ Example:
56
+ Input Shape: [256, 512]
57
+ Block Sizes: {-1: 128, -2: 128}
58
+ Process:
59
+ - Block along last dim → Shape [256, 4, 128]
60
+ - Compute block-wise amax → Shape [256, 4]
61
+ - Block along second-to-last dim → Shape [2, 128, 4]
62
+ - Compute block-wise amax → Shape [2, 4]
63
+ """
64
+ with torch.no_grad():
65
+ amax = input_tensor.clone()
66
+
67
+ for dim, block_size in block_sizes.items():
68
+ # Convert negative dimensions to positive
69
+ dim = dim if dim >= 0 else len(amax.shape) + dim
70
+ assert amax.shape[dim] % block_size == 0, (
71
+ f"Tensor dimension {amax.shape[dim]}, {amax.shape[dim]} is not divisible by {block_size}"
72
+ )
73
+
74
+ # Compute new shape for blocking
75
+ outer_dim = amax.shape[dim] // block_size
76
+ new_shape = [
77
+ *list(amax.shape[:dim]),
78
+ outer_dim,
79
+ block_size,
80
+ *list(amax.shape[dim + 1 :]),
81
+ ]
82
+
83
+ # Reshape into blocks
84
+ amax = amax.reshape(new_shape)
85
+
86
+ # Reduce along the newly created block dimension
87
+ # Shift by 1 because we added an extra dimension
88
+ amax = reduce_amax(amax, dim + 1, keepdims=False, squeeze_scalar=False)
89
+
90
+ return amax
91
+
92
+
93
+ def reduce_block_padding(input: torch.Tensor, block_sizes: dict, pad_value: float = 0):
94
+ """Padding the input using block-based reduction for each dimension.
95
+
96
+ Args:
97
+ input_tensor (torch.Tensor): The input tensor.
98
+ block_sizes (dict): A dictionary specifying the block size for padding each dimension.
99
+ Example: `{-1: 128, -2: 128}` pads the input over 2D blocks.
100
+ """
101
+ with torch.no_grad():
102
+ padded_tensor = input
103
+ num_dims = padded_tensor.dim()
104
+
105
+ # Process each specified dimension independently
106
+ for dim, block in block_sizes.items():
107
+ # Convert negative dimension to positive index
108
+ pos_dim = dim if dim >= 0 else num_dims + dim
109
+
110
+ # Calculate how many elements are missing along that dimension
111
+ current_size = padded_tensor.size(pos_dim)
112
+ remainder = current_size % block
113
+ pad_amt = 0 if remainder == 0 else block - remainder
114
+
115
+ if pad_amt > 0:
116
+ # F.pad expects a pad tuple of length 2*num_dims.
117
+ pad = [0] * (2 * num_dims)
118
+ # For dimension pos_dim, the right padding is at index: (num_dims - 1 - pos_dim)*2 + 1.
119
+ pad_index = (num_dims - 1 - pos_dim) * 2
120
+ pad[pad_index + 1] = (
121
+ pad_amt # Set padding on the right side of the target dimension
122
+ )
123
+
124
+ padded_tensor = F.pad(padded_tensor, pad, value=pad_value)
125
+
126
+ return padded_tensor
127
+
128
+
129
+ def convert_quantization_axis_to_reduce_axis(input, axis):
130
+ """Convert the quantization axis to the reduce axis.
131
+
132
+ Args:
133
+ input (torch.Tensor): The input tensor.
134
+ axis (int, tuple, list of None): The quantization axis. None means per-tensor quantization.
135
+
136
+ Returns:
137
+ list: The axis to reduce. None suggests all dimensions should be reduced.
138
+ """
139
+ if axis is None:
140
+ return None
141
+ axis = axis if isinstance(axis, (list, tuple)) else [axis]
142
+ # Handle positive and negative axis.
143
+ reduce_axis = [i for i in range(input.dim()) if i not in axis and (i - input.dim()) not in axis]
144
+ return reduce_axis
145
+
146
+
147
+ @torch.no_grad()
148
+ def reduce_amax(input, axis=None, keepdims=True, squeeze_scalar=True):
149
+ """Compute the absolute maximum value of a tensor.
150
+
151
+ Reduces input_tensor along the dimensions given in axis. Unless keepdims is true,
152
+ the rank of the tensor is reduced by 1 for each entry in axis. If keepdims is true,
153
+ the reduced dimensions are retained with length 1.
154
+
155
+ .. note::
156
+ Gradient computation is disabled as this function is never meant learning reduces amax
157
+
158
+ Args:
159
+ input: Input tensor
160
+ axis: The dimensions to reduce. None or int or tuple of ints. If None (the default),
161
+ reduces all dimensions. Must be in the range [-rank(input_tensor), rank(input_tensor)).
162
+ keepdims: A boolean. If true, retains reduced dimensions with length 1. Default True
163
+
164
+ Returns:
165
+ The reduced tensor.
166
+ """
167
+ # A memory-efficient implementation that avoids copying input tensor
168
+ if axis is None:
169
+ max_val = torch.max(input)
170
+ min_val = torch.min(input)
171
+ output = torch.maximum(torch.abs(max_val), torch.abs(min_val))
172
+ else:
173
+ if isinstance(axis, int):
174
+ axis = (axis,)
175
+ max_val = torch.amax(input, dim=axis, keepdim=keepdims)
176
+ min_val = torch.amin(input, dim=axis, keepdim=keepdims)
177
+ output = torch.maximum(torch.abs(max_val), torch.abs(min_val))
178
+ if squeeze_scalar and output.numel() == 1:
179
+ output.squeeze_()
180
+ return output
181
+
182
+
183
+ def weight_attr_names(module: nn.Module) -> Generator[str, None, None]:
184
+ """Get the weight param attribute names in a converted module, non-recursive.
185
+
186
+ We consider the following two cases for each weight param attribute:
187
+ - The standard weight attribute (e.g. nn.Linear).
188
+ - The custom `weight_attr_name`. (e.g. Llama4TextExperts has weight attributes `gate_up_proj` and `down_proj`)
189
+ """
190
+ from .nn import SequentialQuantizer, TensorQuantizer
191
+
192
+ # the standard weight and quantizer case
193
+ weight = getattr(module, "weight", None)
194
+ weight_quantizer = getattr(module, "weight_quantizer", None)
195
+ if isinstance(weight, nn.Parameter) and isinstance(
196
+ weight_quantizer, (TensorQuantizer, SequentialQuantizer)
197
+ ):
198
+ yield "weight"
199
+
200
+ # other weight and quantizer case
201
+ for name, _ in module.named_parameters(recurse=False):
202
+ weight = getattr(module, name, None)
203
+ weight_quantizer = getattr(module, f"{name}_weight_quantizer", None)
204
+ if isinstance(weight, nn.Parameter) and isinstance(
205
+ weight_quantizer, (TensorQuantizer, SequentialQuantizer)
206
+ ):
207
+ yield name
208
+
209
+
210
+ """The whole set of quantizer related attribute names for a given weight name."""
211
+ QuantizerAttrNames = namedtuple(
212
+ "QuantizerAttrNames",
213
+ (
214
+ "weight_quantizer",
215
+ "input_quantizer",
216
+ "output_quantizer",
217
+ "weight_scale",
218
+ "weight_scale_2",
219
+ "input_scale",
220
+ "output_scale",
221
+ ),
222
+ )
223
+
224
+
225
+ def quantizer_attr_names(weight_name: str = "weight") -> QuantizerAttrNames:
226
+ """Get all the quantizer related attribute names for a given weight name."""
227
+ prefix = f"{weight_name}_" if weight_name != "weight" else ""
228
+ return QuantizerAttrNames(
229
+ weight_quantizer=f"{prefix}weight_quantizer",
230
+ input_quantizer=f"{prefix}input_quantizer",
231
+ output_quantizer=f"{prefix}output_quantizer",
232
+ weight_scale=f"{prefix}weight_scale",
233
+ weight_scale_2=f"{prefix}weight_scale_2",
234
+ input_scale=f"{prefix}input_scale",
235
+ output_scale=f"{prefix}output_scale",
236
+ )
237
+
238
+
239
+ def is_quantized(module):
240
+ """Check if a module is quantized."""
241
+ from .nn import TensorQuantizer
242
+
243
+ return any(isinstance(_module, TensorQuantizer) for _module in module.modules())
244
+
245
+
246
+ def is_quantized_linear(module):
247
+ """Check if a module is a quantized linear module."""
248
+ from .nn import QuantModule, TensorQuantizer
249
+
250
+ return (
251
+ isinstance(module, QuantModule)
252
+ and isinstance(getattr(module, "input_quantizer", None), TensorQuantizer)
253
+ and hasattr(module, "weight_quantizer")
254
+ and getattr(module, "weight", None) is not None
255
+ and module.weight.dim() == 2
256
+ )
257
+
258
+
259
+ def is_quantized_column_parallel_linear(module):
260
+ """Check if a module is a quantized column parallel linear module."""
261
+ return is_quantized_linear(module) and getattr(module, "_is_column_parallel", False)
262
+
263
+
264
+ def is_quantized_row_parallel_linear(module):
265
+ """Check if a module is a quantized row parallel linear module."""
266
+ return is_quantized_linear(module) and getattr(module, "_is_row_parallel", False)
267
+
268
+
269
+ def is_quantized_parallel_linear(module):
270
+ """Check if a module is a quantized parallel linear module."""
271
+ return is_quantized_column_parallel_linear(module) or is_quantized_row_parallel_linear(module)
272
+
273
+
274
+ @contextmanager
275
+ def calibrate_with_adapters(model, args):
276
+ """Disables LoRA adapters during calibration, then re-enables them afterward."""
277
+ is_lora = getattr(args, "lora", None)
278
+ if is_lora:
279
+ print_rank_0("Disabling LoRA adapters during calibration...")
280
+ model.disable_adapters()
281
+
282
+ yield
283
+
284
+ if is_lora:
285
+ print_rank_0("Enabling LoRA adapters after calibration...")
286
+ model.enable_adapters()
287
+
288
+
289
+ def disable_lora_quantizers_in_config(config, layers):
290
+ """Turns off input, weight, and output quantizers for LoRA weights and LoRALinear layers in config."""
291
+ config["quant_cfg"]["*lora*"] = {"enable": False}
292
+ for layer in layers:
293
+ config["quant_cfg"][f"*{layer}.input_quantizer"] = {"enable": False}
294
+ config["quant_cfg"][f"*{layer}.weight_quantizer"] = {"enable": False}
295
+ config["quant_cfg"][f"*{layer}.output_quantizer"] = {"enable": False}
296
+ return config
297
+
298
+
299
+ @contextmanager
300
+ def replace_function(package, name, new_func):
301
+ """Replace a function with a new one within a context."""
302
+ old_func = getattr(package, name)
303
+ setattr(package, name, new_func)
304
+ setattr(package, "_" + name, old_func)
305
+ yield
306
+ setattr(package, name, old_func)
307
+ delattr(package, "_" + name)
308
+
309
+
310
+ @contextmanager
311
+ def multi_context(*cms):
312
+ """Context manager enabling variable number of context managers."""
313
+ with ExitStack() as stack:
314
+ yield [stack.enter_context(cls) for cls in cms]
315
+
316
+
317
+ EXPORT_MODE: bool = False
318
+
319
+
320
+ @contextmanager
321
+ def export_torch_mode():
322
+ """Context manager enabling the export mode."""
323
+ global EXPORT_MODE
324
+ original_value = EXPORT_MODE
325
+ EXPORT_MODE = True
326
+ try:
327
+ yield
328
+ finally:
329
+ EXPORT_MODE = original_value
330
+
331
+
332
+ def is_torch_export_mode():
333
+ """Check whether in the context of exporting model to torch."""
334
+ return EXPORT_MODE
335
+
336
+
337
+ def is_pow2(n):
338
+ """Check if a number is the power of 2."""
339
+ return (n != 0) and (n & (n - 1) == 0)
340
+
341
+
342
+ def _get_fsdp2_mesh(module: nn.Module):
343
+ """Get the mesh info of the model."""
344
+ try:
345
+ from torch.distributed._composable_state import _get_module_state
346
+ except ImportError:
347
+ return None
348
+
349
+ fsdp_state = _get_module_state(module)
350
+ if (
351
+ fsdp_state._fsdp_param_group
352
+ and fsdp_state._fsdp_param_group.post_forward_mesh_info is not None
353
+ ):
354
+ return fsdp_state._fsdp_param_group.post_forward_mesh_info.mesh
355
+
356
+
357
+ def _get_enclosing_fsdp_module(module: nn.Module, root_model: nn.Module):
358
+ """Get the enclosing FSDP module for a given module."""
359
+ if isinstance(module, FSDPModule):
360
+ return module
361
+
362
+ name_to_module = dict(root_model.named_modules())
363
+ target_module_name = next((name for name, m in name_to_module.items() if m is module), None)
364
+
365
+ if target_module_name is None:
366
+ raise ValueError(f"Module {module} not found in the root model {root_model}.")
367
+
368
+ current_name = target_module_name
369
+ while "." in current_name:
370
+ parent_name = ".".join(current_name.split(".")[:-1])
371
+ parent_module = name_to_module.get(parent_name)
372
+ if parent_module and isinstance(parent_module, FSDPModule):
373
+ return parent_module
374
+ current_name = parent_name
375
+
376
+ if isinstance(root_model, FSDPModule):
377
+ return root_model
378
+
379
+
380
+ @contextmanager
381
+ def fsdp2_weight_access_and_writeback_context(module: nn.Module, root_model: nn.Module):
382
+ """Context manager for FSDP2 weight access and writeback.
383
+
384
+ Note this context will gather the weight across FSDP/HSDP shards. If TP is implemented with DTensor,
385
+ the weight will be a local tensor of the TP DTensor under this context.
386
+ """
387
+ assert isinstance(root_model, torch.distributed.fsdp.FSDPModule), "We only support FSDP2"
388
+
389
+ assert not hasattr(module, "_hf_hook"), "We dont support FSDP2 with HF accelerate hooks"
390
+ assert isinstance(module.weight, torch.distributed.tensor.DTensor)
391
+ fsdp_module = _get_enclosing_fsdp_module(module, root_model)
392
+ assert fsdp_module is not None, "Module is not wrapped by FSDP"
393
+ fsdp_device_mesh = _get_fsdp2_mesh(fsdp_module)
394
+ fsdp_dim = fsdp_device_mesh.ndim
395
+
396
+ original_placements = module.weight.placements
397
+ original_device_mesh = module.weight.device_mesh
398
+ original_weight = module.weight
399
+ # Assuming the first fsdp_dim dimensions are for FSDP/HSDP, we only collect the tensor over FSDP/HSDP dimension,
400
+ # the TP will be handled by the TP reduction.
401
+ if fsdp_dim != original_device_mesh.ndim:
402
+ assert fsdp_device_mesh.mesh_dim_names == original_device_mesh.mesh_dim_names[:fsdp_dim], (
403
+ "FSDP2 mesh should be a slice of DTesnor's device mesh."
404
+ )
405
+
406
+ weight_collected = original_weight.redistribute(
407
+ placements=[Replicate()] * fsdp_dim + list(original_placements[fsdp_dim:]),
408
+ device_mesh=original_device_mesh,
409
+ )
410
+ new_weight = nn.Parameter(weight_collected.to_local())
411
+ module._parameters["weight"] = new_weight
412
+
413
+ yield
414
+
415
+ original_weight.to_local().data.copy_(
416
+ weight_collected.redistribute(
417
+ placements=original_placements, device_mesh=original_device_mesh
418
+ ).to_local()
419
+ )
420
+ module._parameters["weight"] = original_weight
421
+
422
+
423
+ @contextmanager
424
+ def enable_weight_access_and_writeback(module, root_model):
425
+ """Enable weight access and writeback for a module.
426
+
427
+ Useful for modules with weight not intact such as Linear layer in FSDP wrapped model or
428
+ HF accelerate CPU off-loaded models.
429
+ """
430
+ if _get_enclosing_fsdp_module(module, root_model) is not None:
431
+ context = fsdp2_weight_access_and_writeback_context(module, root_model)
432
+ elif is_quantized_parallel_linear(module) and hasattr(module, "_hf_tp_plan"):
433
+ # HF transformers TP sharded linear layer
434
+ context = module.enable_weight_access_and_writeback()
435
+ elif hasattr(module, "_hf_hook"):
436
+ from .plugins.accelerate import weight_access_and_writeback_context
437
+
438
+ context = weight_access_and_writeback_context(module)
439
+ else:
440
+ context = nullcontext()
441
+
442
+ with context:
443
+ yield
@@ -0,0 +1,19 @@
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
+ """API for sparsification algorithms."""
17
+
18
+ from . import mode, module, plugins
19
+ from .sparsification import *
@@ -0,0 +1,51 @@
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
+ """Default configurations for sparsity modes."""
17
+
18
+ from pydantic import create_model
19
+
20
+ from modelopt.torch.opt.config import ModeloptBaseConfig, get_kwargs_for_create_model_with_rules
21
+
22
+ from .module import SpDMRegistry
23
+
24
+ SparseMagnitudeConfig = create_model(
25
+ "SparseMagnitudeConfig",
26
+ **get_kwargs_for_create_model_with_rules(
27
+ registry=SpDMRegistry,
28
+ default_rules={
29
+ "nn.Linear": {"*": {}, "*lm_head*": None},
30
+ "nn.Conv2d": {"*": {}, "*lm_head*": None},
31
+ },
32
+ doc='Configuration for the ``"sparse_magnitude"`` mode.',
33
+ ),
34
+ )
35
+
36
+
37
+ SparseGPTConfig = create_model(
38
+ "SparseGPTConfig",
39
+ **get_kwargs_for_create_model_with_rules(
40
+ registry=SpDMRegistry,
41
+ default_rules={
42
+ "nn.Linear": {"*": {}, "*lm_head*": None},
43
+ "nn.Conv2d": {"*": {}, "*lm_head*": None},
44
+ },
45
+ doc='Configuration for the ``"sparse_gpt"`` mode.',
46
+ ),
47
+ )
48
+
49
+
50
+ class ExportSparseConfig(ModeloptBaseConfig):
51
+ """Configuration (empty!) for the ``"export_sparse"`` mode."""
@@ -0,0 +1,148 @@
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
+ """Magnitude-base sparsity inspired by NVIDIA ASP (Automatic SParsity)."""
17
+
18
+ import re
19
+ import warnings
20
+ from itertools import permutations
21
+
22
+ import torch
23
+ import torch.nn as nn
24
+
25
+ from .module import SparseModule
26
+ from .searcher import BaseSparseSearcher
27
+
28
+
29
+ def get_nmprune_info(pattern: str) -> tuple[bool, int, int]:
30
+ """Gets the n:m sparsity pattern information from a given string."""
31
+ nm_prune = re.search(r"(\d+):(\d+) sparsity", pattern)
32
+ if nm_prune is not None:
33
+ n, m = map(int, nm_prune.groups())
34
+ return nm_prune is not None, n, m
35
+ return False, 0, 0
36
+
37
+
38
+ def fill(x):
39
+ """Calculates the ratio of non-zero elements in a tensor."""
40
+ return float(x.nonzero().size(0)) / torch.numel(x)
41
+
42
+
43
+ def reshape_1d(matrix, m):
44
+ """Reshapes a given matrix into m-dimensional vectors: (h,w) -> (hw/m, m)."""
45
+ if matrix.shape[1] % m > 0:
46
+ new_cols = matrix.shape[1] + (m - matrix.shape[1] % m)
47
+ mat = matrix.new_empty(matrix.shape[0], new_cols).fill_(0)
48
+ mat[:, : matrix.shape[1]] = matrix
49
+
50
+ return mat.view(-1, m), mat.shape
51
+ else:
52
+ return matrix.view(-1, m), matrix.shape
53
+
54
+
55
+ def compute_valid_1d_patterns(m, n):
56
+ """Computes all possible m:n patterns in a 1D vector.
57
+
58
+ The function generates a tensor of size m with n ones and (m-n) zeros.
59
+ It then generates all permutations of this tensor, removes duplicates,
60
+ and returns the unique patterns as a tensor.
61
+ """
62
+ patterns = torch.zeros(m)
63
+ patterns[:n] = 1
64
+ valid_patterns = torch.tensor(list(set(permutations(patterns.tolist()))))
65
+ return valid_patterns
66
+
67
+
68
+ def mn_1d_best(matrix, m, n):
69
+ """Finds the best m:n pattern in a given matrix.
70
+
71
+ The function computes all possible m:n patterns and selects the one
72
+ that maximizes the sum of non-masked weights in the matrix. The selected
73
+ pattern is then used to create a mask for the matrix.
74
+ """
75
+ patterns = compute_valid_1d_patterns(m, n).to(matrix.device)
76
+
77
+ # Find the best m:n pattern (sum of non-masked weights).
78
+ mask = torch.IntTensor(matrix.shape).fill_(1).view(-1, m)
79
+ mat, _ = reshape_1d(matrix, m)
80
+ pmax = torch.argmax(torch.matmul(mat.abs(), patterns.t()), dim=1)
81
+ mask[:] = patterns[pmax[:]]
82
+ mask = mask.view(matrix.shape)
83
+ return mask
84
+
85
+
86
+ def m4n2_1d(mat):
87
+ """Finds the best 2:4 pattern in a given matrix."""
88
+ return mn_1d_best(mat, 4, 2)
89
+
90
+
91
+ def create_asp_mask(tensor: nn.Parameter, pattern: str) -> torch.BoolTensor:
92
+ """Creates a mask for a given tensor based on a specified sparse pattern.
93
+
94
+ The function reshapes the tensor and applies the specified pattern to create a sparse mask.
95
+ The default pattern is m4n2_1d, which finds the best 2:4 sparsity pattern in the tensor.
96
+ """
97
+ pattern_method_lut = {BaseSparseSearcher._pattern_2_4: m4n2_1d}
98
+ if pattern not in pattern_method_lut:
99
+ raise NotImplementedError(f"Unsupported pattern {pattern} for ASP sparsity")
100
+ func = pattern_method_lut[pattern]
101
+
102
+ shape = tensor.shape
103
+ tensor.type()
104
+ t = tensor.float().contiguous()
105
+
106
+ # 1d-tensor
107
+ if len(shape) == 1:
108
+ t = t.view(1, shape[0])
109
+ mask = func(t)
110
+ # 2d-tensor (K, C)
111
+ elif len(shape) == 2:
112
+ # linear
113
+ t = t.view(shape[0], shape[1])
114
+ mask = func(t)
115
+ # 3d-tensor (K, C, R)
116
+ elif len(shape) == 3:
117
+ # 1d convs
118
+ t = t.permute(0, 2, 1).contiguous().view(shape[0] * shape[2], shape[1])
119
+ mask = func(t)
120
+ mask = mask.view(shape[0], shape[2], shape[1]).permute(0, 2, 1).contiguous()
121
+ # 4d-tensor (K, C, R, S)
122
+ elif len(shape) == 4:
123
+ # 2d convs
124
+ t = t.permute(2, 3, 0, 1).contiguous().view(shape[2] * shape[3] * shape[0], shape[1])
125
+ mask = func(t)
126
+ mask = mask.view(shape[2], shape[3], shape[0], shape[1]).permute(2, 3, 0, 1).contiguous()
127
+
128
+ return mask.view(shape).to(dtype=torch.bool)
129
+
130
+
131
+ class MagnitudeSearcher(BaseSparseSearcher):
132
+ """Searcher for magnitude-based sparsity."""
133
+
134
+ def _check_weight_size(self, weight: torch.nn.Parameter, mod_name: str) -> bool:
135
+ """Check if the weight size is supported."""
136
+ # rules from ASP
137
+ if weight.size(0) % 8 != 0 or weight.size(1) % 16 != 0:
138
+ warnings.warn(
139
+ f"Skipping sparsifying {mod_name} of size={weight.size()!s} and"
140
+ f" type={weight.dtype!s} for sparsity"
141
+ )
142
+ return False
143
+
144
+ return True
145
+
146
+ def _compute_mask(self, module: SparseModule) -> torch.BoolTensor:
147
+ """Compute the mask (and weight update) for the given module."""
148
+ return create_asp_mask(module.weight, self.config["pattern"])