Skip to content

Commit 23f1236

Browse files
committed
opt dpa1
1 parent c73ecd0 commit 23f1236

17 files changed

Lines changed: 421 additions & 97 deletions

deepmd/kernels/cuda/dpa1/graph_compress.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def _forward_fake(
107107
gate_table: torch.Tensor,
108108
type_one_side: int,
109109
concat_tebd: int,
110+
write_rotation: int,
110111
smooth: int,
111112
axis: int,
112113
canonical: bool,
@@ -127,7 +128,13 @@ def _forward_fake(
127128
dev = edge_vec.device
128129
return (
129130
torch.empty(n_node, out_dim, dtype=torch.float32, device=dev),
130-
torch.empty(n_node, ng, 3, dtype=torch.float32, device=dev),
131+
torch.empty(
132+
n_node if write_rotation else 0,
133+
ng,
134+
3,
135+
dtype=torch.float32,
136+
device=dev,
137+
),
131138
torch.empty(n_node, 4, ng, dtype=torch.float32, device=dev),
132139
)
133140

@@ -160,6 +167,7 @@ def _setup_context(ctx: Any, inputs: tuple, output: tuple) -> None:
160167
gate_table,
161168
type_one_side,
162169
concat_tebd,
170+
_write_rotation,
163171
smooth,
164172
axis,
165173
canonical,
@@ -269,7 +277,7 @@ def _backward(
269277
protection,
270278
nnei,
271279
)
272-
return (d_edge_vec,) + (None,) * 24
280+
return (d_edge_vec,) + (None,) * 25
273281

274282

275283
# ======================================================================
@@ -493,6 +501,7 @@ def _cpu_forward(
493501
gate_table: torch.Tensor,
494502
type_one_side: int,
495503
concat_tebd: int,
504+
write_rotation: int,
496505
smooth: int,
497506
axis: int,
498507
canonical: bool,
@@ -540,6 +549,8 @@ def _cpu_forward(
540549
nnei,
541550
n_node,
542551
)
552+
if not write_rotation:
553+
rot_mat = rot_mat.new_empty(0, ng, 3)
543554
return grrg, rot_mat, gr
544555

545556

@@ -717,6 +728,7 @@ def dpa1_graph_compress(
717728
gate_table,
718729
int(se.type_one_side),
719730
int(desc.concat_output_tebd),
731+
1,
720732
int(se.smooth),
721733
int(se.axis_neuron),
722734
bool(graph.destination_sorted),
@@ -761,7 +773,9 @@ def dpa1_graph_compress_energy_force(
761773
retained. Destination/source CSR tensors are part of the graph contract and
762774
are reused by both descriptor and force kernels. A graph marked
763775
``destination_sorted`` selects direct destination-major addressing; edge
764-
masks remain authoritative in both addressing modes.
776+
masks remain authoritative in both addressing modes. This inference-only
777+
path suppresses the unused rotation output, and fitting backward does not
778+
retain the descriptor after its last value use.
765779
766780
Parameters
767781
----------
@@ -850,6 +864,7 @@ def dpa1_graph_compress_energy_force(
850864
gate_table,
851865
int(se.type_one_side),
852866
int(desc.concat_output_tebd),
867+
0,
853868
int(se.smooth),
854869
int(se.axis_neuron),
855870
bool(graph.destination_sorted),
@@ -911,12 +926,12 @@ def dpa1_graph_compress_energy_force(
911926
).index_add_(0, frame_index, atom_energy)
912927
descriptor_gradient = torch.ops.deepmd.graph_fitting_backward(
913928
torch.ones_like(atom_energy),
914-
descriptor,
915929
fitting_saved,
916930
weights,
917931
residuals,
918932
head_weight,
919933
)
934+
del descriptor, fitting_saved
920935
edge_gradient = torch.ops.deepmd.dpa1_graph_compress_backward(
921936
descriptor_gradient,
922937
None,

deepmd/kernels/cuda/dpa1/graph_energy_force.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,9 @@ def _cpu(
216216
energy = torch.zeros(nf, 1, dtype=atom_e.dtype, device=atom_e.device)
217217
energy = energy.index_add(0, frame_id, atom_e)
218218
d_grrg = torch.ops.deepmd.graph_fitting_backward(
219-
torch.ones_like(atom_e), grrg, fit_saved, fit_ws, fit_resnets, w_head
219+
torch.ones_like(atom_e), fit_saved, fit_ws, fit_resnets, w_head
220220
)
221+
del grrg, fit_saved
221222
g_e = torch.ops.deepmd.dpa1_graph_descriptor_backward(
222223
d_grrg,
223224
None,

deepmd/kernels/cuda/graph_fitting.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
flat buffer of ``adot chunks`` (the activations themselves are a forward-only
2121
transient); it is an autograd save, never a user-facing value, and receives
2222
no gradient (``set_materialize_grads(False)``).
23+
* The backward infers the node count and descriptor width from the saved
24+
derivative buffer and first weight. It deliberately does not retain the
25+
descriptor tensor, allowing inference memory planners to reuse descriptor
26+
storage for its gradient after the fitting forward.
2327
* The head bias is passed as a device tensor, not a Python float: reading a
2428
value host-side (``.item()``) inside the dispatch path would fail under
2529
symbolic tracing (``GuardOnDataDependentSymNode``) and force a GPU sync per
@@ -121,28 +125,29 @@ def _forward_fake(
121125

122126
def _backward_fake(
123127
d_e: torch.Tensor,
124-
x: torch.Tensor,
125128
saved: torch.Tensor,
126129
ws: list[torch.Tensor],
127130
resnets: list[int],
128131
w_head: torch.Tensor,
129132
) -> torch.Tensor:
130-
return torch.empty_like(x)
133+
total_width = sum(int(w.shape[1]) for w in ws)
134+
n_node = saved.shape[0] // total_width
135+
return saved.new_empty(n_node, ws[0].shape[0])
131136

132137

133138
def _setup_context(ctx: Any, inputs: tuple, output: tuple) -> None:
134139
x, atype, ws, bs, idts, resnets, w_head, b_head, bias_atom_e, act = inputs
135140
_e, saved = output
136-
ctx.save_for_backward(x, saved, w_head, *ws)
141+
ctx.save_for_backward(saved, w_head, *ws)
137142
ctx.n_layers = len(ws)
138143
ctx.resnets = resnets
139144
ctx.set_materialize_grads(False)
140145

141146

142147
def _backward(ctx: Any, d_e: torch.Tensor, d_saved: Any) -> tuple:
143-
x, saved, w_head, *ws = ctx.saved_tensors
148+
saved, w_head, *ws = ctx.saved_tensors
144149
d_x = torch.ops.deepmd.graph_fitting_backward(
145-
d_e, x, saved, list(ws), ctx.resnets, w_head
150+
d_e, saved, list(ws), ctx.resnets, w_head
146151
)
147152
none_list = [None] * ctx.n_layers
148153
return (d_x, None, none_list, none_list, none_list, None, None, None, None, None)
@@ -192,13 +197,13 @@ def _cpu_forward(
192197

193198
def _cpu_backward(
194199
d_e: torch.Tensor,
195-
x: torch.Tensor,
196200
saved: torch.Tensor,
197201
ws: list[torch.Tensor],
198202
resnets: list[int],
199203
w_head: torch.Tensor,
200204
) -> torch.Tensor:
201-
n_node = x.shape[0]
205+
total_width = sum(int(w.shape[1]) for w in ws)
206+
n_node = saved.shape[0] // total_width
202207
offset = [0]
203208
for w in ws:
204209
offset.append(offset[-1] + int(w.shape[1]))

deepmd/pt_expt/infer/deep_eval.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,8 +1752,10 @@ def _eval_model_graph(
17521752
destination_order, destination_row_ptr, source_row_ptr, source_order,
17531753
fparam, aparam, charge_spin)`` to the exported forward. The AOTI artifact's edge axis
17541754
is DYNAMIC (B2.0), so no ``edge_capacity`` padding is needed. The
1755-
forward returns the LOCAL public keys directly, so results are reshaped
1756-
without ``communicate_extended_output``.
1755+
``graph_edge_dtype`` metadata selects float32 geometry for compressed
1756+
DPA1 and float64 for generic graph descriptors. The forward returns the
1757+
LOCAL public keys directly, so results are reshaped without
1758+
``communicate_extended_output``.
17571759
"""
17581760
from deepmd.pt_expt.utils.env import (
17591761
DEVICE,
@@ -1781,7 +1783,16 @@ def _eval_model_graph(
17811783
edge_index_t = torch.as_tensor(
17821784
graph.edge_index, dtype=torch.int64, device=DEVICE
17831785
)
1784-
edge_vec_t = torch.as_tensor(graph.edge_vec, dtype=torch.float64, device=DEVICE)
1786+
edge_dtype = (
1787+
torch.float32
1788+
if self.metadata.get("graph_edge_dtype") == "float32"
1789+
else torch.float64
1790+
)
1791+
edge_vec_t = torch.as_tensor(
1792+
graph.edge_vec,
1793+
dtype=edge_dtype,
1794+
device=DEVICE,
1795+
)
17851796
edge_mask_t = torch.as_tensor(graph.edge_mask, dtype=torch.bool, device=DEVICE)
17861797
destination_order_t = torch.as_tensor(
17871798
graph.destination_order,

deepmd/pt_expt/utils/serialization.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ def build_synthetic_graph_inputs(
375375
nloc: int = 7,
376376
*,
377377
dtype: torch.dtype,
378+
edge_dtype: torch.dtype | None = None,
378379
device: torch.device | None = None,
379380
want_fparam: bool = True,
380381
want_aparam: bool = True,
@@ -412,9 +413,12 @@ def build_synthetic_graph_inputs(
412413
nloc : int
413414
Number of local atoms per frame (``N == nframes * nloc``).
414415
dtype : torch.dtype
415-
Float precision of ``coord``/``edge_vec``/``fparam``/... . The exported
416-
``.pt2`` is float64-only (C++ ABI); training passes
417-
``GLOBAL_PT_FLOAT_PRECISION``.
416+
Float precision of ``coord``/``fparam`` and other conditioning inputs.
417+
edge_dtype : torch.dtype, optional
418+
Precision of ``edge_vec``. Defaults to ``dtype``. A compressed DPA1
419+
graph artifact uses float32 because its descriptor and analytical
420+
backward both compute in float32; generic graph artifacts preserve the
421+
model input precision.
418422
device : torch.device, optional
419423
Target device. Defaults to ``deepmd.pt_expt.utils.env.DEVICE``; the
420424
export path passes ``cpu`` explicitly (make_fx traces on CPU).
@@ -431,6 +435,8 @@ def build_synthetic_graph_inputs(
431435

432436
if device is None:
433437
device = _env.DEVICE
438+
if edge_dtype is None:
439+
edge_dtype = dtype
434440

435441
rcut = model.get_rcut()
436442
ntypes = len(model.get_type_map())
@@ -479,7 +485,7 @@ def build_synthetic_graph_inputs(
479485
atype_t.reshape(-1),
480486
graph.n_node,
481487
graph.edge_index,
482-
graph.edge_vec,
488+
graph.edge_vec.to(edge_dtype),
483489
graph.edge_mask,
484490
graph.destination_order,
485491
graph.destination_row_ptr,
@@ -644,6 +650,20 @@ def _build_dynamic_shapes(
644650
return (*base, None, None, None, None, None, None, None, None)
645651

646652

653+
def _graph_edge_dtype(model: torch.nn.Module, lower_kind: str) -> str:
654+
"""Return the graph edge-vector dtype encoded by the deployment artifact.
655+
656+
Geometrically compressed DPA1 evaluates both descriptor directions in
657+
float32 and therefore accepts float32 geometry directly. Other graph
658+
descriptors retain the model-agnostic float64 geometry ABI.
659+
"""
660+
atomic_model = getattr(model, "atomic_model", None)
661+
descriptor = getattr(atomic_model, "descriptor", None)
662+
if lower_kind == "graph" and bool(getattr(descriptor, "geo_compress", False)):
663+
return "float32"
664+
return "float64"
665+
666+
647667
def _collect_metadata(
648668
model: torch.nn.Module, is_spin: bool = False, lower_kind: str = "nlist"
649669
) -> dict:
@@ -764,6 +784,7 @@ def _probe_has_message_passing(obj: object) -> bool | None:
764784
# "graph" → NeighborGraph (atype, n_node, edge_index, edge_vec, edge_mask)
765785
# The C++ loader branches on this to build the matching inputs.
766786
meta["lower_input_kind"] = "graph" if lower_kind == "graph" else "nlist"
787+
meta["graph_edge_dtype"] = _graph_edge_dtype(model, lower_kind)
767788
return meta
768789

769790

@@ -1066,14 +1087,21 @@ def _trace_and_export(
10661087
nnei = sum(model.get_sel())
10671088
e_sample = math.ceil(1.25 * nloc_sample * nnei)
10681089

1069-
# make_fx traces on CPU; the .pt2 C++ ABI is float64-only. Pass device
1070-
# and dtype explicitly instead of mutating the module-level env.DEVICE.
1090+
# make_fx traces on CPU. Conditioning inputs retain the model-agnostic
1091+
# float64 ABI; compressed DPA1 graph geometry enters directly in its
1092+
# float32 compute precision, as recorded in metadata.
1093+
edge_dtype = (
1094+
torch.float32
1095+
if metadata["graph_edge_dtype"] == "float32"
1096+
else torch.float64
1097+
)
10711098
sample_inputs = build_synthetic_graph_inputs(
10721099
model,
10731100
e_max=e_sample,
10741101
nframes=2,
10751102
nloc=nloc_sample,
10761103
dtype=torch.float64,
1104+
edge_dtype=edge_dtype,
10771105
device=torch.device("cpu"),
10781106
)
10791107

source/api_cc/include/DeepPot.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,20 @@ class DeepPotBackend : public DeepBaseModelBackend {
330330
const std::vector<double>& aparam,
331331
const int nall_nodes,
332332
const InputNlist* comm_nlist);
333+
virtual void compute_edges_gpu(double* d_atom_energy,
334+
double* d_force,
335+
double* d_atom_virial,
336+
const double* d_coord,
337+
const int* d_atype,
338+
const int* d_edge_index,
339+
const float* d_edge_vec,
340+
const int nloc,
341+
const int nedge,
342+
const std::vector<double>& fparam,
343+
const std::vector<double>& aparam,
344+
const int nall_nodes,
345+
const InputNlist* comm_nlist);
346+
virtual bool uses_fp32_edge_vectors() const;
333347
};
334348

335349
/**
@@ -751,6 +765,30 @@ class DeepPot : public DeepBaseModel {
751765
const int nall_nodes = 0,
752766
const InputNlist* comm_nlist = nullptr);
753767

768+
/**
769+
* @brief Device-edge inference with FP32 edge vectors.
770+
*
771+
* Call this overload only when ``uses_fp32_edge_vectors()`` is true.
772+
*/
773+
void compute_edges_gpu(double* d_atom_energy,
774+
double* d_force,
775+
double* d_atom_virial,
776+
const double* d_coord,
777+
const int* d_atype,
778+
const int* d_edge_index,
779+
const float* d_edge_vec,
780+
const int nloc,
781+
const int nedge,
782+
const std::vector<double>& fparam,
783+
const std::vector<double>& aparam,
784+
const int nall_nodes = 0,
785+
const InputNlist* comm_nlist = nullptr);
786+
787+
/**
788+
* @brief Whether the loaded artifact expects FP32 device edge vectors.
789+
*/
790+
bool uses_fp32_edge_vectors() const;
791+
754792
int dim_chg_spin() const;
755793

756794
protected:

0 commit comments

Comments
 (0)