Skip to content

Commit 91ddd54

Browse files
committed
chore(pt): merge updated CuTe pull request
# Conflicts: # deepmd/pt/model/descriptor/sezm.py # deepmd/pt_expt/kernels/cute/sezm/so2/kernels/phase_a_radial_forward.py # deepmd/pt_expt/kernels/cute/sezm/so2/message_grid.py # doc/install/easy-install.md
2 parents f179f6a + 2344b4d commit 91ddd54

39 files changed

Lines changed: 944 additions & 151 deletions

deepmd/backend/backend.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ class Feature(Flag):
153153
"""The supported suffixes of the saved model.
154154
155155
The first element is considered as the default suffix."""
156+
preserves_lower_input_kind: ClassVar[bool] = False
157+
"""Whether the IO hook preserves lower-ABI metadata without materializing it.
158+
159+
Schema-neutral model containers retain ``lower_input_kind`` as provenance
160+
even though their deserializer does not accept a concrete ``lower_kind``.
161+
Executable backends instead materialize a lower ABI and must expose that
162+
choice through their deserializer signature.
163+
"""
156164

157165
@abstractmethod
158166
def is_available(self) -> bool:

deepmd/backend/dpmodel.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class DPModelBackend(Backend):
4242
"""The features of the backend."""
4343
suffixes: ClassVar[list[str]] = [".dp", ".yaml", ".yml"]
4444
"""The suffixes of the backend."""
45+
preserves_lower_input_kind: ClassVar[bool] = True
46+
"""DPModel files retain lower provenance without binding an execution ABI."""
4547

4648
def is_available(self) -> bool:
4749
"""Check if the backend is available.
@@ -106,10 +108,10 @@ def serialize_hook(self) -> Callable[[str], dict]:
106108
The serialize hook of the backend.
107109
"""
108110
from deepmd.dpmodel.utils.serialization import (
109-
load_dp_model,
111+
serialize_from_file,
110112
)
111113

112-
return load_dp_model
114+
return serialize_from_file
113115

114116
@property
115117
def deserialize_hook(self) -> Callable[[str, dict], None]:

deepmd/dpmodel/utils/serialization.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,29 @@ def convert_numpy_ndarray(x: Any) -> Any:
199199
return model_dict
200200

201201

202+
def serialize_from_file(filename: str) -> dict:
203+
"""Serialize a DPModel container for backend conversion.
204+
205+
DPModel files store model parameters rather than an executable lower ABI.
206+
Concrete provenance written by an earlier conversion is retained; a native
207+
file without provenance reports ``"auto"`` so the executable target selects
208+
a compatible lower from the model capabilities.
209+
210+
Parameters
211+
----------
212+
filename : str
213+
The DPModel filename.
214+
215+
Returns
216+
-------
217+
dict
218+
The serialized model data with declared lower-input semantics.
219+
"""
220+
model_dict = load_dp_model(filename)
221+
model_dict.setdefault("lower_input_kind", "auto")
222+
return model_dict
223+
224+
202225
def format_big_number(x: int) -> str:
203226
"""Format a big number with suffixes.
204227

deepmd/entrypoints/convert_backend.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ def convert_backend(
3030
If True, export .pt2/.pte models with per-atom virial correction.
3131
This adds ~2.5x inference cost. Default False. Silently ignored
3232
(with a warning) for backends that don't support the flag.
33+
34+
Notes
35+
-----
36+
Backend conversion preserves an explicit ``lower_input_kind`` reported by
37+
the source serializer. Sources without this metadata retain the target's
38+
automatic lower selection for backward compatibility. A target backend
39+
that cannot represent an explicit non-dense lower is rejected rather than
40+
silently changing the model function.
3341
"""
3442
inp_backend: Backend = Backend.detect_backend_by_model(INPUT)()
3543
out_backend: Backend = Backend.detect_backend_by_model(OUTPUT)()
@@ -40,8 +48,21 @@ def convert_backend(
4048

4149
sig = inspect.signature(out_hook)
4250
hook_kwargs: dict[str, Any] = {}
51+
lower_input_kind = data.get("lower_input_kind")
4352
if "lower_kind" in sig.parameters:
44-
hook_kwargs["lower_kind"] = "auto"
53+
hook_kwargs["lower_kind"] = (
54+
lower_input_kind if lower_input_kind is not None else "auto"
55+
)
56+
elif (
57+
lower_input_kind not in (None, "auto", "nlist")
58+
and not out_backend.preserves_lower_input_kind
59+
):
60+
raise ValueError(
61+
f"Cannot preserve lower_input_kind {lower_input_kind!r} when "
62+
f"converting to output backend {out_backend.name!r}: its "
63+
"deserializer does not accept a lower_kind. Retrain or freeze the "
64+
"model with that backend instead of converting this artifact."
65+
)
4566
if "do_atomic_virial" in sig.parameters:
4667
hook_kwargs["do_atomic_virial"] = atomic_virial
4768
elif atomic_virial:

deepmd/jax/utils/serialization.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ def restore_model(model_params: dict, model_state: dict) -> BaseModel:
427427
"jax_version": jax.__version__,
428428
"model": model_dict,
429429
"model_def_script": model_def_script,
430+
"lower_input_kind": "nlist",
430431
"@variables": {},
431432
}
432433
if min_nbor_dist is not None:
@@ -436,6 +437,7 @@ def restore_model(model_params: dict, model_state: dict) -> BaseModel:
436437
data = load_dp_model(model_file)
437438
data.pop("constants")
438439
data["@variables"].pop("stablehlo")
440+
data["lower_input_kind"] = "nlist"
439441
return data
440442
elif model_file.endswith(".savedmodel"):
441443
raise ValueError(

deepmd/pt/model/descriptor/sezm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,8 @@ def prepare_cute_infer_so2_metadata(
18161816
Destination row pointers, source order, and source row pointers, or
18171817
``None`` when the exact CuTe contract is not satisfied.
18181818
"""
1819+
if torch.jit.is_scripting():
1820+
return None
18191821
if (
18201822
self.training
18211823
or not edge_cache.destinations_sorted

deepmd/pt/model/model/model.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
2626
"min_nbor_dist", torch.tensor(-1.0, dtype=torch.float64, device=env.DEVICE)
2727
)
2828

29+
def export_lower_input_kind(self) -> str:
30+
"""Return the lower-input ABI that preserves this model's semantics.
31+
32+
Returns
33+
-------
34+
str
35+
``"nlist"`` for the standard PyTorch model contract. Models with
36+
a graph-native deployment ABI override this method.
37+
"""
38+
return "nlist"
39+
2940
def compute_or_load_stat(
3041
self,
3142
sampled_func: Any,

deepmd/pt/model/model/spin_model.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,17 @@ def has_spin(self) -> bool:
454454
"""Returns whether it has spin input and output."""
455455
return True
456456

457+
def export_lower_input_kind(self) -> str:
458+
"""Return the dense ABI used by the virtual-atom spin model.
459+
460+
Returns
461+
-------
462+
str
463+
``"nlist"``, because virtual atoms are expanded inside the
464+
bounded neighbor-list contract.
465+
"""
466+
return "nlist"
467+
457468
@torch.jit.export
458469
def has_message_passing(self) -> bool:
459470
"""Returns whether the model has message passing."""

deepmd/pt/utils/serialization.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def serialize_from_file(model_file: str) -> dict:
5353
"pt_version": str(torch.__version__),
5454
"model": model_dict,
5555
"model_def_script": model_def_script,
56+
"lower_input_kind": model.export_lower_input_kind(),
5657
"@variables": {},
5758
}
5859
if model.get_min_nbor_dist() is not None:

deepmd/pt_expt/kernels/cute/sezm/output_grid/kernels/readout_l0.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import cutlass
1717
import cutlass.cute as cute
18+
import cutlass.pipeline as pipeline
1819
from cuda.bindings.driver import (
1920
CUstream,
2021
)
@@ -32,7 +33,6 @@
3233
TILE_K,
3334
TILE_M,
3435
TILE_N,
35-
TiledOutputGridProductBackward,
3636
)
3737

3838
# CuTe JIT functions use DSL-inferred argument and return types.
@@ -116,11 +116,16 @@ def kernel(
116116
out[node, channel] = value.to(out.element_type)
117117

118118

119-
class TiledReadoutL0GramBackward(TiledOutputGridProductBackward):
119+
class TiledReadoutL0GramBackward:
120120
"""Apply the frozen 48x48 Gram matrix to one 64-channel tile."""
121121

122122
def __init__(self) -> None:
123-
super().__init__(HIDDEN_CHANNELS)
123+
self.cta_tiler = (TILE_M, TILE_N, TILE_K)
124+
self.channel_tiles = HIDDEN_CHANNELS // TILE_N
125+
self.cta_sync_barrier = pipeline.NamedBarrier(
126+
barrier_id=1,
127+
num_threads=THREADS,
128+
)
124129

125130
@cute.jit
126131
def __call__(

0 commit comments

Comments
 (0)