Skip to content

Commit ecacb43

Browse files
committed
fix
1 parent 6373f24 commit ecacb43

13 files changed

Lines changed: 164 additions & 75 deletions

File tree

deepmd/kernels/cuda/dpa1/graph_compress.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
"op_available",
3636
]
3737

38-
_TILE_EDGES = 128 # matches kTileEdges in dpa1_graph_compress.cu
39-
4038
# Instantiated table widths in dpa1_graph_compress.cu; each divides the 256
4139
# threads per block, so the moment walk splits channels evenly. An arbitrary
4240
# ``ng`` is served by the smallest width that is at least ``ng``, zero-padding

deepmd/kernels/cuda/dpa1/graph_energy_force.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,13 @@ def _cpu(
245245
protection,
246246
nnei,
247247
)
248+
# Assemble force / virial in the descriptor weight precision, matching
249+
# ``_fake`` and the CUDA operator; the sub-operator would otherwise return
250+
# fp64 whenever the edge inputs are fp64.
251+
fprec = w1.dtype
248252
force, atom_virial, virial = torch.ops.deepmd.edge_force_virial(
249-
g_e,
250-
edge_vec,
253+
g_e.to(fprec),
254+
edge_vec.to(fprec),
251255
edge_index,
252256
edge_mask,
253257
n_node,

deepmd/kernels/cuda/graph_fitting.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
1717
Usage and pitfalls
1818
------------------
19-
* The forward's second output packs the layer activations and their
20-
derivatives as one flat buffer ``[h chunks | adot chunks]``; it is an
21-
autograd save, never a user-facing value, and receives no gradient
22-
(``set_materialize_grads(False)``).
19+
* The forward's second output packs the layer activation derivatives as one
20+
flat buffer of ``adot chunks`` (the activations themselves are a forward-only
21+
transient); it is an autograd save, never a user-facing value, and receives
22+
no gradient (``set_materialize_grads(False)``).
2323
* The head bias is passed as a device tensor, not a Python float: reading a
2424
value host-side (``.item()``) inside the dispatch path would fail under
2525
symbolic tracing (``GuardOnDataDependentSymNode``) and force a GPU sync per
@@ -115,7 +115,7 @@ def _forward_fake(
115115
total_width = sum(int(w.shape[1]) for w in ws)
116116
return (
117117
x.new_empty(n_node, 1, dtype=torch.float64),
118-
x.new_empty(2 * n_node * total_width, dtype=torch.float32),
118+
x.new_empty(n_node * total_width, dtype=torch.float32),
119119
)
120120

121121

@@ -163,7 +163,7 @@ def _cpu_forward(
163163
bias_atom_e: torch.Tensor,
164164
act: int,
165165
) -> tuple[torch.Tensor, torch.Tensor]:
166-
hs, adots = [], []
166+
adots = []
167167
cur = x.to(torch.float32)
168168
for w, b, idt, res in zip(ws, bs, idts, resnets):
169169
pre = cur @ w
@@ -178,17 +178,15 @@ def _cpu_forward(
178178
if idt.numel():
179179
a = a * idt
180180
adot = adot * idt
181-
y = a + cur if (res and w.shape[0] == w.shape[1]) else a
182-
hs.append(y)
183181
adots.append(adot)
184-
cur = y
182+
cur = a + cur if (res and w.shape[0] == w.shape[1]) else a
185183
e = (cur @ w_head[:, None]).to(torch.float64)
186184
if b_head.numel():
187185
e = e + b_head.to(torch.float64)
188186
e = e + bias_atom_e[atype][:, None]
189-
# Chunk layout mirrors the CUDA op: [h chunks | adot chunks], each a
190-
# contiguous row-major (N, w_l) block.
191-
saved = torch.cat([t.reshape(-1) for t in hs + adots])
187+
# Chunk layout mirrors the CUDA op: adot chunks only, each a contiguous
188+
# row-major (N, w_l) block; the backward needs only these derivatives.
189+
saved = torch.cat([t.reshape(-1) for t in adots])
192190
return e, saved
193191

194192

@@ -201,13 +199,11 @@ def _cpu_backward(
201199
w_head: torch.Tensor,
202200
) -> torch.Tensor:
203201
n_node = x.shape[0]
204-
total_width = sum(int(w.shape[1]) for w in ws)
205202
offset = [0]
206203
for w in ws:
207204
offset.append(offset[-1] + int(w.shape[1]))
208-
base = n_node * total_width
209205
adots = [
210-
saved[base + offset[l] * n_node : base + offset[l + 1] * n_node].reshape(
206+
saved[offset[l] * n_node : offset[l + 1] * n_node].reshape(
211207
n_node, int(ws[l].shape[1])
212208
)
213209
for l in range(len(ws))

deepmd/kernels/triton/dpa1/sweep_tile_configs.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@
7373
# the per-program register tile small, large values amortize launch overhead.
7474
_BLOCK_E_CANDIDATES = (1, 2, 4, 8, 16, 32)
7575
_WARP_CANDIDATES = (2, 4, 8)
76-
# Reject a configuration whose forward+backward is slower than this multiple of
77-
# the best observed time; a large backward regression signals a register spill.
78-
_REJECT_RATIO = 3.0
7976
_REL_TOL = 1e-5
8077
# An edge_conv candidate is only recorded when it beats the universal default by
8178
# more than ``1 - _EDGE_WIN_RATIO`` (5%); otherwise the default is kept, so a
@@ -186,16 +183,20 @@ def fwd_bwd(bn: int, nw: int) -> None:
186183

187184
def _make_edge_inputs(
188185
n_edge: int, n_node: int, ng: int, h1: int, device: torch.device
189-
) -> tuple[Tensor, Tensor, Tensor, Tensor, Tensor, Tensor]:
186+
) -> tuple[Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor]:
187+
p = 4096
190188
z2 = torch.randn(n_edge, ng, dtype=torch.float32, device=device)
191189
h1t = torch.randn(n_edge, h1, dtype=torch.float32, device=device)
192190
idt = torch.ones(ng, dtype=torch.float32, device=device)
191+
tt = torch.randn(p, ng, dtype=torch.float32, device=device) * 0.3
192+
idx = torch.randint(0, p, (n_edge,), dtype=torch.int64, device=device)
193+
sw = torch.rand(n_edge, dtype=torch.float32, device=device)
193194
rr = torch.randn(n_edge, 4, dtype=torch.float32, device=device)
194195
dst = torch.randint(0, n_node, (n_edge,), dtype=torch.int64, device=device)
195196
edge_mask = (torch.rand(n_edge, dtype=torch.float32, device=device) > 0.05).to(
196197
dtype=torch.float32
197198
)
198-
return z2, h1t, idt, rr, dst, edge_mask
199+
return z2, h1t, idt, tt, idx, sw, rr, dst, edge_mask
199200

200201

201202
def sweep_edge(
@@ -238,22 +239,31 @@ def sweep_edge(
238239
device = device or torch.device("cuda")
239240
torch.backends.cuda.matmul.allow_tf32 = False
240241
act = 0
241-
z2, h1t, idt, rr, dst, em = _make_edge_inputs(n_edge, n_node, ng, h1, device)
242-
ref = _edge_conv_reference(z2, h1t, idt, rr, dst, em, n_node, resnet_mult, act)
242+
# The launch configuration is keyed by (ng, H1) and is independent of the
243+
# tebd-input mode; the strip gate (gated = 1) is the register-heaviest case,
244+
# so its optimum is a safe upper bound for concat (gated = 0).
245+
gated = 1
246+
# Shared leading arguments of the three edge_conv entry points, ordered to
247+
# match their signatures so each call splats this tuple and appends only its
248+
# launch configuration.
249+
edge_args = (
250+
*_make_edge_inputs(n_edge, n_node, ng, h1, device),
251+
n_node,
252+
resnet_mult,
253+
act,
254+
gated,
255+
)
256+
ref = _edge_conv_reference(*edge_args)
243257
gout = torch.randn_like(ref)
244258

245259
def fwd_bwd(be: int, nw: int) -> None:
246-
_edge_conv_fwd_impl(z2, h1t, idt, rr, dst, em, n_node, resnet_mult, act, be, nw)
247-
_edge_conv_bwd_impl(
248-
gout, z2, h1t, idt, rr, dst, em, n_node, resnet_mult, act, be, nw
249-
)
260+
_edge_conv_fwd_impl(*edge_args, be, nw)
261+
_edge_conv_bwd_impl(gout, *edge_args, be, nw)
250262

251263
results: dict[tuple[int, int], float] = {}
252264
for be, nw in itertools.product(_BLOCK_E_CANDIDATES, _WARP_CANDIDATES):
253265
try:
254-
out = _edge_conv_fwd_impl(
255-
z2, h1t, idt, rr, dst, em, n_node, resnet_mult, act, be, nw
256-
)
266+
out = _edge_conv_fwd_impl(*edge_args, be, nw)
257267
rel = (out - ref).abs().max().item() / ref.abs().max().item()
258268
if rel > _REL_TOL:
259269
continue

deepmd/kernels/triton/env_mat.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,6 @@ def _env_mat_reference(
475475
use_exp_switch: bool,
476476
) -> tuple[Tensor, Tensor, Tensor]:
477477
"""Eager environment matrix, numerically identical to ``prod_env_mat``."""
478-
nf, nloc, nnei = nlist.shape
479478
mask, _, d, length_safe, _ = _geometry(coord, nlist)
480479
q = length_safe + protection
481480
sw_raw, _ = _switch_reference(length_safe, rcut_smth, rcut, use_exp_switch)

deepmd/pt/model/descriptor/se_atten.py

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -667,15 +667,10 @@ def forward(
667667
rr,
668668
gated=0,
669669
)
670-
# ``gg`` (the pair representation g2) is not formed on this path.
671-
gg = torch.empty(
672-
nframes,
673-
nloc,
674-
self.nnei,
675-
self.filter_neuron[-1],
676-
dtype=xyz_scatter.dtype,
677-
device=xyz_scatter.device,
678-
)
670+
# ``gg`` (the pair representation g2) is not formed on this path;
671+
# it is returned as ``None``, so a zero-element placeholder keeps
672+
# ``gg`` a tensor without the full (nf, nloc, nnei, ng) allocation.
673+
gg = xyz_scatter.new_empty(0)
679674
g2_is_none = True
680675
else:
681676
# nfnl x nnei x ng
@@ -755,15 +750,10 @@ def forward(
755750
rr,
756751
gated=1,
757752
)
758-
# ``gg`` (the pair representation g2) is not formed on this path.
759-
gg = torch.empty(
760-
nframes,
761-
nloc,
762-
self.nnei,
763-
self.filter_neuron[-1],
764-
dtype=xyz_scatter.dtype,
765-
device=xyz_scatter.device,
766-
)
753+
# ``gg`` (the pair representation g2) is not formed on this path;
754+
# it is returned as ``None``, so a zero-element placeholder keeps
755+
# ``gg`` a tensor without the full (nf, nloc, nnei, ng) allocation.
756+
gg = xyz_scatter.new_empty(0)
767757
g2_is_none = True
768758
else:
769759
# (nf x nl) x nnei x ng

deepmd/pt_expt/descriptor/se_atten_v2.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,44 @@ def fused_energy_force_graph(self, *args: Any, **kwargs: Any) -> Any:
7171

7272
return DescrptDPA1.fused_energy_force_graph(self, *args, **kwargs)
7373

74+
# Graph-lower routing and the fused-kernel helpers it dispatches to,
75+
# forwarded to DescrptDPA1 so the accelerated CUDA / Triton / tabulated
76+
# graph paths serve se_atten_v2 rather than only the dpmodel reference.
77+
def call_graph(self, *args: Any, **kwargs: Any) -> Any:
78+
from deepmd.pt_expt.descriptor.dpa1 import (
79+
DescrptDPA1,
80+
)
81+
82+
return DescrptDPA1.call_graph(self, *args, **kwargs)
83+
84+
def _call_graph_cuda(self, *args: Any, **kwargs: Any) -> Any:
85+
from deepmd.pt_expt.descriptor.dpa1 import (
86+
DescrptDPA1,
87+
)
88+
89+
return DescrptDPA1._call_graph_cuda(self, *args, **kwargs)
90+
91+
def _call_graph_cuda_compress(self, *args: Any, **kwargs: Any) -> Any:
92+
from deepmd.pt_expt.descriptor.dpa1 import (
93+
DescrptDPA1,
94+
)
95+
96+
return DescrptDPA1._call_graph_cuda_compress(self, *args, **kwargs)
97+
98+
def _call_graph_triton(self, *args: Any, **kwargs: Any) -> Any:
99+
from deepmd.pt_expt.descriptor.dpa1 import (
100+
DescrptDPA1,
101+
)
102+
103+
return DescrptDPA1._call_graph_triton(self, *args, **kwargs)
104+
105+
def _call_graph_compress_reference(self, *args: Any, **kwargs: Any) -> Any:
106+
from deepmd.pt_expt.descriptor.dpa1 import (
107+
DescrptDPA1,
108+
)
109+
110+
return DescrptDPA1._call_graph_compress_reference(self, *args, **kwargs)
111+
74112
def _call_triton(self, *args: Any, **kwargs: Any) -> Any:
75113
from deepmd.pt_expt.descriptor.dpa1 import (
76114
DescrptDPA1,

deepmd/pt_expt/model/edge_transform_output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def edge_energy_deriv(
105105
):
106106
g_e = g_e.to(force_precision)
107107
edge_vec = edge_vec.to(force_precision)
108-
if cuda_infer_level() >= 1 and fused_scatter_available():
108+
if cuda_infer_level() >= 1 and not create_graph and fused_scatter_available():
109109
n_cap = node_capacity if node_capacity is not None else int(n_node.sum())
110110
force, atom_virial, virial = fused_edge_force_virial(
111111
g_e, edge_vec, edge_index, edge_mask, n_node, n_cap, do_atomic_virial

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ environment-pass = [
305305
]
306306
before-all = [
307307
"""if [ ! -z "${DP_PKG_NAME}" ]; then sed -i "s/name = \\"deepmd-kit\\"/name = \\"${DP_PKG_NAME}\\"/g" pyproject.toml; fi""",
308-
"""{ if [ -n "${CUDA_VERSION}" ] && [ "$(uname -m)" = "x86_64" ] ; then yum config-manager --add-repo http://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo && yum install -y cuda-nvcc-${CUDA_VERSION/./-} cuda-cudart-devel-${CUDA_VERSION/./-}; fi }""",
308+
"""{ if [ -n "${CUDA_VERSION}" ] && [ "$(uname -m)" = "x86_64" ] ; then yum config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo && yum install -y cuda-nvcc-${CUDA_VERSION/./-} cuda-cudart-devel-${CUDA_VERSION/./-}; fi }""",
309309
]
310310
before-build = [
311311
]

source/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ if(ENABLE_PYTORCH AND NOT DEEPMD_C_ROOT)
356356
endif()
357357
endif()
358358
find_package(Torch REQUIRED)
359+
# TorchConfig populates TORCH_CUDA_LIBRARIES only when PyTorch itself was
360+
# built with CUDA. The fused graph-lower operators in op/pt include ATen CUDA
361+
# headers and link libtorch_cuda, so they can be compiled only against a
362+
# CUDA-enabled PyTorch: a CPU-only wheel ships the ATen/c10 CUDA headers but
363+
# neither the generated c10/cuda/impl/cuda_cmake_macros.h nor libtorch_cuda.
364+
if(TORCH_CUDA_LIBRARIES)
365+
set(DEEPMD_TORCH_HAS_CUDA TRUE)
366+
else()
367+
set(DEEPMD_TORCH_HAS_CUDA FALSE)
368+
endif()
359369
if(NOT Torch_VERSION VERSION_LESS "2.1.0")
360370
set_if_higher(CMAKE_CXX_STANDARD 17)
361371
elseif(NOT Torch_VERSION VERSION_LESS "1.5.0")

0 commit comments

Comments
 (0)