Skip to content
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ec2e031
implement pytorch-exportable for se_e2_a descriptor
Feb 5, 2026
b8a48ff
better type for xp.zeros
Feb 5, 2026
1cc001f
implement env, base_descriptor and exclude_mask, remove the dependenc…
Feb 6, 2026
f2fbe88
mv to_torch_tensor to common
Feb 6, 2026
e2afbe9
simplify __init__ of the NaiveLayer
Feb 6, 2026
4ba511a
fix bug
Feb 6, 2026
fb9598a
fix bug
Feb 6, 2026
fa03351
simplify init method of se_e2_a descriptor. fig bug in consistent UT
Feb 6, 2026
09b33f1
restructure the test folders. add test_common.
Feb 6, 2026
67f2e54
add test_exclusion_mask.py
Feb 6, 2026
f7d83dd
fix poitential import issue in test.
Feb 6, 2026
0c96bb6
correct __call__(). fix bug
Feb 6, 2026
9dca912
fix registration issue
Feb 6, 2026
17f0a5d
fix pt-expt file extension
Feb 6, 2026
8ce93ba
fix(pt): expansion of get_default_nthreads()
Feb 6, 2026
3091988
fix bug of intra-inter
Feb 6, 2026
85f0583
fix bug of default dp inter value
Feb 6, 2026
d33324d
fix cicd
Feb 6, 2026
4de9a56
feat: add support for se_r
Feb 6, 2026
f4dc0af
fix device of xp array
Feb 6, 2026
2384835
fix device of xp array
Feb 6, 2026
9646d71
revert extend_coord_with_ghosts
Feb 6, 2026
f270069
raise error for non-implemented methods
Feb 6, 2026
57433d3
restore import torch
Feb 6, 2026
eedcbaf
fix(pt,pt-expt): guard thread setters
Feb 6, 2026
d8b2cf4
make exclusion mask modules
Feb 6, 2026
aeef15a
fix(pt-expt): clear params on None
Feb 6, 2026
8bdb1f8
fix bug
Feb 7, 2026
d3b01da
utility to handel dpmodel -> pt_expt conversion
Feb 8, 2026
3452a2a
fix to_numpy_array device
Feb 8, 2026
ba8e7ab
chore(dpmodel,pt_expt): refactorize the implementation of embedding net
Feb 8, 2026
621c7cc
feat: se_t and se_t_tebd descriptors for the pytroch exportable backend.
Feb 8, 2026
faa4026
fix bug
Feb 8, 2026
e263270
refact: fitting net
Feb 8, 2026
ea61141
fix bug
Feb 8, 2026
de8f156
merge master
Feb 8, 2026
ad83d98
Merge branch 'refact-auto-setattr' into refact-fitting-net
Feb 8, 2026
7cf88d2
Merge branch 'master' into refact-fitting-net
njzjz Feb 9, 2026
d02aa6a
Merge branch 'master' into refact-fitting-net
njzjz Feb 10, 2026
271dc3b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 10, 2026
356214c
Revert "feat: se_t and se_t_tebd descriptors for the pytroch exportab…
Feb 11, 2026
53d2768
Merge remote-tracking branch 'origin/refact-fitting-net' into refact-…
Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions deepmd/backend/pt_expt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
from collections.abc import (
Callable,
)
from importlib.util import (
find_spec,
)
from typing import (
TYPE_CHECKING,
ClassVar,
)

from deepmd.backend.backend import (
Backend,
)

if TYPE_CHECKING:
from argparse import (
Namespace,
)

from deepmd.infer.deep_eval import (
DeepEvalBackend,
)
from deepmd.utils.neighbor_stat import (
NeighborStat,
)


@Backend.register("pt-expt")
@Backend.register("pytorch-exportable")
class PyTorchExportableBackend(Backend):
"""PyTorch exportable backend."""

name = "PyTorch Exportable"
Comment thread
wanghan-iapcm marked this conversation as resolved.
"""The formal name of the backend."""
features: ClassVar[Backend.Feature] = (
Backend.Feature.ENTRY_POINT
| Backend.Feature.DEEP_EVAL
| Backend.Feature.NEIGHBOR_STAT
| Backend.Feature.IO
)
Comment thread
wanghan-iapcm marked this conversation as resolved.
"""The features of the backend."""
suffixes: ClassVar[list[str]] = [".pte"]
"""The suffixes of the backend."""

def is_available(self) -> bool:
"""Check if the backend is available.

Returns
-------
bool
Whether the backend is available.
"""
return find_spec("torch") is not None

@property
def entry_point_hook(self) -> Callable[["Namespace"], None]:
"""The entry point hook of the backend.

Returns
-------
Callable[[Namespace], None]
The entry point hook of the backend.
"""
from deepmd.pt.entrypoints.main import main as deepmd_main

return deepmd_main

@property
def deep_eval(self) -> type["DeepEvalBackend"]:
"""The Deep Eval backend of the backend.

Returns
-------
type[DeepEvalBackend]
The Deep Eval backend of the backend.
"""
raise NotImplementedError
Comment thread
wanghan-iapcm marked this conversation as resolved.

@property
def neighbor_stat(self) -> type["NeighborStat"]:
"""The neighbor statistics of the backend.

Returns
-------
type[NeighborStat]
The neighbor statistics of the backend.
"""
raise NotImplementedError

@property
def serialize_hook(self) -> Callable[[str], dict]:
"""The serialize hook to convert the model file to a dictionary.

Returns
-------
Callable[[str], dict]
The serialize hook of the backend.
"""
raise NotImplementedError

@property
def deserialize_hook(self) -> Callable[[str, dict], None]:
"""The deserialize hook to convert the dictionary to a model file.

Returns
-------
Callable[[str, dict], None]
The deserialize hook of the backend.
"""
raise NotImplementedError
5 changes: 3 additions & 2 deletions deepmd/dpmodel/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,11 @@ def to_numpy_array(x: Optional["Array"]) -> np.ndarray | None:
try:
# asarray is not within Array API standard, so may fail
return np.asarray(x)
except (ValueError, AttributeError):
except (ValueError, AttributeError, TypeError):
xp = array_api_compat.array_namespace(x)
# to fix BufferError: Cannot export readonly array since signalling readonly is unsupported by DLPack.
x = xp.asarray(x, copy=True)
# Move to CPU device to ensure numpy compatibility
x = xp.asarray(x, device="cpu", copy=True)
return np.from_dlpack(x)


Expand Down
21 changes: 16 additions & 5 deletions deepmd/dpmodel/descriptor/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
NoReturn,
)

import numpy as np
import array_api_compat

from deepmd.dpmodel.array_api import (
Array,
Expand Down Expand Up @@ -173,7 +173,18 @@ def extend_descrpt_stat(
extend_dstd = des_with_stat["dstd"]
else:
extend_shape = [len(type_map), *list(des["davg"].shape[1:])]
extend_davg = np.zeros(extend_shape, dtype=des["davg"].dtype)
extend_dstd = np.ones(extend_shape, dtype=des["dstd"].dtype)
des["davg"] = np.concatenate([des["davg"], extend_davg], axis=0)
des["dstd"] = np.concatenate([des["dstd"], extend_dstd], axis=0)
# Use array_api_compat to infer device and dtype from context
xp = array_api_compat.array_namespace(des["davg"])
extend_davg = xp.zeros(
extend_shape,
dtype=des["davg"].dtype,
device=array_api_compat.device(des["davg"]),
)
extend_dstd = xp.ones(
extend_shape,
dtype=des["dstd"].dtype,
device=array_api_compat.device(des["dstd"]),
)
xp = array_api_compat.array_namespace(des["davg"])
Comment thread
wanghan-iapcm marked this conversation as resolved.
Outdated
des["davg"] = xp.concat([des["davg"], extend_davg], axis=0)
des["dstd"] = xp.concat([des["dstd"], extend_dstd], axis=0)
11 changes: 9 additions & 2 deletions deepmd/dpmodel/descriptor/dpa1.py
Original file line number Diff line number Diff line change
Expand Up @@ -909,9 +909,14 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.stddev)
device = array_api_compat.device(self.stddev)
if not self.set_davg_zero:
self.mean = xp.asarray(mean, dtype=self.mean.dtype, copy=True)
self.stddev = xp.asarray(stddev, dtype=self.stddev.dtype, copy=True)
self.mean = xp.asarray(
mean, dtype=self.mean.dtype, copy=True, device=device
)
self.stddev = xp.asarray(
stddev, dtype=self.stddev.dtype, copy=True, device=device
)

def get_stats(self) -> dict[str, StatItem]:
"""Get the statistics of the descriptor."""
Expand Down Expand Up @@ -1044,6 +1049,8 @@ def call(
idx_j = xp.reshape(nei_type, (-1,))
# (nf x nl x nnei) x ng
idx = xp.tile(xp.reshape((idx_i + idx_j), (-1, 1)), (1, ng))
# Cast to int64 for PyTorch backend (take_along_dim requires Long indices)
idx = xp.astype(idx, xp.int64)
# (ntypes) * ntypes * nt
type_embedding_nei = xp.tile(
xp.reshape(type_embedding, (1, ntypes_with_padding, nt)),
Expand Down
9 changes: 7 additions & 2 deletions deepmd/dpmodel/descriptor/repflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,9 +453,14 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.stddev)
device = array_api_compat.device(self.stddev)
if not self.set_davg_zero:
self.mean = xp.asarray(mean, dtype=self.mean.dtype, copy=True)
self.stddev = xp.asarray(stddev, dtype=self.stddev.dtype, copy=True)
self.mean = xp.asarray(
mean, dtype=self.mean.dtype, copy=True, device=device
)
self.stddev = xp.asarray(
stddev, dtype=self.stddev.dtype, copy=True, device=device
)

def get_stats(self) -> dict[str, StatItem]:
"""Get the statistics of the descriptor."""
Expand Down
9 changes: 7 additions & 2 deletions deepmd/dpmodel/descriptor/repformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,14 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.stddev)
device = array_api_compat.device(self.stddev)
if not self.set_davg_zero:
self.mean = xp.asarray(mean, dtype=self.mean.dtype, copy=True)
self.stddev = xp.asarray(stddev, dtype=self.stddev.dtype, copy=True)
self.mean = xp.asarray(
mean, dtype=self.mean.dtype, copy=True, device=device
)
self.stddev = xp.asarray(
stddev, dtype=self.stddev.dtype, copy=True, device=device
)

def get_stats(self) -> dict[str, StatItem]:
"""Get the statistics of the descriptor."""
Expand Down
13 changes: 10 additions & 3 deletions deepmd/dpmodel/descriptor/se_e2_a.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,12 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.dstd)
device = array_api_compat.device(self.dstd)
if not self.set_davg_zero:
self.davg = xp.asarray(mean, dtype=self.davg.dtype, copy=True)
self.dstd = xp.asarray(stddev, dtype=self.dstd.dtype, copy=True)
self.davg = xp.asarray(
mean, dtype=self.davg.dtype, copy=True, device=device
)
self.dstd = xp.asarray(stddev, dtype=self.dstd.dtype, copy=True, device=device)

def set_stat_mean_and_stddev(
self,
Expand Down Expand Up @@ -607,7 +610,11 @@ def call(
sec = self.sel_cumsum

ng = self.neuron[-1]
gr = xp.zeros([nf * nloc, ng, 4], dtype=self.dstd.dtype)
gr = xp.zeros(
[nf * nloc, ng, 4],
dtype=input_dtype,
device=array_api_compat.device(coord_ext),
)
exclude_mask = self.emask.build_type_exclude_mask(nlist, atype_ext)
# merge nf and nloc axis, so for type_one_side == False,
# we don't require atype is the same in all frames
Expand Down
11 changes: 8 additions & 3 deletions deepmd/dpmodel/descriptor/se_r.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,12 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.dstd)
device = array_api_compat.device(self.dstd)
if not self.set_davg_zero:
self.davg = xp.asarray(mean, dtype=self.davg.dtype, copy=True)
self.dstd = xp.asarray(stddev, dtype=self.dstd.dtype, copy=True)
self.davg = xp.asarray(
mean, dtype=self.davg.dtype, copy=True, device=device
)
self.dstd = xp.asarray(stddev, dtype=self.dstd.dtype, copy=True, device=device)

def set_stat_mean_and_stddev(
self,
Expand Down Expand Up @@ -391,7 +394,9 @@ def call(

ng = self.neuron[-1]
xyz_scatter = xp.zeros(
[nf, nloc, ng], dtype=get_xp_precision(xp, self.precision)
[nf, nloc, ng],
dtype=get_xp_precision(xp, self.precision),
device=array_api_compat.device(coord_ext),
)
exclude_mask = self.emask.build_type_exclude_mask(nlist, atype_ext)
rr = xp.astype(rr, xyz_scatter.dtype)
Expand Down
13 changes: 10 additions & 3 deletions deepmd/dpmodel/descriptor/se_t.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,12 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.dstd)
device = array_api_compat.device(self.dstd)
if not self.set_davg_zero:
self.davg = xp.asarray(mean, dtype=self.davg.dtype, copy=True)
self.dstd = xp.asarray(stddev, dtype=self.dstd.dtype, copy=True)
self.davg = xp.asarray(
mean, dtype=self.davg.dtype, copy=True, device=device
)
self.dstd = xp.asarray(stddev, dtype=self.dstd.dtype, copy=True, device=device)

def set_stat_mean_and_stddev(
self,
Expand Down Expand Up @@ -366,7 +369,11 @@ def call(
sec = self.sel_cumsum

ng = self.neuron[-1]
result = xp.zeros([nf * nloc, ng], dtype=get_xp_precision(xp, self.precision))
result = xp.zeros(
[nf * nloc, ng],
dtype=get_xp_precision(xp, self.precision),
device=array_api_compat.device(coord_ext),
)
exclude_mask = self.emask.build_type_exclude_mask(nlist, atype_ext)
# merge nf and nloc axis, so for type_one_side == False,
# we don't require atype is the same in all frames
Expand Down
15 changes: 12 additions & 3 deletions deepmd/dpmodel/descriptor/se_t_tebd.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,9 +694,14 @@ def compute_input_stats(
self.stats = env_mat_stat.stats
mean, stddev = env_mat_stat()
xp = array_api_compat.array_namespace(self.stddev)
device = array_api_compat.device(self.stddev)
if not self.set_davg_zero:
self.mean = xp.asarray(mean, dtype=self.mean.dtype, copy=True)
self.stddev = xp.asarray(stddev, dtype=self.stddev.dtype, copy=True)
self.mean = xp.asarray(
mean, dtype=self.mean.dtype, copy=True, device=device
)
self.stddev = xp.asarray(
stddev, dtype=self.stddev.dtype, copy=True, device=device
)

def get_stats(self) -> dict[str, StatItem]:
"""Get the statistics of the descriptor."""
Expand Down Expand Up @@ -764,7 +769,9 @@ def call(
sw = xp.where(
nlist_mask[:, :, None],
xp.reshape(sw, (nf * nloc, nnei, 1)),
xp.zeros((nf * nloc, nnei, 1), dtype=sw.dtype),
xp.zeros(
(nf * nloc, nnei, 1), dtype=sw.dtype, device=array_api_compat.device(sw)
),
)

# nfnl x nnei x 4
Expand Down Expand Up @@ -827,6 +834,8 @@ def call(

# (nf x nl x nt_i x nt_j) x ng
idx = xp.tile(xp.reshape((idx_i + idx_j), (-1, 1)), (1, ng))
# Cast to int64 for PyTorch backend (take_along_dim requires Long indices)
idx = xp.astype(idx, xp.int64)

# ntypes * (ntypes) * nt
type_embedding_i = xp.tile(
Expand Down
Loading
Loading