Skip to content

Commit 7c54170

Browse files
committed
fixup
1 parent 905fe7f commit 7c54170

11 files changed

Lines changed: 155 additions & 46 deletions

File tree

deepmd/pt/infer/deep_eval.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,12 @@ def _setup_nlist_backend(self, nlist_backend: str) -> None:
325325
"vesin[torch]`) or use nlist_backend='native' (or 'auto')."
326326
)
327327
builder = VesinNeighborList()
328+
elif DEVICE.type != "cuda":
329+
raise ValueError(
330+
"nlist_backend='nv' requires CUDA inference tensors; "
331+
f"current DEVICE is {DEVICE!s}. Use nlist_backend='native' "
332+
"(or 'auto') for CPU inference."
333+
)
328334
elif not is_nv_available():
329335
raise ImportError(
330336
"nlist_backend='nv' was requested but 'nvalchemi-toolkit-ops'"
@@ -338,7 +344,7 @@ def _setup_nlist_backend(self, nlist_backend: str) -> None:
338344
# Pick the first available O(N) builder; nv is GPU-only.
339345
if is_vesin_torch_available():
340346
builder = VesinNeighborList()
341-
elif is_nv_available() and torch.cuda.is_available():
347+
elif is_nv_available() and DEVICE.type == "cuda":
342348
builder = NvNeighborList()
343349
self._nlist_builder = builder
344350

deepmd/pt/model/descriptor/sezm.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ def __init__(
619619
self.kmax = int(kmax)
620620
if self.kmax < 0:
621621
raise ValueError("`kmax` must be non-negative")
622-
if self.kmax > int(lmax):
622+
if self.kmax > self.lmax:
623623
raise ValueError("`kmax` must be <= `lmax`")
624624
self.ebed_dims = [get_so3_dim_of_lmax(l) for l in self.l_schedule]
625625
self._init_node_l_schedules(extra_node_l)
@@ -844,8 +844,14 @@ def __init__(
844844

845845
blocks: list[SeZMInteractionBlock] = []
846846
for block_idx, (l_b, node_l_b, m_b) in enumerate(
847-
zip(self.l_schedule, self.node_l_schedule, self.m_schedule)
847+
zip(
848+
self.l_schedule,
849+
self.node_l_schedule,
850+
self.m_schedule,
851+
strict=True,
852+
)
848853
):
854+
k_b = min(self.kmax, l_b)
849855
blocks.append(
850856
SeZMInteractionBlock(
851857
lmax=l_b,
@@ -877,7 +883,7 @@ def __init__(
877883
message_node_so3=self.message_node_so3,
878884
ffn_s2_activation=self.ffn_s2_activation,
879885
ffn_so3_grid=self.ffn_so3_grid,
880-
kmax=self.kmax,
886+
kmax=k_b,
881887
so2_lebedev_quadrature=self.so2_lebedev_quadrature,
882888
ffn_lebedev_quadrature=self.ffn_lebedev_quadrature,
883889
n_atten_head=self.n_atten_head,
@@ -1511,12 +1517,14 @@ def _build_gie_zonal_coupling(
15111517
mp_row_index,
15121518
mp_m0_col_index,
15131519
]
1514-
edge_len = safe_norm(edge_cache.edge_vec, self.eps)
1515-
edge_quat = build_edge_quaternion(
1516-
edge_cache.edge_vec,
1517-
edge_len=edge_len,
1518-
eps=self.eps,
1519-
)
1520+
edge_quat = edge_cache.edge_quat
1521+
if edge_quat is None:
1522+
edge_len = safe_norm(edge_cache.edge_vec, self.eps)
1523+
edge_quat = build_edge_quaternion(
1524+
edge_cache.edge_vec,
1525+
edge_len=edge_len,
1526+
eps=self.eps,
1527+
)
15201528
extra_coupling = self.gie_zonal_wigner_calc.forward_zonal(
15211529
edge_quat,
15221530
lmin=self.lmax + 1,
@@ -1676,7 +1684,7 @@ def _init_lm_schedules(
16761684
raise ValueError("`m_schedule` must have the same length as `l_schedule`")
16771685
if any(x < 0 for x in self.m_schedule):
16781686
raise ValueError("`m_schedule` entries must be non-negative")
1679-
if any(m > l for m, l in zip(self.m_schedule, self.l_schedule)):
1687+
if any(m > l for m, l in zip(self.m_schedule, self.l_schedule, strict=True)):
16801688
raise ValueError(
16811689
"`m_schedule` entries must satisfy `m_schedule[i] <= l_schedule[i]`"
16821690
)

deepmd/pt/model/descriptor/sezm_nn/block.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,8 @@ def _use_infer_activation_checkpoint(self, *tensors: torch.Tensor) -> bool:
638638
"""
639639
return (
640640
not self.training
641-
and os.environ.get("DP_ACT_INFER") == "1"
641+
and os.environ.get("DP_ACT_INFER", "").strip().lower()
642+
in {"1", "true", "yes", "on"}
642643
and os.environ.get("DP_COMPILE_INFER", "").strip().lower()
643644
not in {"1", "true", "yes", "on"}
644645
and torch.is_grad_enabled()

deepmd/pt/model/descriptor/sezm_nn/cute/so2_rotation.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -830,10 +830,12 @@ def _rb_backward(ctx: Any, grad_out: Tensor) -> tuple:
830830

831831

832832
# === Public API ==============================================================
833-
def _cute_usable(*tensors: Tensor) -> bool:
833+
def _cute_usable(channels: int, *tensors: Tensor) -> bool:
834834
"""Return True when the CuTe fast path is available for these tensors."""
835835
if not SEZM_CUTE_AVAILABLE:
836836
return False
837+
if int(channels) < _TN or int(channels) % _TN != 0:
838+
return False
837839
return all(
838840
t.is_cuda and t.dtype == torch.float32 for t in tensors if t.is_floating_point()
839841
)
@@ -872,7 +874,7 @@ def rotate_to_local_cute(
872874
Experimental path that is not used in production. See the module docstring
873875
for the benchmark conclusion and why the Triton kernels were chosen instead.
874876
"""
875-
if _cute_usable(x, wigner) and src.numel() > 0:
877+
if _cute_usable(x.shape[2], x, wigner) and src.numel() > 0:
876878
return torch.ops.sezm_cute.rotate_to_local(
877879
x, src, wigner, coeff_index, int(dim_full)
878880
)
@@ -909,7 +911,7 @@ def rotate_back_cute(
909911
Experimental path that is not used in production. See the module docstring
910912
for the benchmark conclusion and why the Triton kernels were chosen instead.
911913
"""
912-
if _cute_usable(x_local, wigner) and x_local.shape[0] > 0:
914+
if _cute_usable(x_local.shape[2], x_local, wigner) and x_local.shape[0] > 0:
913915
return torch.ops.sezm_cute.rotate_back(
914916
x_local, wigner, coeff_index, int(dim_full)
915917
)

deepmd/pt/model/descriptor/sezm_nn/edge_cache.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class EdgeFeatureCache(NamedTuple):
7171
Used for efficient batched rotation. None if not available.
7272
Dt_full
7373
Transpose of D_full with shape (E, D, D). None if not available.
74+
edge_quat
75+
Per-edge global-to-local quaternion actually used to build ``D_full`` and
76+
``Dt_full`` with shape (E, 4). Includes the optional random local-Z roll.
7477
D_to_m_cache
7578
Lazy cache for projected D matrices keyed by a normalized
7679
``"lmax:mmax"`` identifier.
@@ -103,6 +106,7 @@ class EdgeFeatureCache(NamedTuple):
103106
D_to_m_cache: dict[str, torch.Tensor] | None = None
104107
Dt_from_m_cache: dict[str, torch.Tensor] | None = None
105108
edge_src_gate: torch.Tensor | None = None
109+
edge_quat: torch.Tensor | None = None
106110

107111

108112
def compute_edge_src_gate(
@@ -337,13 +341,13 @@ def build_edge_cache(
337341

338342
# === Step 6. Edge quaternion -> Wigner-D blocks ===
339343
with nvtx_range("wigner_d"):
340-
D_full, Dt_full = _build_edge_wigner(
344+
D_full, Dt_full, edge_quat = _build_edge_wigner(
341345
edge_vec=edge_vec,
342346
edge_len=edge_len,
343347
eps=eps,
344348
random_gamma=random_gamma,
345349
wigner_calc=wigner_calc,
346-
) # (E, D, D), (E, D, D)
350+
) # (E, D, D), (E, D, D), (E, 4)
347351

348352
edge_type_feat = build_edge_type_feat(type_ebed, src, dst) # (E, C)
349353

@@ -357,6 +361,7 @@ def build_edge_cache(
357361
edge_env=edge_env,
358362
D_full=D_full,
359363
Dt_full=Dt_full,
364+
edge_quat=edge_quat,
360365
deg_norm_floor=deg_norm_floor,
361366
)
362367

@@ -460,13 +465,13 @@ def build_edge_cache_from_edges(
460465

461466
# === Step 4. Edge quaternion -> Wigner-D blocks ===
462467
with nvtx_range("wigner_d"):
463-
D_full, Dt_full = _build_edge_wigner(
468+
D_full, Dt_full, edge_quat = _build_edge_wigner(
464469
edge_vec=edge_vec,
465470
edge_len=edge_len,
466471
eps=eps,
467472
random_gamma=random_gamma,
468473
wigner_calc=wigner_calc,
469-
) # (E, D, D), (E, D, D)
474+
) # (E, D, D), (E, D, D), (E, 4)
470475

471476
# === Step 5. Edge type features ===
472477
edge_type_feat = build_edge_type_feat(type_ebed, src, dst)
@@ -498,6 +503,7 @@ def build_edge_cache_from_edges(
498503
edge_env=edge_env,
499504
D_full=D_full,
500505
Dt_full=Dt_full,
506+
edge_quat=edge_quat,
501507
deg_norm_floor=deg_norm_floor,
502508
edge_src_gate=edge_src_gate,
503509
)
@@ -510,7 +516,7 @@ def _build_edge_wigner(
510516
eps: float,
511517
random_gamma: bool,
512518
wigner_calc: WignerCalculatorFn,
513-
) -> tuple[torch.Tensor, torch.Tensor]:
519+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
514520
"""
515521
Build packed Wigner-D blocks from edge vectors.
516522
@@ -530,8 +536,9 @@ def _build_edge_wigner(
530536
531537
Returns
532538
-------
533-
tuple[torch.Tensor, torch.Tensor]
534-
Packed Wigner-D matrices ``(D_full, Dt_full)`` with shape ``(E, D, D)``.
539+
tuple[torch.Tensor, torch.Tensor, torch.Tensor]
540+
Packed Wigner-D matrices ``(D_full, Dt_full)`` with shape ``(E, D, D)``
541+
and the quaternion used to build them with shape ``(E, 4)``.
535542
"""
536543
# === Step 1. Build edge-aligned quaternions ===
537544
edge_quat = build_edge_quaternion(
@@ -550,7 +557,8 @@ def _build_edge_wigner(
550557
edge_quat = quaternion_multiply(quaternion_z_rotation(gamma), edge_quat)
551558

552559
# === Step 3. Convert quaternions to packed Wigner-D blocks ===
553-
return wigner_calc(edge_quat)
560+
D_full, Dt_full = wigner_calc(edge_quat)
561+
return D_full, Dt_full, edge_quat
554562

555563

556564
def _finalize_edge_cache(
@@ -564,6 +572,7 @@ def _finalize_edge_cache(
564572
edge_env: torch.Tensor,
565573
D_full: torch.Tensor,
566574
Dt_full: torch.Tensor,
575+
edge_quat: torch.Tensor,
567576
deg_norm_floor: float,
568577
edge_src_gate: torch.Tensor | None = None,
569578
) -> EdgeFeatureCache:
@@ -590,6 +599,9 @@ def _finalize_edge_cache(
590599
Packed Wigner-D matrices with shape (E, D, D).
591600
Dt_full
592601
Transposed packed Wigner-D matrices with shape (E, D, D).
602+
edge_quat
603+
Global-to-local quaternions used to build the Wigner-D matrices with
604+
shape (E, 4).
593605
deg_norm_floor
594606
Floor added to the envelope-squared degree before the inverse-sqrt
595607
normalization. A tiny ``eps`` reproduces the legacy behavior; an
@@ -627,6 +639,7 @@ def _finalize_edge_cache(
627639
D_to_m_cache={},
628640
Dt_from_m_cache={},
629641
edge_src_gate=edge_src_gate,
642+
edge_quat=edge_quat,
630643
)
631644

632645

@@ -661,6 +674,7 @@ def _get_empty_edge_cache(
661674
"""
662675
empty_long = torch.empty(0, dtype=torch.long, device=device)
663676
empty_vec = torch.empty(0, 3, dtype=dtype, device=device)
677+
empty_quat = torch.empty(0, 4, dtype=dtype, device=device)
664678
empty_rbf = torch.empty(0, n_radial, dtype=dtype, device=device)
665679
empty_type_feat = torch.empty(0, n_channel, dtype=dtype, device=device)
666680
deg = torch.zeros(n_nodes, dtype=dtype, device=device)
@@ -679,6 +693,7 @@ def _get_empty_edge_cache(
679693
D_to_m_cache={},
680694
Dt_from_m_cache={},
681695
edge_src_gate=None,
696+
edge_quat=empty_quat,
682697
)
683698

684699

@@ -835,15 +850,19 @@ def edge_cache_to_dtype(
835850
_D_full = cache.D_full
836851
_Dt_full = cache.Dt_full
837852
_edge_src_gate = cache.edge_src_gate
853+
_edge_quat = cache.edge_quat
838854
D_full: torch.Tensor | None = None
839855
Dt_full: torch.Tensor | None = None
840856
edge_src_gate: torch.Tensor | None = None
857+
edge_quat: torch.Tensor | None = None
841858
if _D_full is not None:
842859
D_full = _D_full.to(dtype=dtype)
843860
if _Dt_full is not None:
844861
Dt_full = _Dt_full.to(dtype=dtype)
845862
if _edge_src_gate is not None:
846863
edge_src_gate = _edge_src_gate.to(dtype=dtype)
864+
if _edge_quat is not None:
865+
edge_quat = _edge_quat.to(dtype=dtype)
847866

848867
return EdgeFeatureCache(
849868
src=cache.src,
@@ -859,4 +878,5 @@ def edge_cache_to_dtype(
859878
D_to_m_cache=None if cache.D_to_m_cache is None else {},
860879
Dt_from_m_cache=None if cache.Dt_from_m_cache is None else {},
861880
edge_src_gate=edge_src_gate,
881+
edge_quat=edge_quat,
862882
)

deepmd/pt/model/descriptor/sezm_nn/so2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,7 @@ def __init__(
13341334
dtype=self.compute_dtype,
13351335
layout="flat",
13361336
grid_resolution_list=self.s2_grid_resolution,
1337+
coefficient_layout="m_major",
13371338
grid_method=self.s2_grid_method,
13381339
grid_branches=node_wise_branches,
13391340
mlp_bias=self.mlp_bias,

deepmd/pt/model/descriptor/sezm_nn/triton/so2_rotation.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,13 +1169,9 @@ def _block_layout_lmax(coeff_index: Tensor, dim_full: int) -> int:
11691169
"""Return ``lmax`` if ``(coeff_index, dim_full)`` is the m-major ``mmax=1``
11701170
layout that the block-diagonal kernels assume, else ``-1``.
11711171
1172-
Detection uses ONLY shapes / python ints -- never tensor *values* -- so it is
1173-
safe under ``make_fx`` / fake-tensor tracing (the production compiled
1174-
inference path). The test is: ``dim_full`` is a perfect square ``(lmax+1)^2``
1175-
and ``Dm == 3*lmax+1``. For a fixed ``lmax`` the reduced size ``Dm`` is
1176-
strictly increasing in ``mmax`` (``lmax+1``, ``3*lmax+1``, ``5*lmax-1``, ...),
1177-
so ``Dm == 3*lmax+1`` uniquely pins ``mmax == 1``; combined with the model's
1178-
canonical ``build_m_major_index`` ordering this fully determines the layout.
1172+
Shape detection handles fake-tensor tracing; real tensors are additionally
1173+
checked against ``build_m_major_index(lmax, 1)`` because the block kernels
1174+
ignore ``coeff_index`` values.
11791175
"""
11801176
dim_full = int(dim_full)
11811177
root = math.isqrt(dim_full)
@@ -1188,9 +1184,24 @@ def _block_layout_lmax(coeff_index: Tensor, dim_full: int) -> int:
11881184
return -1
11891185
if lmax < 1 or numel != 3 * lmax + 1:
11901186
return -1
1187+
if not _canonical_block_index_values(coeff_index, lmax):
1188+
return -1
11911189
return lmax
11921190

11931191

1192+
def _canonical_block_index_values(coeff_index: Tensor, lmax: int) -> bool:
1193+
"""Return whether real ``coeff_index`` values match the m-major mmax=1 layout."""
1194+
if getattr(coeff_index, "fake_mode", None) is not None:
1195+
return True
1196+
if coeff_index.device.type == "meta":
1197+
return True
1198+
try:
1199+
expected = build_m_major_index(int(lmax), 1, device=coeff_index.device)
1200+
return torch.equal(coeff_index, expected)
1201+
except Exception: # pragma: no cover - exotic tensor subclasses
1202+
return False
1203+
1204+
11941205
def _launch_bd_to_local_fwd(
11951206
x: Tensor, src: Tensor, wigner: Tensor, lmax: int
11961207
) -> Tensor:

0 commit comments

Comments
 (0)