@@ -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+
647667def _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
0 commit comments