Skip to content

Commit acd68f8

Browse files
author
Han Wang
committed
fix(ci): keep the linear wire type shared, count tf32 warns at the source
Two CI failures on deepmodeling#5884: - `test_linear_ener.py::test_pt_expt_consistent_with_ref` (all 4 cases, CPU + CUDA): the flat linear dict emitted `"type": "linear_ener"` while pt/tf write `"linear"`, so the cross-backend serialization comparison diverged. One wire format for all backends wins: emit `"linear"` again and keep `"linear_ener"` as an accepted atomic alias. The energy-specific name stays the config/model type, and it is now also registered as a MODEL alias in dpmodel/pt_expt so a serialized composition round-trips through `BaseModel.deserialize`. - `test_get_model_dpa4.py::test_enable_tf32_warns_once[True]` (CPU + CUDA): counted records through caplog, which reads a ROOT handler, so the count moved with whatever global logging state earlier tests left behind (0 with `set_log_handles`' propagate=False, 2 in the CI shard). Count on the EMITTING logger with a dedicated handler instead -- exactly one record per `log.warning`, independent of propagation and of handlers attached elsewhere. Also register test_zbl_bridging.py in `_AOTI_COMPILE_MODULES`: it freezes a .pt2 and the drift guard flagged it, so its compile would otherwise run in the CUDA lane.
1 parent 9f2e811 commit acd68f8

7 files changed

Lines changed: 49 additions & 30 deletions

File tree

deepmd/dpmodel/atomic_model/linear_atomic_model.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
)
4040

4141

42-
@BaseAtomicModel.register("linear_ener")
43-
@BaseAtomicModel.register("linear") # legacy wire alias
42+
@BaseAtomicModel.register("linear")
43+
@BaseAtomicModel.register("linear_ener") # accepted alias, never emitted
4444
class LinearEnergyAtomicModel(BaseAtomicModel):
4545
r"""Linear model makes linear combinations of several existing models.
4646
@@ -472,9 +472,11 @@ def serialize(self) -> dict:
472472
{
473473
"@class": "Model",
474474
"@version": 3,
475-
# energy-specific wire type: future linear dipole/polar models get
476-
# their own, so the flat model dict dispatches unambiguously
477-
"type": "linear_ener",
475+
# ONE wire type across backends: pt/tf write "linear" here, so
476+
# dpmodel must too or cross-backend conversion breaks. The
477+
# unambiguous energy-specific name lives in the config/model
478+
# registry ("linear_ener"), which is also accepted here.
479+
"type": "linear",
478480
"models": [model.serialize() for model in self.models],
479481
"type_map": self.type_map,
480482
"weights": self.weights,

deepmd/dpmodel/model/dp_linear_model.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
DPLinearModel_ = make_model(LinearEnergyAtomicModel, T_Bases=(NativeOP, BaseModel))
2727

2828

29-
@BaseModel.register("linear_ener")
29+
@BaseModel.register("linear_ener") # config type
30+
@BaseModel.register("linear") # wire type emitted by the flat serialize
3031
class LinearEnergyModel(DPModelCommon, DPLinearModel_):
3132
r"""Energy model over a linear combination of atomic models.
3233

deepmd/pt_expt/model/dp_linear_model.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
DPLinearModel_ = make_model(LinearEnergyAtomicModel, T_Bases=(BaseModel,))
3434

3535

36-
@BaseModel.register("linear_ener")
36+
@BaseModel.register("linear_ener") # config type
37+
@BaseModel.register("linear") # wire type emitted by the flat serialize
3738
class LinearEnergyModel(DPModelCommon, DPLinearModel_):
3839
# The graph .pt2 exportable is energy-contract machinery (public-key
3940
# translation over the CM's forward_common_lower_graph_exportable),

source/tests/common/dpmodel/test_zbl_bridging.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ def test_zbl_serialize_roundtrip_energy_identical():
132132
model = get_model(copy.deepcopy(ZBL_CONFIG))
133133
coord, atype, box = _close_pair_inputs()
134134
data = model.serialize()
135-
assert data["type"] == "linear_ener"
135+
# the flat wire type is "linear" -- the SAME string pt/tf write, so a
136+
# composition round-trips across backends
137+
assert data["type"] == "linear"
136138
m2 = BaseModel.deserialize(data)
137139
assert type(m2) is LinearEnergyModel
138140
e1 = model.call_common(coord, atype, box=box, neighbor_graph_method="dense")[

source/tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"pt_expt/model/test_export_with_comm.py",
3737
"pt_expt/model/test_dpa1_graph_lower.py",
3838
"pt_expt/model/test_graph_export.py",
39+
"pt_expt/model/test_zbl_bridging.py",
3940
"pt_expt/model/test_graph_export_with_comm.py",
4041
"pt_expt/utils/test_graph_pt2_metadata.py",
4142
"pt_expt/infer/test_deep_eval_metadata_only.py",

source/tests/pt_expt/model/test_get_model_dpa4.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def test_default_unsupported_values_pass(self) -> None:
274274
# `enable_tf32` toggles TF32 matmul precision in pt but is ignored by pt_expt
275275
# (always "highest" precision); a truthy value must emit a warn-once message.
276276
@pytest.mark.parametrize("enable_tf32", [True, False]) # truthy warns, falsy silent
277-
def test_enable_tf32_warns_once(enable_tf32, caplog, monkeypatch) -> None:
277+
def test_enable_tf32_warns_once(enable_tf32, monkeypatch) -> None:
278278
import importlib
279279

280280
# the package __init__ rebinds the name ``get_model`` to the function, so
@@ -285,27 +285,37 @@ def test_enable_tf32_warns_once(enable_tf32, caplog, monkeypatch) -> None:
285285
# test ordering (other get_sezm_model calls may have already warned)
286286
monkeypatch.setattr(gm_mod, "_WARNED_ONCE", set())
287287

288-
# caplog captures via a ROOT-logger handler and relies on propagation,
289-
# but any earlier test that ran main() (e.g. test_dp_freeze's dispatcher
290-
# tests) leaves set_log_handles' global ``deepmd``-logger
291-
# propagate=False behind (deepmd/loggers/loggers.py), silently emptying
292-
# caplog.records. monkeypatch restores the attribute afterwards.
293-
monkeypatch.setattr(logging.getLogger("deepmd"), "propagate", True)
294-
295-
raw = _make_raw_model_config(enable_tf32=enable_tf32)
296-
297-
with caplog.at_level(logging.WARNING, logger=gm_mod.log.name):
298-
gm_mod.get_sezm_model(raw)
299-
matches = [r for r in caplog.records if "enable_tf32" in r.getMessage()]
300-
if enable_tf32:
301-
assert len(matches) == 1, caplog.text
302-
# a second call must NOT warn again (warn-once per process)
303-
caplog.clear()
304-
with caplog.at_level(logging.WARNING, logger=gm_mod.log.name):
288+
# Count emissions on the EMITTING logger with our own handler rather than
289+
# through caplog: caplog reads a root handler, so whatever global logging
290+
# state earlier tests left behind (set_log_handles flips the ``deepmd``
291+
# logger's propagate off and installs its own handlers) changes how many
292+
# records reach it -- zero when propagation is off, more than one when the
293+
# record is seen through several attached handlers. A handler on the
294+
# emitting logger sees exactly one record per ``log.warning`` call.
295+
records: list[logging.LogRecord] = []
296+
297+
class _Collect(logging.Handler):
298+
def emit(self, record: logging.LogRecord) -> None:
299+
records.append(record)
300+
301+
handler = _Collect(level=logging.WARNING)
302+
old_level = gm_mod.log.level
303+
gm_mod.log.setLevel(logging.WARNING)
304+
gm_mod.log.addHandler(handler)
305+
try:
306+
gm_mod.get_sezm_model(_make_raw_model_config(enable_tf32=enable_tf32))
307+
matches = [r for r in records if "enable_tf32" in r.getMessage()]
308+
if enable_tf32:
309+
assert len(matches) == 1, [r.getMessage() for r in records]
310+
# a second call must NOT warn again (warn-once per process)
311+
records.clear()
305312
gm_mod.get_sezm_model(_make_raw_model_config(enable_tf32=enable_tf32))
306-
assert not [r for r in caplog.records if "enable_tf32" in r.getMessage()]
307-
else:
308-
assert not matches, caplog.text
313+
assert not [r for r in records if "enable_tf32" in r.getMessage()]
314+
else:
315+
assert not matches, [r.getMessage() for r in records]
316+
finally:
317+
gm_mod.log.removeHandler(handler)
318+
gm_mod.log.setLevel(old_level)
309319

310320

311321
class TestNativeSpinErrorTranslation(unittest.TestCase):

source/tests/pt_expt/model/test_zbl_bridging.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ def test_serialize_roundtrip(self) -> None:
209209
)
210210

211211
data = self.pt_expt_model.serialize()
212-
assert data["type"] == "linear_ener"
212+
# the flat wire type is "linear" -- the SAME string pt/tf write, so a
213+
# composition round-trips across backends
214+
assert data["type"] == "linear"
213215
m2 = BaseModel.deserialize(data).to(torch.device("cpu")).eval()
214216
assert type(m2) is LinearEnergyModel
215217
out = self.pt_expt_model.forward(self.coord, self.atype, box=self.box)

0 commit comments

Comments
 (0)