Skip to content

Commit 881e208

Browse files
author
Han Wang
committed
fix(pt_expt): freeze a graph-capable model through the graph lower
dp freeze resolved the lower kind with an isinstance(NativeSpinModelKind) special case, so native-spin models were auto-upgraded to the graph lower but nothing else was. A ZBL-bridged model is not an instance of that type, yet it has no dense route either -- the analytical term's forward_atomic raises there -- so the public default lower_kind="nlist" failed deep inside the dense trace instead of freezing. Ask the graph-lower capability instead: the dense lower is deprecated in this backend, so any model that can ride the graph lower now does. That covers native spin, bridging, and the plain graph-capable descriptors in one rule, with no wrapper-type test. Regression: a bridged checkpoint frozen through the public freeze() with no lower_kind and a suffixless output must yield a graph-kind .pt2, next to the existing native-spin twin.
1 parent 749fd49 commit 881e208

2 files changed

Lines changed: 54 additions & 10 deletions

File tree

‎deepmd/pt_expt/entrypoints/main.py‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -600,19 +600,21 @@ def freeze(
600600

601601
m.eval()
602602

603-
# Native-spin models implement ONLY the NeighborGraph lower (no
604-
# dense/nlist lower exists), so resolve the lower kind HERE -- before the
605-
# export ABI is chosen and before the default output suffix below is
606-
# derived from it. Without this, the public default (lower_kind="nlist")
607-
# would reach the dense-spin trace branch and fail deep inside tracing.
608-
from deepmd.dpmodel.model.native_spin_model import (
609-
NativeSpinModelKind,
603+
# A graph-capable model ALWAYS freezes through the NeighborGraph lower:
604+
# the dense (nlist) lower is deprecated in this backend, and for some
605+
# architectures it does not exist at all (native spin has no dense spin
606+
# lower; an analytical bridging term has no dense injection site), so the
607+
# public default ``lower_kind="nlist"`` would fail deep inside the dense
608+
# trace. Resolved HERE -- before the export ABI is chosen and before the
609+
# default output suffix below is derived from it.
610+
from deepmd.pt_expt.model.graph_lower import (
611+
model_uses_graph_lower,
610612
)
611613

612-
if isinstance(m, NativeSpinModelKind) and lower_kind != "graph":
614+
if lower_kind != "graph" and model_uses_graph_lower(m):
613615
log.info(
614-
"Native-spin model implements only the NeighborGraph lower; "
615-
"resolving lower_kind=%r -> 'graph'.",
616+
"Model is graph-lower capable; resolving lower_kind=%r -> 'graph' "
617+
"(the dense lower is deprecated in the pt_expt backend).",
616618
lower_kind,
617619
)
618620
lower_kind = "graph"

‎source/tests/pt_expt/model/test_dpa4_export.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,48 @@ def test_native_spin_default_freeze_routes_to_graph(tmp_path) -> None:
520520
assert md["is_spin"] is True
521521

522522

523+
def test_bridged_default_freeze_routes_to_graph(tmp_path) -> None:
524+
"""A ZBL-bridged model also default-freezes to a graph-kind ``.pt2``.
525+
526+
The dense lower is deprecated in this backend, and for a bridged model
527+
it does not exist at all: the analytical term's ``forward_atomic``
528+
raises on the dense route, so the public default
529+
``lower_kind="nlist"`` used to fail deep inside the dense trace. The
530+
resolution was previously an ``isinstance(NativeSpinModelKind)``
531+
special case, which a bridged model is not an instance of; it now asks
532+
the graph-lower capability, which covers both.
533+
"""
534+
import copy
535+
536+
from deepmd.pt_expt.entrypoints.main import (
537+
freeze,
538+
)
539+
from deepmd.pt_expt.model.get_model import (
540+
get_model,
541+
)
542+
from deepmd.pt_expt.train.wrapper import (
543+
ModelWrapper,
544+
)
545+
546+
config = copy.deepcopy(_DPA4_CONFIG)
547+
config["bridging_method"] = "ZBL"
548+
config["bridging_r_inner"] = 0.8
549+
config["bridging_r_outer"] = 1.2
550+
model = get_model(copy.deepcopy(config)).to(torch.device("cpu")).eval()
551+
wrapper = ModelWrapper(model, model_params=copy.deepcopy(config))
552+
ckpt = tmp_path / "model.pt"
553+
torch.save({"model": wrapper.state_dict()}, ckpt)
554+
555+
output = tmp_path / "frozen_bridged" # suffixless: default CLI form
556+
freeze(model=str(ckpt), output=str(output))
557+
558+
pt2 = output.with_suffix(".pt2")
559+
assert pt2.exists(), "default suffix must follow the resolved graph kind"
560+
with zipfile.ZipFile(pt2) as z:
561+
md = json.loads(z.read("model/extra/metadata.json").decode("utf-8"))
562+
assert md["lower_input_kind"] == "graph"
563+
564+
523565
def test_virtual_spin_graph_freeze_still_rejected(tmp_path) -> None:
524566
"""spin_ener (virtual) graph freeze keeps raising ``NotImplementedError``.
525567

0 commit comments

Comments
 (0)