Skip to content

Commit 1e134a1

Browse files
author
Han Wang
committed
test(dpmodel): build_angle_index multi-center, static-layout, ordered-no-self coverage
1 parent 0b1b49f commit 1e134a1

1 file changed

Lines changed: 84 additions & 1 deletion

File tree

source/tests/common/dpmodel/test_angle_builder.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import numpy as np
22
import pytest
33

4-
from deepmd.dpmodel.utils.neighbor_graph import build_angle_index, pad_and_guard_angles
4+
from deepmd.dpmodel.utils.neighbor_graph import (
5+
GraphLayout,
6+
build_angle_index,
7+
pad_and_guard_angles,
8+
)
59

610

711
def test_pad_angles_dynamic_appends_min_guard():
@@ -120,3 +124,82 @@ def test_build_angle_index_torch_namespace():
120124
if am_t[p].item()
121125
}
122126
assert got_t == got_np
127+
128+
129+
def test_build_angle_index_multi_center():
130+
# Edges with MIXED centers: dst=[0,1,0,1,2]; exercises the dst[a]!=dst[b] exclusion
131+
# src=[1,2,3,4,5], dst=[0,1,0,1,2], all norms within a_rcut
132+
# Expected angles per center:
133+
# dst=0: edges {0,2} => {(0,2),(2,0)}
134+
# dst=1: edges {1,3} => {(1,3),(3,1)}
135+
# dst=2: edge {4} => no pairs
136+
edge_index = np.array([[1, 2, 3, 4, 5], [0, 1, 0, 1, 2]], dtype=np.int64)
137+
edge_vec = np.array(
138+
[[0.3, 0, 0], [0.4, 0, 0], [0.5, 0, 0], [0.6, 0, 0], [0.7, 0, 0]]
139+
)
140+
edge_mask = np.array([True, True, True, True, True])
141+
ai, am = build_angle_index(edge_index, edge_vec, edge_mask, 6, a_rcut=1.0)
142+
got = {(int(ai[0, p]), int(ai[1, p])) for p in range(ai.shape[1]) if am[p]}
143+
evnorm = np.linalg.norm(edge_vec, axis=-1)
144+
expected = _angle_oracle(
145+
[0, 1, 0, 1, 2], evnorm, edge_mask, 1.0, ordered=False, include_self=False
146+
)
147+
assert got == expected
148+
# Verify no cross-center angles
149+
assert all(
150+
edge_index[1, int(ai[0, p])] == edge_index[1, int(ai[1, p])]
151+
for p in range(ai.shape[1])
152+
if am[p]
153+
)
154+
155+
156+
def test_build_angle_index_static_layout():
157+
# Test with static layout.angle_capacity; shape must be (2, capacity)
158+
edge_index = np.array([[1, 2, 3, 1], [0, 0, 0, 0]], dtype=np.int64)
159+
edge_vec = np.array([[0.5, 0, 0], [0.9, 0, 0], [2.5, 0, 0], [0.7, 0, 0]])
160+
edge_mask = np.array([True, True, True, True])
161+
layout = GraphLayout(edge_capacity=100, angle_capacity=10)
162+
ai, am = build_angle_index(
163+
edge_index, edge_vec, edge_mask, 4, a_rcut=1.0, layout=layout
164+
)
165+
# Check static shape
166+
assert ai.shape == (2, 10)
167+
assert am.shape == (10,)
168+
# Check real angles match the dynamic result
169+
got_static = {(int(ai[0, p]), int(ai[1, p])) for p in range(ai.shape[1]) if am[p]}
170+
ai_dyn, am_dyn = build_angle_index(edge_index, edge_vec, edge_mask, 4, a_rcut=1.0)
171+
got_dynamic = {
172+
(int(ai_dyn[0, p]), int(ai_dyn[1, p]))
173+
for p in range(ai_dyn.shape[1])
174+
if am_dyn[p]
175+
}
176+
assert got_static == got_dynamic
177+
# Check mask counts match
178+
assert int(am.sum()) == int(am_dyn.sum())
179+
180+
181+
def test_build_angle_index_ordered_no_self():
182+
# Test ordered=True, include_self=False; should be symmetric pairs excluding diagonals
183+
edge_index = np.array([[1, 2, 3, 1], [0, 0, 0, 0]], dtype=np.int64)
184+
edge_vec = np.array([[0.5, 0, 0], [0.9, 0, 0], [2.5, 0, 0], [0.7, 0, 0]])
185+
edge_mask = np.array([True, True, True, True])
186+
ai, am = build_angle_index(
187+
edge_index, edge_vec, edge_mask, 4, a_rcut=1.0, ordered=True, include_self=False
188+
)
189+
got = {(int(ai[0, p]), int(ai[1, p])) for p in range(ai.shape[1]) if am[p]}
190+
evnorm = np.linalg.norm(edge_vec, axis=-1)
191+
expected = _angle_oracle(
192+
[0, 0, 0, 0], evnorm, edge_mask, 1.0, ordered=True, include_self=False
193+
)
194+
assert got == expected
195+
# Verify no self-angles and ordered includes both directions
196+
assert all(
197+
int(ai[0, p]) != int(ai[1, p]) for p in range(ai.shape[1]) if am[p]
198+
) # no self
199+
for pair in got:
200+
a, b = pair
201+
# For ordered=True, include_self=False, we expect both (a,b) and (b,a)
202+
# if they are different and both within a_rcut and same center
203+
rev_pair = (b, a)
204+
if a != b and a not in [2] and b not in [2]: # edge 2 is outside a_rcut
205+
assert rev_pair in got # symmetric pairs should both exist

0 commit comments

Comments
 (0)