-
Notifications
You must be signed in to change notification settings - Fork 608
feat(pretrained): add built-in pretrained downloader and alias backend #5277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
wanghan-iapcm
merged 11 commits into
deepmodeling:master
from
njzjz-bot:feat/pretrained-integration
Mar 10, 2026
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
665e508
feat(pretrained): integrate pretrained downloader and alias backend
njzjz-bot 06c1ff9
chore: drop temporary deepmd/__about__.py fallback
njzjz-bot 35f384b
style: apply prek formatting fixes for pretrained integration
njzjz-bot 27ac825
refactor(pretrained): rename backend helper and address PR review com…
njzjz-bot 7303412
refactor(pretrained): lazy load at backend boundary, eager deep_eval …
njzjz-bot 397a451
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 88db878
refactor(cli): move pretrained registry import to module scope
njzjz-bot e1c0f9a
fix(pretrained): accept case-insensitive .pretrained suffix
njzjz-bot 00a0ea2
docs(pretrained): add usage guide for dp pretrained
njzjz-bot 48ecbdf
feat(pretrained): support model-name aliases and clarify DeepPot docs
njzjz-bot 386ff68
refactor(pretrained): drop .pretrained compatibility
njzjz-bot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # SPDX-License-Identifier: LGPL-3.0-or-later | ||
| from collections.abc import ( | ||
| Callable, | ||
| ) | ||
| from typing import ( | ||
| TYPE_CHECKING, | ||
| ClassVar, | ||
| ) | ||
|
|
||
| from deepmd.backend.backend import ( | ||
| Backend, | ||
| ) | ||
| from deepmd.pretrained.registry import ( | ||
| available_model_names, | ||
| ) | ||
|
|
||
| if TYPE_CHECKING: | ||
| from argparse import ( | ||
| Namespace, | ||
| ) | ||
|
|
||
| from deepmd.infer.deep_eval import ( | ||
| DeepEvalBackend, | ||
| ) | ||
| from deepmd.utils.neighbor_stat import ( | ||
| NeighborStat, | ||
| ) | ||
|
|
||
|
|
||
| @Backend.register("pretrained") | ||
| class PretrainedBackend(Backend): | ||
| """Internal virtual backend for pretrained model-name alias dispatch. | ||
|
|
||
| This backend is not intended to be selected explicitly by users as a real | ||
| compute backend (such as TensorFlow/PyTorch/Paddle/JAX). It only bridges | ||
| built-in pretrained model names into the regular deep-eval loading path. | ||
|
|
||
| For convenience, all built-in pretrained model names are registered as | ||
| suffix-like aliases, so users can pass model names directly, e.g. | ||
| ``DeepPot("DPA-3.2-5M")``. | ||
| """ | ||
|
|
||
| name = "Pretrained" | ||
| features: ClassVar[Backend.Feature] = Backend.Feature.DEEP_EVAL | ||
| suffixes: ClassVar[list[str]] = [ | ||
| *[model_name.lower() for model_name in available_model_names()], | ||
| ] | ||
|
|
||
| def is_available(self) -> bool: | ||
| return True | ||
|
|
||
| @property | ||
| def entry_point_hook(self) -> Callable[["Namespace"], None]: | ||
| raise NotImplementedError("Unsupported backend: pretrained") | ||
|
|
||
| @property | ||
| def deep_eval(self) -> type["DeepEvalBackend"]: | ||
| from deepmd.pretrained.deep_eval import ( | ||
| PretrainedDeepEvalBackend, | ||
| ) | ||
|
|
||
| return PretrainedDeepEvalBackend | ||
|
|
||
| @property | ||
| def neighbor_stat(self) -> type["NeighborStat"]: | ||
| raise NotImplementedError("Unsupported backend: pretrained") | ||
|
|
||
| @property | ||
| def serialize_hook(self) -> Callable[[str], dict]: | ||
| raise NotImplementedError("Unsupported backend: pretrained") | ||
|
|
||
| @property | ||
| def deserialize_hook(self) -> Callable[[str, dict], None]: | ||
| raise NotImplementedError("Unsupported backend: pretrained") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # SPDX-License-Identifier: LGPL-3.0-or-later | ||
| """Pretrained model helpers for DeePMD-kit.""" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| # SPDX-License-Identifier: LGPL-3.0-or-later | ||
| """DeepEval adapter for pretrained model-name aliases.""" | ||
|
|
||
| from __future__ import ( | ||
| annotations, | ||
| ) | ||
|
|
||
| from pathlib import ( | ||
| Path, | ||
| ) | ||
| from typing import ( | ||
| TYPE_CHECKING, | ||
| Any, | ||
| ) | ||
|
|
||
| from deepmd.infer.deep_eval import ( | ||
| DeepEval, | ||
| DeepEvalBackend, | ||
| ) | ||
| from deepmd.pretrained.download import ( | ||
| resolve_model_path, | ||
| ) | ||
| from deepmd.pretrained.registry import ( | ||
| MODEL_REGISTRY, | ||
| ) | ||
|
|
||
| if TYPE_CHECKING: | ||
| import numpy as np | ||
|
|
||
|
|
||
| class InvalidPretrainedAliasError(ValueError): | ||
| """Raised when a pretrained alias string is malformed.""" | ||
|
|
||
| def __init__(self, model_file: str) -> None: | ||
| super().__init__(f"Invalid pretrained model name: {model_file}") | ||
|
|
||
|
|
||
| def parse_pretrained_alias(model_file: str) -> str: | ||
| """Extract built-in pretrained model name from alias string. | ||
|
|
||
| Accepted form: | ||
| - ``<MODEL>`` where ``<MODEL>`` is a built-in registry name | ||
| """ | ||
| alias = Path(model_file).name | ||
|
|
||
| if alias in MODEL_REGISTRY: | ||
| return alias | ||
|
|
||
| lowered = alias.lower() | ||
| for model_name in MODEL_REGISTRY: | ||
| if model_name.lower() == lowered: | ||
| return model_name | ||
|
|
||
| raise InvalidPretrainedAliasError(model_file) | ||
|
|
||
|
|
||
| class PretrainedDeepEvalBackend(DeepEvalBackend): | ||
| """Resolve alias and delegate to backend selected by resolved model path.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| model_file: str, | ||
| output_def: object, | ||
| *args: object, | ||
| auto_batch_size: object = True, | ||
| neighbor_list: object | None = None, | ||
| **kwargs: object, | ||
| ) -> None: | ||
| model_name = parse_pretrained_alias(model_file) | ||
| resolved = str(resolve_model_path(model_name)) | ||
|
|
||
| # DeepEvalBackend.__new__ dispatches by resolved suffix (.pt/.pb/.dp...) | ||
| self._backend = DeepEvalBackend( | ||
| resolved, | ||
| output_def, | ||
| *args, | ||
| auto_batch_size=auto_batch_size, | ||
| neighbor_list=neighbor_list, | ||
| **kwargs, | ||
| ) | ||
|
|
||
| def eval( | ||
| self, | ||
| coords: np.ndarray, | ||
| cells: np.ndarray | None, | ||
| atom_types: np.ndarray, | ||
| atomic: bool = False, | ||
| fparam: np.ndarray | None = None, | ||
| aparam: np.ndarray | None = None, | ||
| **kwargs: Any, | ||
| ) -> dict[str, np.ndarray]: | ||
| return self._backend.eval( | ||
| coords, | ||
| cells, | ||
| atom_types, | ||
| atomic, | ||
| fparam=fparam, | ||
| aparam=aparam, | ||
| **kwargs, | ||
| ) | ||
|
|
||
| def eval_descriptor( | ||
| self, | ||
| coords: np.ndarray, | ||
| cells: np.ndarray | None, | ||
| atom_types: np.ndarray, | ||
| fparam: np.ndarray | None = None, | ||
| aparam: np.ndarray | None = None, | ||
| efield: np.ndarray | None = None, | ||
| mixed_type: bool = False, | ||
| **kwargs: Any, | ||
| ) -> np.ndarray: | ||
| return self._backend.eval_descriptor( | ||
| coords, | ||
| cells, | ||
| atom_types, | ||
| fparam=fparam, | ||
| aparam=aparam, | ||
| efield=efield, | ||
| mixed_type=mixed_type, | ||
| **kwargs, | ||
| ) | ||
|
|
||
| def eval_fitting_last_layer( | ||
| self, | ||
| coords: np.ndarray, | ||
| cells: np.ndarray | None, | ||
| atom_types: np.ndarray, | ||
| fparam: np.ndarray | None = None, | ||
| aparam: np.ndarray | None = None, | ||
| **kwargs: Any, | ||
| ) -> np.ndarray: | ||
| return self._backend.eval_fitting_last_layer( | ||
| coords, | ||
| cells, | ||
| atom_types, | ||
| fparam=fparam, | ||
| aparam=aparam, | ||
| **kwargs, | ||
| ) | ||
|
|
||
| def get_rcut(self) -> float: | ||
| return self._backend.get_rcut() | ||
|
|
||
| def get_ntypes(self) -> int: | ||
| return self._backend.get_ntypes() | ||
|
|
||
| def get_type_map(self) -> list[str]: | ||
| return self._backend.get_type_map() | ||
|
|
||
| def get_dim_fparam(self) -> int: | ||
| return self._backend.get_dim_fparam() | ||
|
|
||
| def has_default_fparam(self) -> bool: | ||
| return self._backend.has_default_fparam() | ||
|
|
||
| def get_dim_aparam(self) -> int: | ||
| return self._backend.get_dim_aparam() | ||
|
|
||
| @property | ||
| def model_type(self) -> type[DeepEval]: | ||
| return self._backend.model_type | ||
|
|
||
| def get_sel_type(self) -> list[int]: | ||
| return self._backend.get_sel_type() | ||
|
|
||
| def get_numb_dos(self) -> int: | ||
| return self._backend.get_numb_dos() | ||
|
|
||
| def get_has_efield(self) -> bool: | ||
| return self._backend.get_has_efield() | ||
|
|
||
| def get_has_spin(self) -> bool: | ||
| return self._backend.get_has_spin() | ||
|
|
||
| def get_has_hessian(self) -> bool: | ||
| return self._backend.get_has_hessian() | ||
|
|
||
| def get_var_name(self) -> str: | ||
| return self._backend.get_var_name() | ||
|
|
||
| def get_ntypes_spin(self) -> int: | ||
| return self._backend.get_ntypes_spin() | ||
|
|
||
| def get_model(self) -> Any: | ||
| return self._backend.get_model() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.