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,808 @@
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
+ """Entrypoints for AutoNAS mode."""
17
+
18
+ import copy
19
+ import hashlib
20
+ import json
21
+ import warnings
22
+ from abc import ABC, abstractmethod
23
+ from collections import defaultdict, deque
24
+ from functools import partial
25
+ from typing import Any
26
+
27
+ import torch
28
+ import torch.nn as nn
29
+ import tqdm
30
+ from pydantic import create_model
31
+ from torch.nn.modules.batchnorm import _BatchNorm
32
+
33
+ from modelopt.torch.opt.config import (
34
+ ModeloptBaseConfig,
35
+ ModeloptField,
36
+ get_kwargs_for_create_model_with_rules,
37
+ )
38
+ from modelopt.torch.opt.conversion import ApplyModeError, ModelLikeModule
39
+ from modelopt.torch.opt.mode import (
40
+ ConvertEntrypoint,
41
+ ConvertReturnType,
42
+ MetadataDict,
43
+ ModeDescriptor,
44
+ RestoreEntrypoint,
45
+ UpdateEntrypoint,
46
+ )
47
+ from modelopt.torch.opt.searcher import BaseSearcher, ForwardLoop, SearchConfig, SearchStateDict
48
+ from modelopt.torch.opt.utils import is_configurable
49
+ from modelopt.torch.utils import (
50
+ compare_dict,
51
+ get_model_attributes,
52
+ is_channels_last,
53
+ num2hrb,
54
+ random,
55
+ run_forward_loop,
56
+ stats,
57
+ torch_detach,
58
+ torch_to,
59
+ unwrap_model,
60
+ )
61
+
62
+ from .algorithms import ConstraintsFunc, get_constraints_func
63
+ from .conversion import NASModeRegistry
64
+ from .patch import PatchData, PatchManager, _modelopt_eval_recursion_guard, prep_for_eval
65
+ from .registry import DMRegistry
66
+ from .search_space import SearchSpace, generate_search_space
67
+ from .utils import MODELOPT_BN_CALIB_ITERS, MODELOPT_QUEUE_MAXLEN, get_subnet_config, sample, select
68
+
69
+ __all__ = [
70
+ "AutoNASConfig",
71
+ "AutoNASModeDescriptor",
72
+ "AutoNASPatchManager",
73
+ "EvolveSearcher",
74
+ "ExportConfig",
75
+ "ExportModeDescriptor",
76
+ "IterativeSearcher",
77
+ "RandomSearcher",
78
+ "convert_autonas_searchspace",
79
+ "convert_searchspace",
80
+ "export_searchspace",
81
+ "restore_autonas_searchspace",
82
+ "restore_export",
83
+ "restore_searchspace",
84
+ "update_autonas_metadata",
85
+ ]
86
+
87
+
88
+ def _get_ratio_list():
89
+ return (0.5, 0.67, 1.0)
90
+
91
+
92
+ def _conv_config():
93
+ return {
94
+ "channels_ratio": _get_ratio_list(),
95
+ "kernel_size": (),
96
+ "channel_divisor": 32,
97
+ }
98
+
99
+
100
+ def _norm_lin_config():
101
+ return {
102
+ "features_ratio": _get_ratio_list(),
103
+ "feature_divisor": 32,
104
+ }
105
+
106
+
107
+ AutoNASConfig: type[ModeloptBaseConfig] = create_model(
108
+ "AutoNASConfig",
109
+ **get_kwargs_for_create_model_with_rules(
110
+ registry=DMRegistry,
111
+ default_rules={
112
+ "nn.Conv1d": _conv_config(),
113
+ "nn.Conv2d": _conv_config(),
114
+ "nn.Conv3d": _conv_config(),
115
+ "nn.ConvTranspose1d": _conv_config(),
116
+ "nn.ConvTranspose2d": _conv_config(),
117
+ "nn.ConvTranspose3d": _conv_config(),
118
+ "nn.Linear": _norm_lin_config(),
119
+ "nn.BatchNorm1d": _norm_lin_config(),
120
+ "nn.BatchNorm2d": _norm_lin_config(),
121
+ "nn.BatchNorm3d": _norm_lin_config(),
122
+ "nn.SyncBatchNorm": _norm_lin_config(),
123
+ "nn.InstanceNorm1d": _norm_lin_config(),
124
+ "nn.InstanceNorm2d": _norm_lin_config(),
125
+ "nn.InstanceNorm3d": _norm_lin_config(),
126
+ "nn.LayerNorm": _norm_lin_config(),
127
+ "nn.GroupNorm": {k: v for k, v in _conv_config().items() if k != "kernel_size"},
128
+ "nn.Sequential": {"min_depth": 0},
129
+ },
130
+ doc='Configuration for the ``"autonas"`` mode.',
131
+ ),
132
+ )
133
+
134
+
135
+ class ExportConfig(ModeloptBaseConfig):
136
+ """Configuration for the export mode.
137
+
138
+ This mode is used to export a model after NAS search.
139
+ """
140
+
141
+ strict: bool = ModeloptField(
142
+ default=True,
143
+ title="Strict export",
144
+ description="Enforces that the subnet configuration must exactly match during export.",
145
+ )
146
+
147
+ calib: bool = ModeloptField(
148
+ default=False,
149
+ title="Calibration",
150
+ description="Whether to calibrate the subnet before exporting.",
151
+ )
152
+
153
+
154
+ class AutoNASPatchManager(PatchManager):
155
+ """A class to handle the monkey patching of the model for automode."""
156
+
157
+ def _get_default_patch_data(self) -> PatchData:
158
+ """Return the default patch data of the model."""
159
+ return {
160
+ "queue": deque(maxlen=MODELOPT_QUEUE_MAXLEN), # queue for reset_bn data batches cache
161
+ "fill": False, # force push to queue if set to True
162
+ "count": 0, # internal count for number of forward passes
163
+ "train_count_since_sample": MODELOPT_BN_CALIB_ITERS, # step count since last sample
164
+ "autosampled": False, # indicator whether current sub-net was auto-sampled
165
+ }
166
+
167
+ def _hook_post_eval(self, forward_loop: ForwardLoop | None = None) -> None:
168
+ """Optional hook that is called after eval() (or train(False)) to calibrate the model."""
169
+ # get the model and patch_data reference
170
+ model = self._model
171
+ patch_data = self.patch_data
172
+
173
+ # automode-related attributes
174
+ fill = patch_data["fill"]
175
+
176
+ # BN calibration related in autonas/fastnas mode
177
+ if is_configurable(model):
178
+ # indicates that model is being trained in which case we want max sub-net for eval
179
+ if patch_data["autosampled"]:
180
+ sample(model, sample_func=max)
181
+
182
+ # check current count since last sample operation
183
+ count_since_sample = patch_data["train_count_since_sample"]
184
+
185
+ # logic to determine whether and how to calibrate BN statistics
186
+ queue = patch_data["queue"]
187
+ if forward_loop is None and count_since_sample < MODELOPT_BN_CALIB_ITERS:
188
+ if len(queue) < queue.maxlen and self._is_modelopt_queue_needed(model):
189
+ warnings.warn(
190
+ "modelopt data queue not filled! Inference results can be inaccurate."
191
+ )
192
+ else:
193
+ forward_loop = partial(
194
+ run_forward_loop,
195
+ data_loader=queue,
196
+ collect_func=lambda x: x,
197
+ )
198
+ elif forward_loop is not None:
199
+ # in this case we want to fill up the queue since new data is provided.
200
+ patch_data["fill"] = True
201
+
202
+ # call reset_bn in eval mode
203
+ if forward_loop is not None:
204
+ with _modelopt_eval_recursion_guard(model):
205
+ self._reset_bn(model, forward_loop)
206
+ patch_data["train_count_since_sample"] = MODELOPT_BN_CALIB_ITERS
207
+
208
+ # resetting modelopt_fill
209
+ patch_data["fill"] = fill
210
+
211
+ @staticmethod
212
+ def _is_modelopt_queue_needed(model) -> bool:
213
+ return any(isinstance(m, _BatchNorm) and m.track_running_stats for m in model.modules())
214
+
215
+ @classmethod
216
+ def _reset_bn(cls, model: nn.Module, forward_loop: ForwardLoop):
217
+ """Calibrate BN statistics by the provided data loader.
218
+
219
+ Args:
220
+ model: The model to be calibrated
221
+ forward_loop: A ``Callable`` that takes a model as input and runs a pre-defined forward
222
+ loop on it using data that is suitable for BN calibration.
223
+ """
224
+ # skip if there is no batch normalization layer in the network that
225
+ # requires tracking running stats
226
+ if not cls._is_modelopt_queue_needed(model):
227
+ return
228
+
229
+ is_training = model.training
230
+ momentum = {}
231
+
232
+ # modify all batch norms to track cumulative moving average
233
+ for name, m in model.named_modules():
234
+ if isinstance(m, _BatchNorm):
235
+ m.reset_running_stats()
236
+ m.train()
237
+ momentum[name] = m.momentum
238
+ m.momentum = None
239
+
240
+ # do some forward passes to track BN statistics
241
+ with torch.no_grad():
242
+ forward_loop(model)
243
+
244
+ # reset network into its original state
245
+ model.train(is_training)
246
+ for name, m in model.named_modules():
247
+ if name in momentum:
248
+ m.momentum = momentum[name]
249
+
250
+ def _hook_pre_sample(self) -> None:
251
+ """Optional hook to be called before sample-related operations (sample & select)."""
252
+ patch_data = self.patch_data_or_empty
253
+ patch_data["train_count_since_sample"] = 0
254
+ patch_data["autosampled"] = False
255
+
256
+ @property
257
+ def sample_during_training(self) -> bool:
258
+ """Indicates whether we should sample a new subnet during training."""
259
+ return True
260
+
261
+ def _hook_pre_forward(self, *args, **kwargs) -> None:
262
+ """Optional hook that is called before the original forward function is called."""
263
+ patch_data = self.patch_data
264
+ mod = self._model
265
+ if not (mod.training or patch_data["fill"]):
266
+ return
267
+
268
+ # run sample step and update metadata related to sampling
269
+ def process_data(data):
270
+ return torch_to(torch_detach(data), device="cpu", non_blocking=True)
271
+
272
+ if mod.training or patch_data["fill"]:
273
+ # sample new model configuration in training mode and indicate it was auto-sampled
274
+ if mod.training:
275
+ if self.sample_during_training:
276
+ sample(mod)
277
+ patch_data["autosampled"] = True
278
+ patch_data["train_count_since_sample"] += 1 # update train mode count
279
+ # push detached CPU copy of data to queue (once full only update periodically)
280
+ queue = patch_data["queue"]
281
+ if len(queue) < queue.maxlen or patch_data["count"] % 100 == 0 or patch_data["fill"]:
282
+ queue.append((*process_data(args), process_data(kwargs)))
283
+ patch_data["count"] += 1
284
+
285
+
286
+ class IterativeSearcher(BaseSearcher, ABC):
287
+ """Base class for iterative search algorithms."""
288
+
289
+ iter_num: int
290
+ num_satisfied: int
291
+ constraints_func: ConstraintsFunc
292
+ candidate: dict[str, Any]
293
+ best: SearchStateDict
294
+ samples: dict[str, Any]
295
+ history: dict[str, Any]
296
+ best_history: dict[str, Any]
297
+
298
+ @property
299
+ def default_search_config(self) -> SearchConfig:
300
+ """Get the default config for the searcher."""
301
+ return {
302
+ **super().default_search_config,
303
+ "num_iters": 5000,
304
+ "max_iter_data_loader": 50, # plenty to calibrate BN statistics
305
+ }
306
+
307
+ @property
308
+ def default_state_dict(self) -> SearchStateDict:
309
+ """Return default state dict."""
310
+ return {
311
+ "iter_num": 0,
312
+ "num_satisfied": 0,
313
+ "candidate": {},
314
+ "best": {"metric": -float("inf"), "constraints": None},
315
+ "samples": {},
316
+ "history": {"metric": [], "constraints": defaultdict(list)},
317
+ "best_history": {"iter_num": [], "candidates": []},
318
+ }
319
+
320
+ def sanitize_search_config(self, config: SearchConfig | None) -> SearchConfig:
321
+ """Sanitize the search config dict."""
322
+ config = super().sanitize_search_config(config)
323
+ assert config["score_func"] is not None, "Please provide `score_func`!"
324
+ return config
325
+
326
+ def _sample(self) -> dict[str, Any]:
327
+ return {"config": sample(self.model)}
328
+
329
+ @abstractmethod
330
+ def sample(self) -> dict[str, Any]:
331
+ """Sample and select new sub-net configuration and return configuration."""
332
+ raise NotImplementedError
333
+
334
+ def before_search(self) -> None:
335
+ """Ensure that the model is actually configurable and ready for eval."""
336
+ super().before_search()
337
+
338
+ # Initialize the constraints functor
339
+ self.constraints_func = get_constraints_func(
340
+ self.model, self.constraints, self.dummy_input, deployment=self.deployment
341
+ )
342
+
343
+ # Set the model to max subnet and put model into eval mode with potential calibaration
344
+ sample(self.model, max)
345
+ prep_for_eval(self.model, self.forward_loop)
346
+
347
+ # check for configurability of the models; otherwise we don't need to run the search.
348
+ if not is_configurable(self.model):
349
+ warnings.warn(
350
+ "Provided model does not contain configurable hparams with multiple choices! "
351
+ "Running only one iteration."
352
+ )
353
+ self.config["num_iters"] = 1
354
+
355
+ # do a sanity check and profile the constraints
356
+ # This way we also fill up the interpolation table for latency with min, centroid, max!
357
+ from .algorithms import profile # TODO: hack until we refactor files
358
+
359
+ profile(
360
+ self.model,
361
+ constraints=self.constraints_func,
362
+ strict=True,
363
+ verbose=self.config["verbose"],
364
+ use_centroid=True,
365
+ )
366
+
367
+ def run_search(self) -> None:
368
+ """Run iterative search loop."""
369
+ num_iters = self.config["num_iters"]
370
+ verbose = self.config["verbose"]
371
+
372
+ # run search loop
373
+ if verbose:
374
+ pbar = tqdm.trange(
375
+ self.iter_num,
376
+ num_iters,
377
+ initial=self.iter_num,
378
+ total=num_iters,
379
+ position=0,
380
+ leave=True,
381
+ )
382
+
383
+ for self.iter_num in range(self.iter_num, num_iters):
384
+ self.before_step()
385
+ self.run_step()
386
+ self.after_step()
387
+
388
+ if verbose:
389
+ info = {
390
+ "num_satisfied": self.num_satisfied,
391
+ "metric": self.candidate["metric"],
392
+ "constraints": self.candidate["constraints"],
393
+ "best_subnet_metric": self.best["metric"],
394
+ "best_subnet_constraints": self.best["constraints"],
395
+ }
396
+
397
+ # display the full stats only once a while
398
+ if len(self.history["metric"]) == 100:
399
+ info["metric/stats"] = stats(self.history["metric"])
400
+ info["constraints/stats"] = {
401
+ name: stats(vals) for name, vals in self.history["constraints"].items()
402
+ }
403
+ self.history["metric"].clear()
404
+ self.history["constraints"].clear()
405
+
406
+ def _recursive_format(obj, fmt):
407
+ if isinstance(obj, float):
408
+ return num2hrb(obj)
409
+ if isinstance(obj, dict):
410
+ return {k: _recursive_format(v, fmt) for k, v in obj.items()}
411
+ return obj
412
+
413
+ pbar.update()
414
+ info = _recursive_format(info, fmt="{:.4g}")
415
+ pbar.set_description(f"[num_satisfied] = {info['num_satisfied']}")
416
+
417
+ if self.early_stop():
418
+ break
419
+
420
+ if verbose:
421
+ pbar.close()
422
+ print(f"[best_subnet_constraints] = {info['best_subnet_constraints']}")
423
+
424
+ def after_search(self) -> None:
425
+ """Select best model."""
426
+ super().after_search()
427
+ select(self.model, self.best["config"])
428
+ # if self.has_score:
429
+ # final_score = self.eval_score(silent=False)
430
+ # print(f"Final score for the searched model: {final_score}")
431
+
432
+ def before_step(self) -> None:
433
+ """Run before each iterative step."""
434
+
435
+ def run_step(self) -> None:
436
+ """The main routine of each iterative step."""
437
+ # sample and select the candidate
438
+ self.candidate = self.sample()
439
+
440
+ # obtain the independent config and hparams
441
+ # TODO: consider whether we want to eventually bring back "active_config" from omnimizer
442
+ # to improve the efficiency of the search and avoid redundant sampling. This was used to
443
+ # avoid testing two architectures that only differed in modules that were not active, i.e.,
444
+ # due to reduced depth.
445
+ independent_config = self._configurable_config(self.model)
446
+
447
+ # serialize and hash the candidate
448
+ buffer = json.dumps({"config": independent_config}, sort_keys=True)
449
+ ckey = hashlib.sha256(buffer.encode()).hexdigest()
450
+
451
+ if ckey not in self.samples:
452
+ # check constraints
453
+ self.candidate["is_satisfied"], self.candidate["constraints"] = self.constraints_func()
454
+
455
+ # evaluate the metric
456
+ if self.candidate["is_satisfied"]:
457
+ self.candidate["metric"] = self.eval_score()
458
+ else:
459
+ self.candidate["metric"] = -float("inf")
460
+
461
+ self.samples[ckey] = copy.deepcopy(self.candidate)
462
+ else:
463
+ self.candidate = copy.deepcopy(self.samples[ckey])
464
+
465
+ self.num_satisfied += int(self.candidate["is_satisfied"])
466
+ is_best = self.candidate["metric"] >= self.best["metric"] or self.iter_num == 0
467
+ self.candidate["is_best"] = is_best
468
+
469
+ # update the stats if satisfied
470
+ if self.candidate["is_satisfied"]:
471
+ self.history["metric"].append(self.candidate["metric"])
472
+ for name, val in self.candidate["constraints"].items():
473
+ self.history["constraints"][name].append(val)
474
+
475
+ # update the best if necessary
476
+ if is_best:
477
+ self.best = copy.deepcopy(self.candidate)
478
+ self.best_history["iter_num"].append(self.iter_num)
479
+ self.best_history["candidates"].append(self.best)
480
+
481
+ # save the state dict
482
+ if self.iter_num % 100 == 0 or self.iter_num == self.config["num_iters"] - 1 or is_best:
483
+ self.save_search_checkpoint()
484
+
485
+ def after_step(self) -> None:
486
+ """Run after each iterative step."""
487
+
488
+ def early_stop(self) -> bool:
489
+ """Check if we should early stop the search if possible."""
490
+ return False
491
+
492
+ def _configurable_config(self, model) -> dict[str, Any]:
493
+ """Returns the config dict of the configurable hyperparameters of the model."""
494
+ return get_subnet_config(model, configurable=True)
495
+
496
+
497
+ class RandomSearcher(IterativeSearcher):
498
+ """An iterative searcher that samples subnets randomly."""
499
+
500
+ def sample(self) -> dict[str, Any]:
501
+ """Random sample new subset during each steo."""
502
+ return self._sample()
503
+
504
+
505
+ class EvolveSearcher(IterativeSearcher):
506
+ """An iterative searcher that uses an evolutionary algorithm to optimize the subnet config."""
507
+
508
+ population: list[dict[str, Any]]
509
+ candidates: list[dict[str, Any]]
510
+
511
+ @property
512
+ def default_search_config(self) -> SearchConfig:
513
+ """Default search config contains additional algorithm parameters."""
514
+ return {
515
+ **super().default_search_config,
516
+ "population_size": 100,
517
+ "candidate_size": 25,
518
+ "mutation_prob": 0.1,
519
+ }
520
+
521
+ @property
522
+ def default_state_dict(self) -> SearchStateDict:
523
+ """Return default state dict."""
524
+ return {
525
+ **super().default_state_dict,
526
+ "population": [],
527
+ "candidates": [],
528
+ }
529
+
530
+ def _mutate(self, input: dict[str, Any]) -> dict[str, Any]:
531
+ output = self._sample()
532
+ # only considers independent hparams
533
+ output["config"] = self._configurable_config(self.model)
534
+ for var in output: # pylint: disable=C0206
535
+ for name in output[var]:
536
+ if random.random() > self.config["mutation_prob"]:
537
+ output[var][name] = input[var][name]
538
+ # returns reduced, independent config
539
+ return output
540
+
541
+ def _crossover(self, inputs: list[dict[str, Any]]) -> dict[str, Any]:
542
+ output = {"config": {}}
543
+ # only considers independent hparams
544
+ for name in self._configurable_config(self.model):
545
+ output["config"][name] = random.choice(inputs)["config"][name]
546
+ # returns reduced, independent config
547
+ return output
548
+
549
+ def sample(self) -> dict[str, Any]:
550
+ """Sampling a new subnet involves random sampling, mutation, and crossover."""
551
+ if not self.candidates:
552
+ return self._sample()
553
+ if len(self.population) < self.config["population_size"] // 2:
554
+ output = self._mutate(random.choice(self.candidates))
555
+ else:
556
+ output = self._crossover(random.sample(self.candidates, 2))
557
+ # returns full config
558
+ select(self.model, output["config"])
559
+ output["config"] = get_subnet_config(self.model)
560
+ return output
561
+
562
+ def before_search(self) -> None:
563
+ """Set the lower bound of the constraints to 0.85 * upper bound before search."""
564
+ super().before_search()
565
+ self.constraints_func.set_rel_lower_bounds(rel_lower=0.85)
566
+
567
+ def before_step(self) -> None:
568
+ """Update candidates and population before each iterative step."""
569
+ if len(self.population) >= self.config["population_size"]:
570
+ all_candidates = sorted(self.population, key=lambda x: x["metric"], reverse=True)
571
+ self.candidates = all_candidates[: self.config["candidate_size"]]
572
+ self.population = []
573
+
574
+ def after_step(self) -> None:
575
+ """Update population after each iterative step."""
576
+ if self.candidate["is_satisfied"]:
577
+ self.population.append(copy.deepcopy(self.candidate))
578
+
579
+
580
+ def convert_searchspace(
581
+ model: nn.Module, config: ModeloptBaseConfig, patch_manager_type: type[PatchManager]
582
+ ) -> ConvertReturnType:
583
+ """Convert given model into a search space."""
584
+ # initialize the true module if necessary
585
+ model = model.init_modellike() if isinstance(model, ModelLikeModule) else model
586
+
587
+ # check if the model is in channels-last format
588
+ # NOTE: not supported because we have not implemented channel sorting for channels-last format
589
+ if is_channels_last(model):
590
+ raise RuntimeError(f"Channels-last format in {type(model).__name__} is not supported!")
591
+
592
+ # put together metadata before starting to modify model
593
+ metadata = {"model_attributes": get_model_attributes(model)}
594
+
595
+ # now convert the model to a search space
596
+ search_space = generate_search_space(model, rules=config.model_dump())
597
+
598
+ # sanity check if any module has been converted
599
+ if not search_space.is_configurable():
600
+ raise ApplyModeError(
601
+ "The model does not contain any configurable hyperparameters! Please check the"
602
+ " documentation for modules and config and how to get a configurable model."
603
+ )
604
+
605
+ # activate the max subnet that can be used for subsequent tasks like training
606
+ # (note it should be activated already but we do it here again to be sure)
607
+ sample(model, max)
608
+
609
+ # search space requires a patch
610
+ patch_manager_type(model).patch()
611
+
612
+ # get current config store in metadata
613
+ metadata["subnet_config"] = get_subnet_config(model)
614
+
615
+ # return converted model as well as metadata
616
+ return model, metadata
617
+
618
+
619
+ def convert_autonas_searchspace(model: nn.Module, config: ModeloptBaseConfig) -> ConvertReturnType:
620
+ """Convert search space for AutoNAS mode with correct patch manager."""
621
+ return convert_searchspace(model, config, AutoNASPatchManager)
622
+
623
+
624
+ def restore_autonas_searchspace(
625
+ model: nn.Module, config: ModeloptBaseConfig, metadata: MetadataDict
626
+ ) -> nn.Module:
627
+ """Restore search space for AutoNAS mode with correct patch manager."""
628
+ return restore_searchspace(model, config, metadata, AutoNASPatchManager)
629
+
630
+
631
+ def restore_searchspace(
632
+ model: nn.Module,
633
+ config: ModeloptBaseConfig,
634
+ metadata: MetadataDict,
635
+ patch_manager: type[PatchManager],
636
+ ) -> nn.Module:
637
+ """Restore a search space from the given model."""
638
+ # retrieve metadata model attributes
639
+ model_attributes = metadata["model_attributes"]
640
+
641
+ # initialize the true module if necessary
642
+ model = model.init_modellike() if isinstance(model, ModelLikeModule) else model
643
+
644
+ # set up train/eval mode accordingly
645
+ is_training = model.training
646
+ model.train(model_attributes["training"])
647
+
648
+ # run regular convert entrypoint
649
+ model, metadata_new = convert_searchspace(model, config, patch_manager)
650
+
651
+ # compare new model attributes with provided model attributes
652
+ new_attributes = metadata_new["model_attributes"]
653
+ unmatched_keys = compare_dict(model_attributes, new_attributes)
654
+ if len(unmatched_keys) > 0:
655
+ error_msg = ["Following keys in your model do not match the checkpoint:"]
656
+ error_msg += [
657
+ f"{k}: Original={model_attributes.get(k, 'N/A')}; New={new_attributes.get(k, 'N/A')}"
658
+ for k in unmatched_keys
659
+ ]
660
+ raise ApplyModeError("\n\t".join(error_msg))
661
+
662
+ # ensure model is back in mode it started out in
663
+ model.train(is_training)
664
+
665
+ # select subnet config stored in metadata
666
+ select(model, metadata["subnet_config"])
667
+
668
+ # return converted model
669
+ return model
670
+
671
+
672
+ def update_autonas_metadata(
673
+ model: nn.Module, config: ModeloptBaseConfig, metadata: MetadataDict
674
+ ) -> None:
675
+ """Update subnet config to current subnet config of model."""
676
+ metadata["subnet_config"] = get_subnet_config(model)
677
+
678
+
679
+ def export_searchspace(model: nn.Module, config: ExportConfig) -> ConvertReturnType:
680
+ """Export a subnet configuration of the search space to a regular model."""
681
+ # sanity check to avoid DP/DDP here in the entrypoint
682
+ model = unwrap_model(model, raise_error=True)
683
+
684
+ # store config from model if we can find it for a future convert/restore process
685
+ subnet_config = get_subnet_config(model)
686
+
687
+ # Check for patching and calibration
688
+ if PatchManager.is_patched(model):
689
+ manager = PatchManager.get_manager(model)
690
+ if config.calib:
691
+ manager.call_post_eval()
692
+ manager.unpatch()
693
+
694
+ # export model in-place
695
+ model = SearchSpace(model).export()
696
+
697
+ # construct metadata
698
+ metadata = {
699
+ "subnet_config": subnet_config,
700
+ }
701
+
702
+ return model, metadata
703
+
704
+
705
+ def restore_export(model: nn.Module, config: ExportConfig, metadata: MetadataDict) -> nn.Module:
706
+ """Restore & export the subnet configuration of the search space to a regular model."""
707
+ # select subnet config provided in metadata
708
+ select(model, metadata["subnet_config"], strict=config["strict"])
709
+
710
+ # run export
711
+ model, metadata_new = export_searchspace(model, config)
712
+
713
+ # double check metadata
714
+ unmatched_keys = compare_dict(metadata, metadata_new)
715
+ if unmatched_keys:
716
+ raise ApplyModeError(f"Unmatched metadata={unmatched_keys}!")
717
+
718
+ return model
719
+
720
+
721
+ @NASModeRegistry.register_mode
722
+ class AutoNASModeDescriptor(ModeDescriptor):
723
+ """Class to describe the ``"autonas"`` mode.
724
+
725
+ The properties of this mode can be inspected via the source code.
726
+ """
727
+
728
+ @property
729
+ def name(self) -> str:
730
+ """Returns the value (str representation) of the mode."""
731
+ return "autonas"
732
+
733
+ @property
734
+ def config_class(self) -> type[ModeloptBaseConfig]:
735
+ """Specifies the config class for the mode."""
736
+ return AutoNASConfig
737
+
738
+ @property
739
+ def next_modes(self) -> set[str] | None:
740
+ """Modes that must immediately follow this mode."""
741
+ return {"export", "kd_loss", "quantize", "sparse_magnitude", "sparse_gpt"}
742
+
743
+ @property
744
+ def export_mode(self) -> str | None:
745
+ """The mode that corresponds to the export mode of this mode."""
746
+ return "export"
747
+
748
+ @property
749
+ def search_algorithm(self) -> type[BaseSearcher]:
750
+ """Specifies the search algorithm to use for this mode (if any)."""
751
+ return EvolveSearcher
752
+
753
+ @property
754
+ def convert(self) -> ConvertEntrypoint:
755
+ """The mode's entrypoint for converting a model."""
756
+ return convert_autonas_searchspace
757
+
758
+ @property
759
+ def restore(self) -> RestoreEntrypoint:
760
+ """The mode's entrypoint for restoring a model."""
761
+ return restore_autonas_searchspace
762
+
763
+ @property
764
+ def update_for_save(self) -> UpdateEntrypoint:
765
+ """The mode's entrypoint for updating the models state before saving."""
766
+ return update_autonas_metadata
767
+
768
+ @property
769
+ def update_for_new_mode(self) -> UpdateEntrypoint:
770
+ """The mode's entrypoint for updating the models state before new mode."""
771
+ return update_autonas_metadata
772
+
773
+
774
+ @NASModeRegistry.register_mode
775
+ class ExportModeDescriptor(ModeDescriptor):
776
+ """Class to describe the ``"export"`` mode.
777
+
778
+ The properties of this mode can be inspected via the source code.
779
+ """
780
+
781
+ @property
782
+ def name(self) -> str:
783
+ """Returns the value (str representation) of the mode."""
784
+ return "export"
785
+
786
+ @property
787
+ def config_class(self) -> type[ModeloptBaseConfig]:
788
+ """Specifies the config class for the mode."""
789
+ return ExportConfig
790
+
791
+ @property
792
+ def is_export_mode(self) -> bool:
793
+ """Whether the mode is an export mode.
794
+
795
+ Returns:
796
+ True if the mode is an export mode, False otherwise. Defaults to False.
797
+ """
798
+ return True
799
+
800
+ @property
801
+ def convert(self) -> ConvertEntrypoint:
802
+ """The mode's entrypoint for converting a model."""
803
+ return export_searchspace
804
+
805
+ @property
806
+ def restore(self) -> RestoreEntrypoint:
807
+ """The mode's entrypoint for restoring a model."""
808
+ return restore_export