Skip to content

Commit 49eeda7

Browse files
njzjznjzjz-bot
andauthored
test(dpa4): enable array-api-strict consistency tests (#5751)
## Summary - add array-api-strict wrappers for the DPA4 descriptor and SeZM energy fitting net - enable array-api-strict coverage in the existing DPA4 descriptor/fitting consistent tests - make DPA4 scalar RMSNorm indexing array-api-strict compliant and lazy-load DPA4 PT test classes during PT-only execution ## Validation - `ruff format .` - `ruff check .` - `pytest source/tests/consistent/descriptor/test_dpa4.py source/tests/consistent/fitting/test_dpa4_ener.py -q -k array_api_strict` -> 32 passed, 224 deselected - `pytest source/tests/consistent/descriptor/test_dpa4.py source/tests/consistent/fitting/test_dpa4_ener.py -q -k dp_self_consistent` -> 16 passed, 240 deselected <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Expanded strict array-API coverage for the DPA4 descriptor and DPA4 energy fitting workflow, including new DPA4 strict descriptor exports and energy fitting wrappers. * **Bug Fixes** * Corrected 2D input normalization scaling so the applied scale matches the expected channel layout. * **Tests** * Updated consistency tests to conditionally enable the strict backend and added strict-backend evaluation helpers for descriptor and energy outputs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: njzjz-bot <njzjz.bot@gmail.com>
1 parent c175b9b commit 49eeda7

8 files changed

Lines changed: 139 additions & 5 deletions

File tree

deepmd/dpmodel/descriptor/dpa4_nn/norm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ def call(self, x: Any) -> Any:
546546
if x.ndim == 2:
547547
inv_rms = 1.0 / xp.sqrt(xp.mean(x * x, axis=-1, keepdims=True) + self.eps)
548548
x = x * inv_rms
549-
x = x * xp_asarray_nodetach(xp, self.adam_scale[...], device=device)[0]
549+
x = x * xp_asarray_nodetach(xp, self.adam_scale[...], device=device)[0, :]
550550
return xp.astype(x, in_dtype)
551551

552552
inv_rms = 1.0 / xp.sqrt(xp.mean(x * x, axis=-1, keepdims=True) + self.eps)

source/tests/array_api_strict/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ def to_array_api_strict_array(array: np.ndarray | None) -> Any:
5959
f"{_PACKAGE_ROOT}.descriptor.dpa2",
6060
f"{_PACKAGE_ROOT}.descriptor.repflows",
6161
f"{_PACKAGE_ROOT}.descriptor.dpa3",
62+
f"{_PACKAGE_ROOT}.descriptor.dpa4",
6263
f"{_PACKAGE_ROOT}.descriptor.hybrid",
6364
f"{_PACKAGE_ROOT}.fitting",
65+
f"{_PACKAGE_ROOT}.fitting.dpa4_ener",
6466
)
6567

6668

source/tests/array_api_strict/descriptor/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from .dpa3 import (
99
DescrptDPA3,
1010
)
11+
from .dpa4 import (
12+
DescrptDPA4,
13+
)
1114
from .hybrid import (
1215
DescrptHybrid,
1316
)
@@ -31,6 +34,7 @@
3134
"DescrptDPA1",
3235
"DescrptDPA2",
3336
"DescrptDPA3",
37+
"DescrptDPA4",
3438
"DescrptHybrid",
3539
"DescrptSeA",
3640
"DescrptSeAttenV2",
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
from importlib import (
3+
import_module,
4+
)
5+
6+
from deepmd.dpmodel.descriptor.dpa4 import DescrptDPA4 as DescrptDPA4DP
7+
from deepmd.dpmodel.descriptor.dpa4_nn.activation import (
8+
SwiGLU,
9+
)
10+
from deepmd.dpmodel.descriptor.dpa4_nn.grid_net import (
11+
GridProduct,
12+
)
13+
from deepmd.dpmodel.descriptor.dpa4_nn.radial import (
14+
BridgingSwitch,
15+
C3CutoffEnvelope,
16+
InnerClamp,
17+
)
18+
from deepmd.dpmodel.descriptor.dpa4_nn.wignerd import (
19+
WignerDCalculator,
20+
)
21+
22+
from ..common import (
23+
array_api_strict_module,
24+
register_dpmodel_mapping,
25+
)
26+
from .base_descriptor import (
27+
BaseDescriptor,
28+
)
29+
30+
import_module("..utils.exclude_mask", __package__)
31+
import_module("..utils.network", __package__)
32+
33+
34+
@BaseDescriptor.register("SeZM")
35+
@BaseDescriptor.register("sezm")
36+
@BaseDescriptor.register("DPA4")
37+
@BaseDescriptor.register("dpa4")
38+
@array_api_strict_module
39+
class DescrptDPA4(DescrptDPA4DP):
40+
pass
41+
42+
43+
for _stateless_cls in (
44+
BridgingSwitch,
45+
C3CutoffEnvelope,
46+
GridProduct,
47+
InnerClamp,
48+
SwiGLU,
49+
WignerDCalculator,
50+
):
51+
register_dpmodel_mapping(_stateless_cls, lambda v: v)

source/tests/array_api_strict/fitting/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
from .dpa4_ener import (
3+
SeZMEnergyFittingNet,
4+
)
25
from .fitting import (
36
DipoleFittingNet,
47
DOSFittingNet,
@@ -13,4 +16,5 @@
1316
"EnergyFittingNet",
1417
"PolarFittingNet",
1518
"PropertyFittingNet",
19+
"SeZMEnergyFittingNet",
1620
]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
from importlib import (
3+
import_module,
4+
)
5+
from typing import (
6+
ClassVar,
7+
)
8+
9+
from deepmd.dpmodel.fitting.dpa4_ener import GLUFittingNet as GLUFittingNetDP
10+
from deepmd.dpmodel.fitting.dpa4_ener import (
11+
SeZMEnergyFittingNet as SeZMEnergyFittingNetDP,
12+
)
13+
from deepmd.dpmodel.fitting.dpa4_ener import (
14+
SeZMNetworkCollection as SeZMNetworkCollectionDP,
15+
)
16+
17+
from ..common import (
18+
array_api_strict_module,
19+
)
20+
21+
import_module("..utils.network", __package__)
22+
23+
24+
@array_api_strict_module
25+
class GLUFittingNet(GLUFittingNetDP):
26+
pass
27+
28+
29+
@array_api_strict_module
30+
class SeZMNetworkCollection(SeZMNetworkCollectionDP):
31+
NETWORK_TYPE_MAP: ClassVar[dict[str, type]] = {
32+
"sezm_fitting_network": GLUFittingNet,
33+
}
34+
35+
36+
@array_api_strict_module
37+
class SeZMEnergyFittingNet(SeZMEnergyFittingNetDP):
38+
pass

source/tests/consistent/descriptor/test_dpa4.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
)
2020

2121
from ..common import (
22+
INSTALLED_ARRAY_API_STRICT,
2223
INSTALLED_PT,
2324
INSTALLED_PT_EXPT,
2425
CommonTest,
@@ -36,6 +37,10 @@
3637
from deepmd.pt_expt.descriptor.dpa4 import DescrptDPA4 as DescrptDPA4PTExpt
3738
else:
3839
DescrptDPA4PTExpt = None
40+
if INSTALLED_ARRAY_API_STRICT:
41+
from ...array_api_strict.descriptor.dpa4 import DescrptDPA4 as DescrptDPA4Strict
42+
else:
43+
DescrptDPA4Strict = None
3944

4045
# not implemented
4146
DescrptDPA4TF = None
@@ -153,15 +158,15 @@ def skip_pt(self) -> bool:
153158
skip_jax = True
154159
skip_pd = True
155160
skip_pt_expt = not INSTALLED_PT_EXPT
156-
skip_array_api_strict = True
161+
skip_array_api_strict = not INSTALLED_ARRAY_API_STRICT
157162

158163
tf_class = DescrptDPA4TF
159164
dp_class = DescrptDPA4DP
160165
pt_class = DescrptDPA4PT
161166
pt_expt_class = DescrptDPA4PTExpt
162167
jax_class = None
163168
pd_class = None
164-
array_api_strict_class = None
169+
array_api_strict_class = DescrptDPA4Strict
165170
args: ClassVar[list] = [
166171
*descrpt_se_zm_args(),
167172
Argument("ntypes", int, optional=False),
@@ -234,6 +239,16 @@ def eval_pt_expt(self, pt_expt_obj: Any) -> Any:
234239
mixed_types=True,
235240
)
236241

242+
def eval_array_api_strict(self, array_api_strict_obj: Any) -> Any:
243+
return self.eval_array_api_strict_descriptor(
244+
array_api_strict_obj,
245+
self.natoms,
246+
self.coords,
247+
self.atype,
248+
self.box,
249+
mixed_types=True,
250+
)
251+
237252
def extract_ret(self, ret: Any, backend) -> tuple[np.ndarray, ...]:
238253
return (ret[0],)
239254

source/tests/consistent/fitting/test_dpa4_ener.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
import numpy as np
88

9+
from deepmd.dpmodel.common import (
10+
to_numpy_array,
11+
)
912
from deepmd.dpmodel.fitting.dpa4_ener import SeZMEnergyFittingNet as SeZMEnerFittingDP
1013
from deepmd.env import (
1114
GLOBAL_NP_FLOAT_PRECISION,
@@ -15,6 +18,7 @@
1518
)
1619

1720
from ..common import (
21+
INSTALLED_ARRAY_API_STRICT,
1822
INSTALLED_PT,
1923
INSTALLED_PT_EXPT,
2024
CommonTest,
@@ -40,6 +44,14 @@
4044
from deepmd.pt_expt.utils.env import DEVICE as PT_EXPT_DEVICE
4145
else:
4246
SeZMEnerFittingPTExpt = None
47+
if INSTALLED_ARRAY_API_STRICT:
48+
import array_api_strict
49+
50+
from ...array_api_strict.fitting.dpa4_ener import (
51+
SeZMEnergyFittingNet as SeZMEnerFittingStrict,
52+
)
53+
else:
54+
SeZMEnerFittingStrict = None
4355

4456
# not implemented
4557
SeZMEnerFittingTF = None
@@ -77,15 +89,15 @@ def skip_pt(self) -> bool:
7789
skip_jax = True
7890
skip_pd = True
7991
skip_pt_expt = not INSTALLED_PT_EXPT
80-
skip_array_api_strict = True
92+
skip_array_api_strict = not INSTALLED_ARRAY_API_STRICT
8193

8294
tf_class = SeZMEnerFittingTF
8395
dp_class = SeZMEnerFittingDP
8496
pt_class = SeZMEnerFittingPT
8597
pt_expt_class = SeZMEnerFittingPTExpt
8698
jax_class = None
8799
pd_class = None
88-
array_api_strict_class = None
100+
array_api_strict_class = SeZMEnerFittingStrict
89101
args = fitting_sezm_ener()
90102

91103
def setUp(self) -> None:
@@ -138,6 +150,14 @@ def eval_pt_expt(self, pt_expt_obj: Any) -> Any:
138150
.numpy()
139151
)
140152

153+
def eval_array_api_strict(self, array_api_strict_obj: Any) -> Any:
154+
return to_numpy_array(
155+
array_api_strict_obj(
156+
array_api_strict.asarray(self.inputs),
157+
array_api_strict.asarray(self.atype.reshape(1, -1)),
158+
)["energy"]
159+
)
160+
141161
def extract_ret(self, ret: Any, backend) -> tuple[np.ndarray, ...]:
142162
return (ret,)
143163

0 commit comments

Comments
 (0)