Skip to content

Commit 44ebdda

Browse files
author
Han Wang
committed
Merge master into feat-graph-dpa2 (integrate deepmodeling#5758 dpa1 graph CUDA rework)
Master's deepmodeling#5758 restructured the NeighborGraph deployment ABI while this PR's review batch was landing; this merge re-expresses the batch on the new schema. Master's conventions adopted as the base: - 13-input graph lower (atype, n_node, n_local, edge_index, edge_vec, edge_mask, destination_order, destination_row_ptr, source_order, source_row_ptr, fparam, aparam, charge_spin), canonicalize=True destination-major payloads, destination_sorted validation, and the canonical (compressed-dpa1 CUDA) export flavor. - n_local as a first-class device input on every graph route: this SUBSUMES this branch's 17th-input with-comm design -- the extra input is dropped, while the all-8-comm-tensors-on-CPU placement, the border_op explicit CUDA-stream sync, and the ago==0 preflight cache are kept. - n_local now also masks halo rows of the per-node outputs (owned rows unchanged); contract tests updated. - The rectangular-aparam ragged extension shim (_extend_graph_aparam) is kept as a correct alternate input layout; flat (N, nda) remains the canonical trace/export ABI. Review-batch semantics re-grafted onto the new base: - trace capacity from the real synthetic edge count (probe + prime collision-free dims, export and compiled training). - flat (N, nda) aparam ABI: trace sample slot 11, dynamic-shape spec sharing atype's n_node_total symbol, all rectangular->flat boundaries. - energy gate on the default-flip, now in the shared graph_lower.model_uses_graph_lower; dense compiled key translation stays output-agnostic. - with-comm graph artifact: trace branch re-added to the export graph section (master had none), nested-artifact trace now inherits lower_kind, EnergyModel with-comm wrapper rewritten to the 13-input base, C++ run_model_graph_with_comm and its call site moved to the 13-input pack (canonicalized payload + flat aparam via extend_graph_aparam). - pt_expt dpa1 call_graph regains the comm_dict pass-through (ABI stability; forwarded to the dpmodel fallback). Verified: graph export/metadata/with-comm suites, dpa1+dpa2 graph lower, dpmodel graph suites, training+multitask, dos_graph, neighbor_list, graph deepeval, dpa1 descriptor (400+ tests); C++ full rebuild + dpa1/dpa2 graph and fparam/aparam gtests (39 pass) on regenerated 13-input fixtures.
2 parents 95b3dee + 2aee81c commit 44ebdda

268 files changed

Lines changed: 38488 additions & 3940 deletions

File tree

Some content is hidden

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

.github/workflows/mirror_gitee.yml

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
11
name: Mirror to Gitee Repo
22

3+
# Ref creation is also delivered as a push event, but deletion may only emit a
4+
# delete event. Keep both triggers so removed branches and tags are pruned from
5+
# the mirror without launching duplicate mirrors for ref creation.
36
on:
47
push:
5-
branches-ignore:
6-
- "copilot/**"
7-
- "dependabot/**"
8-
- "pre-commit-ci-update-config"
8+
# Use positive patterns with exclusions so tag pushes can be listed below.
9+
# `branches-ignore` by itself disables all tag-triggered runs.
10+
branches:
11+
- "**"
12+
# GitHub creates these ephemeral refs while testing a merge queue entry.
13+
# They are neither source branches nor refs that Gitee should receive.
14+
- "!gh-readonly-queue/**"
15+
- "!copilot/**"
16+
- "!dependabot/**"
17+
- "!pre-commit-ci-update-config"
18+
tags:
19+
- "**"
920
delete:
10-
create:
1121

1222
# Ensures that only one mirror task will run at a time.
1323
concurrency:
1424
group: git-mirror
1525

1626
jobs:
1727
git-mirror:
18-
if: github.repository_owner == 'deepmodeling'
19-
runs-on: ubuntu-slim
28+
# Delete events use the default branch as `github.ref`, so inspect the
29+
# payload ref to suppress only ephemeral merge-queue branch deletions.
30+
if: >-
31+
github.repository_owner == 'deepmodeling' &&
32+
(github.event_name != 'delete' ||
33+
!startsWith(github.event.ref, 'gh-readonly-queue/'))
34+
# This is a Docker action; ubuntu-slim does not provide a Docker daemon.
35+
runs-on: ubuntu-latest
2036
steps:
2137
- uses: wearerequired/git-mirror-action@v1
2238
env:

backend/dynamic_metadata.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
"""Provide project metadata that depends on the selected build configuration."""
3+
24
import sys
5+
from collections.abc import (
6+
Mapping,
7+
)
38
from pathlib import (
49
Path,
510
)
11+
from typing import (
12+
Any,
13+
)
614

715
from .find_pytorch import (
816
get_pt_requirement,
@@ -27,29 +35,44 @@ def __dir__() -> list[str]:
2735

2836

2937
def dynamic_metadata(
30-
field: str,
31-
settings: dict[str, object] | None = None,
32-
):
33-
assert field in ["optional-dependencies", "entry-points", "scripts"]
38+
settings: Mapping[str, object],
39+
_project: Mapping[str, Any],
40+
) -> dict[str, Any]:
41+
"""Return one metadata fragment using the standard v1 provider protocol.
42+
43+
Each ``[[tool.dynamic-metadata]]`` entry selects a field explicitly. The
44+
returned mapping is merged into the resolved ``[project]`` table by
45+
scikit-build-core.
46+
"""
47+
field = settings.get("field")
48+
if not isinstance(field, str) or field not in {
49+
"optional-dependencies",
50+
"scripts",
51+
}:
52+
msg = f"Unsupported dynamic metadata field: {field!r}"
53+
raise ValueError(msg)
54+
3455
_, _, find_libpython_requires, extra_scripts, tf_version, pt_version = (
3556
get_argument_from_env()
3657
)
3758
with Path("pyproject.toml").open("rb") as f:
3859
pyproject = tomllib.load(f)
3960

4061
if field == "scripts":
41-
return {
62+
result = {
4263
**pyproject["tool"]["deepmd_build_backend"]["scripts"],
4364
**extra_scripts,
4465
}
45-
elif field == "optional-dependencies":
66+
else:
4667
optional_dependencies = pyproject["tool"]["deepmd_build_backend"][
4768
"optional-dependencies"
4869
]
4970
optional_dependencies["lmp"].extend(find_libpython_requires)
5071
optional_dependencies["ipi"].extend(find_libpython_requires)
51-
return {
72+
result = {
5273
**optional_dependencies,
5374
**get_tf_requirement(tf_version),
5475
**get_pt_requirement(pt_version),
5576
}
77+
78+
return {field: result}

backend/read_env.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,5 +135,6 @@ def get_argument_from_env() -> tuple[str, list, list, dict, str, str]:
135135
def set_scikit_build_env() -> None:
136136
"""Set scikit-build environment variables before executing scikit-build."""
137137
cmake_minimum_required_version, cmake_args, _, _, _, _ = get_argument_from_env()
138-
os.environ["SKBUILD_CMAKE_MINIMUM_VERSION"] = cmake_minimum_required_version
138+
# scikit-build-core v1 expects cmake.version to be a version specifier.
139+
os.environ["SKBUILD_CMAKE_VERSION"] = f">={cmake_minimum_required_version}"
139140
os.environ["SKBUILD_CMAKE_ARGS"] = ";".join(cmake_args)

deepmd/dpmodel/atomic_model/base_atomic_model.py

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,51 @@ def forward_common_atomic(
320320
atom_mask = xp_take_first_n(ext_atom_mask, 1, nloc)
321321
return self._finalize_atomic_ret(ret_dict, atom_mask, atype)
322322

323+
def _prepare_graph_nodes(
324+
self,
325+
n_node: Array,
326+
n_local: Array | None,
327+
atype: Array,
328+
reference: Array,
329+
) -> tuple[Array, Array]:
330+
"""Apply node masks shared by generic and compact graph forwards."""
331+
xp = array_api_compat.array_namespace(reference)
332+
atype = xp.asarray(atype, device=array_api_compat.device(reference))
333+
atom_mask = self.make_atom_mask(atype)
334+
atype_clamped = xp.where(atom_mask, atype, xp.zeros_like(atype))
335+
output_mask = atom_mask
336+
if n_local is not None:
337+
from deepmd.dpmodel.utils.neighbor_graph import (
338+
node_ownership_mask,
339+
)
340+
341+
output_mask = output_mask & node_ownership_mask(
342+
n_node,
343+
n_local,
344+
atype.shape[0],
345+
)
346+
if self.atom_excl is not None:
347+
output_mask = xp.logical_and(
348+
output_mask,
349+
self.atom_excl.build_type_exclude_mask(atype_clamped),
350+
)
351+
return atype_clamped, output_mask
352+
353+
def _prepare_graph_inputs(
354+
self,
355+
graph: "NeighborGraph",
356+
atype: Array,
357+
) -> tuple["NeighborGraph", Array, Array]:
358+
"""Apply graph masks shared by standard and fused atomic forwards."""
359+
atype_clamped, output_mask = self._prepare_graph_nodes(
360+
graph.n_node,
361+
graph.n_local,
362+
atype,
363+
graph.edge_vec,
364+
)
365+
self._assert_graph_pair_excluded(graph, atype_clamped)
366+
return graph, atype_clamped, output_mask
367+
323368
def forward_common_atomic_graph(
324369
self,
325370
graph: "NeighborGraph",
@@ -365,17 +410,7 @@ def forward_common_atomic_graph(
365410
the result dict on the flat node axis, defined by the `FittingOutputDef`.
366411
367412
"""
368-
xp = array_api_compat.array_namespace(graph.edge_vec)
369-
atype = xp.asarray(atype, device=array_api_compat.device(graph.edge_vec))
370-
atom_mask = self.make_atom_mask(atype) # (N,) bool
371-
atype_clamped = xp.where(atom_mask, atype, xp.zeros_like(atype))
372-
# NOTE: model-level ``pair_exclude_types`` is NOT applied here. It is a
373-
# graph-BUILD transform (decision #18) already folded into
374-
# ``graph.edge_mask`` by the NeighborGraph builder (Python) or
375-
# ``applyPairExclusion`` (C++); this method consumes a pre-excluded graph.
376-
# Fail-safe (eager only): guard against a caller that skipped the build
377-
# seam, which would silently INCLUDE excluded pairs (fail-open).
378-
self._assert_graph_pair_excluded(graph, atype_clamped)
413+
graph, atype_clamped, output_mask = self._prepare_graph_inputs(graph, atype)
379414
ret_dict = self.forward_atomic_graph(
380415
graph,
381416
atype_clamped,
@@ -384,7 +419,7 @@ def forward_common_atomic_graph(
384419
charge_spin=charge_spin,
385420
comm_dict=comm_dict,
386421
)
387-
return self._finalize_atomic_ret(ret_dict, atom_mask, atype)
422+
return self._finalize_atomic_ret(ret_dict, output_mask, atype)
388423

389424
def _assert_nlist_pair_excluded(self, nlist: Array, extended_atype: Array) -> None:
390425
"""Fail-safe: assert the nlist reaching the dense seam is pre-excluded.
@@ -498,10 +533,16 @@ def _finalize_atomic_ret(
498533
499534
"""
500535
xp = array_api_compat.array_namespace(atype)
501-
ret_dict = self.apply_out_stat(ret_dict, atype)
536+
safe_atype = xp.where(
537+
self.make_atom_mask(atype),
538+
atype,
539+
xp.zeros_like(atype),
540+
)
541+
ret_dict = self.apply_out_stat(ret_dict, safe_atype)
502542
if self.atom_excl is not None:
503543
atom_mask = xp.logical_and(
504-
atom_mask, self.atom_excl.build_type_exclude_mask(atype)
544+
atom_mask,
545+
self.atom_excl.build_type_exclude_mask(safe_atype),
505546
)
506547
lead = atom_mask.shape # (nf, nloc) dense | (N,) graph
507548
for kk in ret_dict.keys():

deepmd/dpmodel/atomic_model/dp_atomic_model.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,46 @@
3737
)
3838

3939

40+
def _extend_graph_aparam(
41+
aparam: Array,
42+
n_node: Array,
43+
n_local: Array,
44+
n_total: int,
45+
) -> Array:
46+
"""Expand frame-local atomic parameters onto a local-plus-halo node axis."""
47+
import array_api_compat
48+
49+
from deepmd.dpmodel.utils.neighbor_graph import (
50+
frame_id_from_n_node,
51+
node_ownership_mask,
52+
)
53+
54+
xp = array_api_compat.array_namespace(aparam, n_node, n_local)
55+
frame_id = frame_id_from_n_node(n_node, n_total=n_total)
56+
frame_end = xp.cumulative_sum(n_node)
57+
frame_start = frame_end - n_node
58+
node_index = xp.arange(
59+
n_total,
60+
dtype=n_node.dtype,
61+
device=array_api_compat.device(n_node),
62+
)
63+
index_in_frame = node_index - xp.take(frame_start, frame_id, axis=0)
64+
local_capacity = aparam.shape[1]
65+
sentinel = xp.zeros(
66+
(aparam.shape[0], 1, aparam.shape[2]),
67+
dtype=aparam.dtype,
68+
device=array_api_compat.device(aparam),
69+
)
70+
padded_aparam = xp.concat([aparam, sentinel], axis=1)
71+
padded_capacity = local_capacity + 1
72+
local_index = index_in_frame % padded_capacity
73+
flat_index = frame_id * padded_capacity + local_index
74+
flat_aparam = xp.reshape(padded_aparam, (-1, aparam.shape[-1]))
75+
gathered = xp.take(flat_aparam, flat_index, axis=0)
76+
ownership = node_ownership_mask(n_node, n_local, n_total)
77+
return xp.where(ownership[:, None], gathered, xp.zeros_like(gathered))
78+
79+
4080
@BaseAtomicModel.register("standard")
4181
class DPAtomicModel(BaseAtomicModel):
4282
r"""Model give atomic prediction of some physical property.
@@ -315,8 +355,22 @@ def forward_atomic_graph(
315355
# compiled-training paths when ``numb_fparam > 0``.
316356
frame_id = frame_id_from_n_node(graph.n_node, n_total=atype.shape[0])
317357
fparam_node = xp.take(fparam, frame_id, axis=0) # (N, ndf)
358+
aparam_node = aparam
359+
if aparam is not None and graph.n_local is not None and aparam.ndim == 3:
360+
aparam_node = _extend_graph_aparam(
361+
aparam,
362+
graph.n_node,
363+
graph.n_local,
364+
atype.shape[0],
365+
)
318366
return self.fitting_net.call_graph(
319-
gg, atype, gr=rot_mat, g2=None, h2=None, fparam=fparam_node, aparam=aparam
367+
gg,
368+
atype,
369+
gr=rot_mat,
370+
g2=None,
371+
h2=None,
372+
fparam=fparam_node,
373+
aparam=aparam_node,
320374
)
321375

322376
def compute_or_load_stat(

deepmd/dpmodel/common.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,12 @@ def get_xp_precision(
8585
elif precision == "global":
8686
return get_xp_precision(xp, RESERVED_PRECISION_DICT[GLOBAL_NP_FLOAT_PRECISION])
8787
elif precision == "bfloat16":
88-
return ml_dtypes.bfloat16
88+
# Return the backend-native bfloat16 dtype when available (e.g. torch.bfloat16),
89+
# falling back to ml_dtypes.bfloat16 for NumPy/JAX-compatible namespaces that
90+
# do not expose a native bfloat16. This fixes Code scan #5638: PyTorch tensors
91+
# cannot be cast to ml_dtypes.bfloat16 via xp.astype(), but can be cast to the
92+
# namespace's own bfloat16.
93+
return getattr(xp, "bfloat16", ml_dtypes.bfloat16)
8994
else:
9095
raise ValueError(f"unsupported precision {precision} for {xp}")
9196

deepmd/dpmodel/descriptor/dpa1.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -434,12 +434,13 @@ def uses_graph_lower(self) -> bool:
434434
"""Returns whether this descriptor supports the graph-native lower.
435435
436436
The graph-native lower (``call_graph``) covers the factorizable path
437-
AND transformer attention (``attn_layer >= 0``, NeighborGraph PR-D)
438-
with concat OR strip type-embedding. ``exclude_types`` is fully
439-
supported via
437+
and transformer attention (``attn_layer >= 0``) with concat or strip
438+
type embedding. ``exclude_types`` is applied through
440439
:func:`~deepmd.dpmodel.utils.neighbor_graph.apply_pair_exclusion`.
441-
Compressed descriptors are the remaining ineligible config and fall
442-
back to the legacy dense path, so those models keep working unchanged.
440+
Geo-compressed strip models
441+
(``geo_compress=True``, ``attn_layer == 0``) are also graph-eligible;
442+
tebd-only compression and compressed descriptors with descriptor-level
443+
exclusions stay on the legacy dense path.
443444
444445
Eligibility does NOT imply numerical interchangeability with the
445446
dense route for every config: with ``smooth_type_embedding=True``
@@ -449,13 +450,13 @@ def uses_graph_lower(self) -> bool:
449450
"""
450451
if self._graph_lower_disabled:
451452
return False
452-
# compressed descriptors have no graph kernel (geo/tebd tabulation is
453-
# dense-only); keep them on the legacy dense path.
454453
if self.compress:
455-
return False
456-
# strip is graph-eligible (per-edge factorized embedding, no neighbor
457-
# coupling); exclude_types is graph-native via ``apply_pair_exclusion``
458-
# (owned at this seam). Only compression / the disable flag force dense.
454+
return (
455+
self.geo_compress
456+
and self.se_atten.tebd_input_mode == "strip"
457+
and self.se_atten.attn_layer == 0
458+
and not self.se_atten.exclude_types
459+
)
459460
return self.se_atten.tebd_input_mode in ("concat", "strip")
460461

461462
def disable_graph_lower(self) -> None:
@@ -1782,7 +1783,7 @@ def call_graph(
17821783
Notes
17831784
-----
17841785
Known limitations:
1785-
- ``tebd_input_mode`` in {"concat", "strip"}; compressed descriptors stay dense;
1786+
- ``tebd_input_mode`` in {"concat", "strip"};
17861787
- ``exclude_types`` is applied graph-natively via ``apply_pair_exclusion``.
17871788
"""
17881789
from deepmd.dpmodel.utils.neighbor_graph import (

0 commit comments

Comments
 (0)