Skip to content

Commit 4a45fe5

Browse files
pd: support paddle backend and water/se_e2_a (#4302)
Split <#4157> into several pull requests. 1. Add core modules of paddle backend(`deepmd.pd.*`) and related backend module unitests. 2. Support training/testing/freeze(C++ inference will be supported in subsequent pull request) for example water/se_e2_a. 3. Add se_e2_a related uinttests Related PR to be merged: - [x] <PaddlePaddle/Paddle#69139> ## Accuracy test ### pytorch ![image](https://github.com/user-attachments/assets/cea8f313-4a57-4575-b55a-b6cf577654a2) ### paddle: ``` log deepmd.utils.batch_size Adjust batch size from 1024 to 2048 deepmd.utils.batch_size Adjust batch size from 2048 to 4096 deepmd.entrypoints.test # number of test data : 30 , deepmd.entrypoints.test Energy MAE : 7.467160e-02 eV deepmd.entrypoints.test Energy RMSE : 8.981154e-02 eV deepmd.entrypoints.test Energy MAE/Natoms : 3.889146e-04 eV deepmd.entrypoints.test Energy RMSE/Natoms : 4.677685e-04 eV deepmd.entrypoints.test Force MAE : 4.495974e-02 eV/A deepmd.entrypoints.test Force RMSE : 5.883696e-02 eV/A deepmd.entrypoints.test Virial MAE : 4.683873e+00 eV deepmd.entrypoints.test Virial RMSE : 6.298489e+00 eV deepmd.entrypoints.test Virial MAE/Natoms : 2.439517e-02 eV deepmd.entrypoints.test Virial RMSE/Natoms : 3.280463e-02 eV ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced support for PaddlePaddle in the DeePMD framework, enhancing model training and evaluation capabilities. - Added new backend options and configuration files for multitask models. - Implemented new classes and methods for handling Paddle-specific functionalities, including descriptor calculations and model evaluations. - Enhanced the command-line interface to include Paddle as a backend option. - Expanded the functionality for managing Paddle dependencies and configurations in the testing framework. - **Bug Fixes** - Improved error handling and robustness in various components across the framework. - **Tests** - Expanded the test suite to include Paddle-specific tests, ensuring consistency and reliability across different backends. - Introduced unit tests for new functionalities related to Paddle, including model evaluations and descriptor calculations. - Added tests to validate force gradient calculations and smoothness properties in models. - Implemented tests for neighbor statistics and region transformations, ensuring accuracy in calculations. - **Documentation** - Updated documentation across multiple modules to reflect new features and usage instructions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: HydrogenSulfate <490868991@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 3cdf407 commit 4a45fe5

File tree

136 files changed

+21039
-25
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+21039
-25
lines changed

.github/workflows/test_cuda.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
- run: |
5252
export PYTORCH_ROOT=$(python -c 'import torch;print(torch.__path__[0])')
5353
export TENSORFLOW_ROOT=$(python -c 'import importlib,pathlib;print(pathlib.Path(importlib.util.find_spec("tensorflow").origin).parent)')
54+
source/install/uv_with_retry.sh pip install --system --pre paddlepaddle-gpu -i https://www.paddlepaddle.org.cn/packages/nightly/cu123/
5455
source/install/uv_with_retry.sh pip install --system -v -e .[gpu,test,lmp,cu12,torch,jax] mpi4py
5556
env:
5657
DP_VARIANT: cuda

.github/workflows/test_python.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
export PYTORCH_ROOT=$(python -c 'import torch;print(torch.__path__[0])')
3232
source/install/uv_with_retry.sh pip install --system -e .[test,jax] mpi4py
3333
source/install/uv_with_retry.sh pip install --system horovod --no-build-isolation
34+
source/install/uv_with_retry.sh pip install --system --pre "paddlepaddle" -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/
3435
env:
3536
# Please note that uv has some issues with finding
3637
# existing TensorFlow package. Currently, it uses

backend/find_paddle.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
import importlib
3+
import os
4+
import site
5+
from functools import (
6+
lru_cache,
7+
)
8+
from importlib.machinery import (
9+
FileFinder,
10+
)
11+
from importlib.util import (
12+
find_spec,
13+
)
14+
from pathlib import (
15+
Path,
16+
)
17+
from sysconfig import (
18+
get_path,
19+
)
20+
from typing import (
21+
Optional,
22+
Union,
23+
)
24+
25+
26+
@lru_cache
27+
def find_paddle() -> tuple[Optional[str], list[str]]:
28+
"""Find PaddlePadle library.
29+
30+
Tries to find PaddlePadle in the order of:
31+
32+
1. Environment variable `PADDLE_ROOT` if set
33+
2. The current Python environment.
34+
3. user site packages directory if enabled
35+
4. system site packages directory (purelib)
36+
37+
Considering the default PaddlePadle package still uses old CXX11 ABI, we
38+
cannot install it automatically.
39+
40+
Returns
41+
-------
42+
str, optional
43+
PaddlePadle library path if found.
44+
list of str
45+
Paddle requirement if not found. Empty if found.
46+
"""
47+
if os.environ.get("DP_ENABLE_PADDLE", "0") == "0":
48+
return None, []
49+
requires = []
50+
pd_spec = None
51+
52+
if (pd_spec is None or not pd_spec) and os.environ.get("PADDLE_ROOT") is not None:
53+
site_packages = Path(os.environ.get("PADDLE_ROOT")).parent.absolute()
54+
pd_spec = FileFinder(str(site_packages)).find_spec("paddle")
55+
56+
# get paddle spec
57+
# note: isolated build will not work for backend
58+
if pd_spec is None or not pd_spec:
59+
pd_spec = find_spec("paddle")
60+
61+
if not pd_spec and site.ENABLE_USER_SITE:
62+
# first search TF from user site-packages before global site-packages
63+
site_packages = site.getusersitepackages()
64+
if site_packages:
65+
pd_spec = FileFinder(site_packages).find_spec("paddle")
66+
67+
if not pd_spec:
68+
# purelib gets site-packages path
69+
site_packages = get_path("purelib")
70+
if site_packages:
71+
pd_spec = FileFinder(site_packages).find_spec("paddle")
72+
73+
# get install dir from spec
74+
try:
75+
pd_install_dir = pd_spec.submodule_search_locations[0] # type: ignore
76+
# AttributeError if ft_spec is None
77+
# TypeError if submodule_search_locations are None
78+
# IndexError if submodule_search_locations is an empty list
79+
except (AttributeError, TypeError, IndexError):
80+
pd_install_dir = None
81+
requires.extend(get_pd_requirement()["paddle"])
82+
return pd_install_dir, requires
83+
84+
85+
@lru_cache
86+
def get_pd_requirement(pd_version: str = "") -> dict:
87+
"""Get PaddlePadle requirement when Paddle is not installed.
88+
89+
If pd_version is not given and the environment variable `PADDLE_VERSION` is set, use it as the requirement.
90+
91+
Parameters
92+
----------
93+
pd_version : str, optional
94+
Paddle version
95+
96+
Returns
97+
-------
98+
dict
99+
PaddlePadle requirement.
100+
"""
101+
if pd_version is None:
102+
return {"paddle": []}
103+
if pd_version == "":
104+
pd_version = os.environ.get("PADDLE_VERSION", "")
105+
106+
return {
107+
"paddle": [
108+
"paddlepaddle>=3.0.0b1" if pd_version != "" else "paddlepaddle>=3.0.0b1",
109+
],
110+
}
111+
112+
113+
@lru_cache
114+
def get_pd_version(pd_path: Optional[Union[str, Path]]) -> str:
115+
"""Get Paddle version from a Paddle Python library path.
116+
117+
Parameters
118+
----------
119+
pd_path : str or Path
120+
Paddle Python library path, e.g. "/python3.10/site-packages/paddle/"
121+
122+
Returns
123+
-------
124+
str
125+
version
126+
"""
127+
if pd_path is None or pd_path == "":
128+
return ""
129+
version_file = Path(pd_path) / "version" / "__init__.py"
130+
spec = importlib.util.spec_from_file_location("paddle.version", version_file)
131+
module = importlib.util.module_from_spec(spec)
132+
spec.loader.exec_module(module)
133+
return module.full_version

deepmd/backend/paddle.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
from importlib.util import (
3+
find_spec,
4+
)
5+
from typing import (
6+
TYPE_CHECKING,
7+
Callable,
8+
ClassVar,
9+
)
10+
11+
from deepmd.backend.backend import (
12+
Backend,
13+
)
14+
15+
if TYPE_CHECKING:
16+
from argparse import (
17+
Namespace,
18+
)
19+
20+
from deepmd.infer.deep_eval import (
21+
DeepEvalBackend,
22+
)
23+
from deepmd.utils.neighbor_stat import (
24+
NeighborStat,
25+
)
26+
27+
28+
@Backend.register("pd")
29+
@Backend.register("paddle")
30+
class PaddleBackend(Backend):
31+
"""Paddle backend."""
32+
33+
name = "Paddle"
34+
"""The formal name of the backend."""
35+
features: ClassVar[Backend.Feature] = (
36+
Backend.Feature.ENTRY_POINT
37+
| Backend.Feature.DEEP_EVAL
38+
| Backend.Feature.NEIGHBOR_STAT
39+
| Backend.Feature.IO
40+
)
41+
"""The features of the backend."""
42+
suffixes: ClassVar[list[str]] = [".json", ".pd"]
43+
"""The suffixes of the backend."""
44+
45+
def is_available(self) -> bool:
46+
"""Check if the backend is available.
47+
48+
Returns
49+
-------
50+
bool
51+
Whether the backend is available.
52+
"""
53+
return find_spec("paddle") is not None
54+
55+
@property
56+
def entry_point_hook(self) -> Callable[["Namespace"], None]:
57+
"""The entry point hook of the backend.
58+
59+
Returns
60+
-------
61+
Callable[[Namespace], None]
62+
The entry point hook of the backend.
63+
"""
64+
from deepmd.pd.entrypoints.main import main as deepmd_main
65+
66+
return deepmd_main
67+
68+
@property
69+
def deep_eval(self) -> type["DeepEvalBackend"]:
70+
"""The Deep Eval backend of the backend.
71+
72+
Returns
73+
-------
74+
type[DeepEvalBackend]
75+
The Deep Eval backend of the backend.
76+
"""
77+
from deepmd.pd.infer.deep_eval import DeepEval as DeepEvalPD
78+
79+
return DeepEvalPD
80+
81+
@property
82+
def neighbor_stat(self) -> type["NeighborStat"]:
83+
"""The neighbor statistics of the backend.
84+
85+
Returns
86+
-------
87+
type[NeighborStat]
88+
The neighbor statistics of the backend.
89+
"""
90+
from deepmd.pd.utils.neighbor_stat import (
91+
NeighborStat,
92+
)
93+
94+
return NeighborStat
95+
96+
@property
97+
def serialize_hook(self) -> Callable[[str], dict]:
98+
"""The serialize hook to convert the model file to a dictionary.
99+
100+
Returns
101+
-------
102+
Callable[[str], dict]
103+
The serialize hook of the backend.
104+
"""
105+
from deepmd.pd.utils.serialization import (
106+
serialize_from_file,
107+
)
108+
109+
return serialize_from_file
110+
111+
@property
112+
def deserialize_hook(self) -> Callable[[str, dict], None]:
113+
"""The deserialize hook to convert the dictionary to a model file.
114+
115+
Returns
116+
-------
117+
Callable[[str, dict], None]
118+
The deserialize hook of the backend.
119+
"""
120+
from deepmd.pd.utils.serialization import (
121+
deserialize_to_file,
122+
)
123+
124+
return deserialize_to_file

deepmd/dpmodel/model/make_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def format_nlist(
457457
458458
Returns
459459
-------
460-
formated_nlist
460+
formatted_nlist
461461
the formatted nlist.
462462
463463
"""

deepmd/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ def main_parser() -> argparse.ArgumentParser:
9999
formatter_class=RawTextArgumentDefaultsHelpFormatter,
100100
epilog=textwrap.dedent(
101101
"""\
102-
Use --tf or --pt to choose the backend:
102+
Use --tf, --pt or --pd to choose the backend:
103103
dp --tf train input.json
104104
dp --pt train input.json
105+
dp --pd train input.json
105106
"""
106107
),
107108
)

deepmd/pd/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
# import customized OPs globally
4+
5+
from deepmd.utils.entry_point import (
6+
load_entry_point,
7+
)
8+
9+
load_entry_point("deepmd.pd")
10+
11+
__all__ = []

deepmd/pd/entrypoints/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later

0 commit comments

Comments
 (0)