Skip to content

feat(pt_expt): add dp compress support for pt_expt backend#5323

Open
wanghan-iapcm wants to merge 2 commits intodeepmodeling:masterfrom
wanghan-iapcm:feat-pt-expt-compress
Open

feat(pt_expt): add dp compress support for pt_expt backend#5323
wanghan-iapcm wants to merge 2 commits intodeepmodeling:masterfrom
wanghan-iapcm:feat-pt-expt-compress

Conversation

@wanghan-iapcm
Copy link
Collaborator

@wanghan-iapcm wanghan-iapcm commented Mar 17, 2026

Summary

  • Add model compression (embedding net tabulation) for the pt_expt backend, matching the existing pt backend capability
  • Compressed models replace embedding net forward passes with polynomial lookup tables via C++ custom ops (tabulate_fusion_se_*), significantly speeding up inference
  • Support all compressible descriptors: se_e2_a, se_r, se_t, se_t_tebd, dpa1, se_atten_v2, dpa2 (hybrid delegates automatically)

Key changes

Infrastructure:

  • deepmd/pt_expt/utils/tabulate_ops.py — Register torch.library.register_fake for all 5 custom ops to enable torch.export/make_fx tracing through compressed forward paths
  • deepmd/pt_expt/utils/tabulate.pyDPTabulate subclass that detects descriptor type via serialized data (avoids isinstance checks against pt-specific classes)
  • deepmd/pt_expt/entrypoints/compress.py — Entry point: load .pte → deserialize → enable_compression() → re-export .pte

Descriptors: Each gets enable_compression() + @cast_precision call() override with compressed branch using the appropriate custom op.

dpmodel: Initialize self.compress = False in all descriptor __init__ methods.

Summary by CodeRabbit

  • New Features

    • Added model compression support for multiple descriptor types with an optional tabulated compressed inference path.
    • New CLI command to enable compression on trained models.
    • Persistent min_nbor_dist storage on models to support compression workflows.
  • Utilities

    • New tabulation helpers and fake-op registrations to enable tracing and compressed execution.
  • Tests

    • Added comprehensive tests validating compressed vs. uncompressed forward results and end-to-end compression pipelines.

Implement model compression (embedding net tabulation) for the pt_expt
backend, replacing embedding net forward passes with polynomial lookup
tables via C++ custom ops (tabulate_fusion_se_*).

- Register fake tensor implementations for custom ops to enable
  torch.export/make_fx tracing through compressed forward paths
- Create pt_expt DPTabulate subclass using serialized type detection
  instead of isinstance checks against pt-specific classes
- Add enable_compression() and compressed call() to all descriptors:
  se_e2_a, se_r, se_t, se_t_tebd, dpa1, se_atten_v2, dpa2
  (hybrid delegates to sub-descriptors automatically)
- Add compress entry point and command dispatch in main.py
- Add test_compressed_forward tests for all descriptor types
- Initialize self.compress = False in dpmodel descriptor __init__
@wanghan-iapcm wanghan-iapcm requested a review from njzjz March 17, 2026 13:39
@dosubot dosubot bot added the new feature label Mar 17, 2026
@wanghan-iapcm wanghan-iapcm marked this pull request as draft March 17, 2026 13:42
)


class DPTabulate(DPTabulatePT):

Check failure

Code scanning / CodeQL

Missing call to superclass `__init__` during object initialization Error

This class does not call
DPTabulate.__init__
during initialization. (
DPTabulate.__init__
may be missing a call to a base class __init__)
Comment on lines +154 to +161
def call(
self,
coord_ext: torch.Tensor,
atype_ext: torch.Tensor,
nlist: torch.Tensor,
mapping: torch.Tensor | None = None,
fparam: torch.Tensor | None = None,
) -> Any:

Check warning

Code scanning / CodeQL

Signature mismatch in overriding method Warning

This method requires at least 4 positional arguments, whereas overridden
NativeOP.call
may be called with 1.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method does not accept arbitrary keyword arguments, which overridden
NativeOP.call
does.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method requires at most 6 positional arguments, whereas overridden
NativeOP.call
may be called with arbitrarily many.
This call
correctly calls the base method, but does not match the signature of the overriding method.
Comment on lines +188 to +195
def call(
self,
coord_ext: torch.Tensor,
atype_ext: torch.Tensor,
nlist: torch.Tensor,
mapping: torch.Tensor | None = None,
fparam: torch.Tensor | None = None,
) -> Any:

Check warning

Code scanning / CodeQL

Signature mismatch in overriding method Warning

This method requires at least 4 positional arguments, whereas overridden
NativeOP.call
may be called with 1.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method does not accept arbitrary keyword arguments, which overridden
NativeOP.call
does.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method requires at most 6 positional arguments, whereas overridden
NativeOP.call
may be called with arbitrarily many.
This call
correctly calls the base method, but does not match the signature of the overriding method.
Comment on lines +111 to +118
def call(
self,
coord_ext: torch.Tensor,
atype_ext: torch.Tensor,
nlist: torch.Tensor,
mapping: torch.Tensor | None = None,
fparam: torch.Tensor | None = None,
) -> Any:

Check warning

Code scanning / CodeQL

Signature mismatch in overriding method Warning

This method requires at least 4 positional arguments, whereas overridden
NativeOP.call
may be called with 1.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method does not accept arbitrary keyword arguments, which overridden
NativeOP.call
does.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method requires at most 6 positional arguments, whereas overridden
NativeOP.call
may be called with arbitrarily many.
This call
correctly calls the base method, but does not match the signature of the overriding method.
Comment on lines +100 to +107
def call(
self,
coord_ext: torch.Tensor,
atype_ext: torch.Tensor,
nlist: torch.Tensor,
mapping: torch.Tensor | None = None,
fparam: torch.Tensor | None = None,
) -> Any:

Check warning

Code scanning / CodeQL

Signature mismatch in overriding method Warning

This method requires at least 4 positional arguments, whereas overridden
NativeOP.call
may be called with 1.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method does not accept arbitrary keyword arguments, which overridden
NativeOP.call
does.
This call
correctly calls the base method, but does not match the signature of the overriding method.
This method requires at most 6 positional arguments, whereas overridden
NativeOP.call
may be called with arbitrarily many.
This call
correctly calls the base method, but does not match the signature of the overriding method.
# type_embedding.call() returns (ntypes+1) x tebd_dim (with padding)
full_embd = self.type_embedding.call()
nt, t_dim = full_embd.shape
ng = self.se_atten.neuron[-1]

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable ng is not used.
# type_embedding.call() returns (ntypes+1) x tebd_dim (with padding)
full_embd = self.type_embedding.call()
nt, t_dim = full_embd.shape
ng = self.repinit.neuron[-1]

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable ng is not used.
"""Register a fake implementation, silently skipping if already registered."""
try:
torch.library.register_fake(op_name)(fn)
except RuntimeError:

Check notice

Code scanning / CodeQL

Empty except Note

'except' clause does nothing but pass and there is no explanatory comment.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: adae1e005e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +86 to +89
if isinstance(min_nbor_dist, (int, float)):
min_nbor_dist_val = float(min_nbor_dist)
else:
min_nbor_dist_val = float(min_nbor_dist.item())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist computed min_nbor_dist before enabling compression

When min_nbor_dist is missing, this block computes min_nbor_dist_val from the training data but never assigns it back to the model. model.enable_compression(...) then reads self.get_min_nbor_dist() (via the dpmodel base implementation) and still receives None, so dp --pt_expt compress -t ... can fail for models that rely on --training-script to provide neighbor statistics.

Useful? React with 👍 / 👎.

Comment on lines +102 to +103
new_model_dict = model.serialize()
data = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep compression state when writing the output .pte

Serializing the in-memory model right after enable_compression() drops the tabulation state, because descriptor serialize() paths in dpmodel (for example DescrptSeA.serialize) do not include runtime fields like compress, compress_data, or compress_info. Since deserialize_to_file rebuilds from that dict, the exported file reverts to the uncompressed descriptor path even though compression was enabled just before export.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

Adds a compression feature: dpmodel descriptors get a compress flag; pt_expt descriptors implement enable_compression, tabulation-based compressed forward paths, fake-op registrations for tracing, a CLI entrypoint, model min_nbor_dist persistence, and comprehensive tests for compressed vs uncompressed parity.

Changes

Cohort / File(s) Summary
dpmodel descriptor compress attribute
deepmd/dpmodel/descriptor/dpa1.py, deepmd/dpmodel/descriptor/dpa2.py, deepmd/dpmodel/descriptor/se_atten_v2.py, deepmd/dpmodel/descriptor/se_e2_a.py, deepmd/dpmodel/descriptor/se_r.py, deepmd/dpmodel/descriptor/se_t.py, deepmd/dpmodel/descriptor/se_t_tebd.py
Initialize self.compress = False in descriptor constructors; no behavioral changes.
pt_expt DPA1 compression
deepmd/pt_expt/descriptor/dpa1.py
Adds enable_compression, _store_compress_data, _store_type_embd_data, and _call_compressed implementing tabulated compression, type-embedding caching, and compressed forward path.
pt_expt DPA2 compression
deepmd/pt_expt/descriptor/dpa2.py
Adds enable_compression, _store_compress_data, _store_type_embd_data, _call_compressed, and _compressed_repinit_forward to support table-based compressed repinit and repformer flows.
pt_expt SeAttenV2 delegation
deepmd/pt_expt/descriptor/se_atten_v2.py
Delegates compression-related methods and compressed call to DPA1 implementations (wrapper methods).
pt_expt SeA compression
deepmd/pt_expt/descriptor/se_e2_a.py
Adds enable_compression, _store_compress_data, and _call_compressed using tabulate fusion op and per-type embedding handling.
pt_expt SeR compression
deepmd/pt_expt/descriptor/se_r.py
Adds enable_compression, _store_compress_data, extended call branching, and _call_compressed using tabulated radial/environment fusion.
pt_expt SeT compression
deepmd/pt_expt/descriptor/se_t.py
Adds enable_compression, _store_compress_data, call routing, and _call_compressed with stride scaling and tabulated forward logic.
pt_expt SeT_TEBD compression
deepmd/pt_expt/descriptor/se_t_tebd.py
Adds enable_compression, _store_compress_data, _store_type_embd_data, and _call_compressed supporting geometric and type-embedding compressed inference.
pt_expt tabulation utilities
deepmd/pt_expt/utils/tabulate.py, deepmd/pt_expt/utils/tabulate_ops.py
New DPTabulate for serialized-descriptor tabulation (no isinstance checks) and tabulate_ops registering fake tensor ops for tracing (tabulate_fusion_* fakes).
pt_expt compression entrypoint & CLI
deepmd/pt_expt/entrypoints/compress.py, deepmd/pt_expt/entrypoints/main.py
New enable_compression entrypoint (computes/extracts min_nbor_dist, invokes descriptor compression, re-serializes); CLI "compress" command added.
pt_expt module init & registry
deepmd/pt_expt/utils/__init__.py, deepmd/pt_expt/common.py
Auto-registers fake tabulate ops on import; auto-populates dpmodel→pt_expt converter registry when wrapping TorchModule subclasses.
model support for min_nbor_dist
deepmd/pt_expt/model/make_model.py
Adds min_nbor_dist property/getter/setter and persistent buffer backing to CM model class for round-trip storage.
tests: descriptor & model compression
source/tests/pt_expt/descriptor/*, source/tests/pt_expt/model/test_model_compression.py
Adds compressed-forward parity tests across descriptors and end-to-end model compression tests including freeze/compress/eval and min_nbor_dist round-trip.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as CLI (main.py)
    participant Entrypoint as Compress Entrypoint
    participant Model as Deserialized Model
    participant Descriptor as Descriptor Module
    participant Tabulate as DPTabulate
    participant Buffers as compress_data/compress_info

    User->>CLI: deepmd compress input.pte output.pte
    CLI->>Entrypoint: enable_compression(input, output, ...)
    Entrypoint->>Model: load & deserialize .pte
    Entrypoint->>Model: set/get min_nbor_dist
    Entrypoint->>Descriptor: call enable_compression(min_nbor_dist,...)
    Descriptor->>Tabulate: build DPTabulate table
    Tabulate->>Descriptor: return table buffers
    Descriptor->>Buffers: _store_compress_data() (save tables & info)
    Descriptor->>Buffers: _store_type_embd_data() (optional)
    Descriptor->>Descriptor: set self.compress = True
    Entrypoint->>Model: re-serialize compressed model
    Entrypoint->>User: save output.pte
Loading
sequenceDiagram
    participant Input as Input Tensors
    participant Descriptor as Descriptor.call
    participant Check as compress flag
    participant Compressed as _call_compressed
    participant Op as Custom tabulate_fusion_* op
    participant Buffers as compress_data/compress_info
    participant Output as Output

    Input->>Descriptor: coord_ext, atype_ext, nlist
    Descriptor->>Check: if self.compress?
    alt compressed
        Check->>Compressed: run _call_compressed
        Compressed->>Buffers: load compress_data/compress_info
        Compressed->>Op: invoke tabulate_fusion_* with buffers
        Op->>Compressed: return accumulated env/type outputs
        Compressed->>Output: assemble final tensor (+ aux)
    else uncompressed
        Check->>Descriptor: run original call path
        Descriptor->>Output: return original outputs
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

OP, Core, Examples

Suggested reviewers

  • njzjz
  • iProzd
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(pt_expt): add dp compress support for pt_expt backend' accurately describes the main feature added: compression support for the pt_expt backend, matching the pr_objectives summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (14)
deepmd/dpmodel/descriptor/se_atten_v2.py (1)

199-199: Redundant initialization, but harmless.

DescrptSeAttenV2 explicitly calls DescrptDPA1.__init__() which already sets self.compress = False (line 347 in dpa1.py). Re-setting it here is redundant but ensures the child class explicitly controls its compression state for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/dpmodel/descriptor/se_atten_v2.py` at line 199, Remove the redundant
assignment to self.compress in DescrptSeAttenV2.__init__; DescrptDPA1.__init__
already initializes self.compress = False, so delete the line "self.compress =
False" in the DescrptSeAttenV2 constructor to avoid duplication while preserving
behavior.
source/tests/pt_expt/descriptor/test_se_e2_a.py (1)

135-163: Also trace/export the compressed path.

This only validates eager execution after enable_compression(). The new fake-op registrations are there specifically to make torch.export/make_fx work on compressed models, so please add one compressed export/tracing assertion here or in a shared helper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/tests/pt_expt/descriptor/test_se_e2_a.py` around lines 135 - 163, The
test_compressed_forward currently only checks eager outputs after
dd0.enable_compression(0.5); add a compressed export/tracing check by exporting
or tracing the compressed DescrptSeA instance (e.g., using torch.export or
torch.fx.make_fx on dd0 after enable_compression) and run the traced/exported
graph on the same coord_ext/atype_ext/nlist inputs, then assert the
traced/export outputs match result_ref/result_cmp (use
torch.testing.assert_close with the same atol/rtol); locate the test function
test_compressed_forward and the DescrptSeA instance/enable_compression call to
insert this additional export/trace + assertion.
source/tests/pt_expt/descriptor/test_dpa1.py (1)

151-160: Keep an attention-enabled case in this compression test.

attn_layer=0 only exercises the no-attention configuration, while the rest of this file uses attn_layer=2. Reusing a nonzero layer count here would make the new compression coverage much closer to the DPA1 path this feature is targeting. If compression is intentionally limited to attn_layer=0, please assert that explicitly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/tests/pt_expt/descriptor/test_dpa1.py` around lines 151 - 160, The
test instantiates DescrptDPA1 with attn_layer=0 which only covers the
no-attention path; update the instantiation to use a nonzero attention layer
(e.g., attn_layer=2) to exercise the attention-enabled compression path
consistent with other tests, or if compression is intentionally limited to
no-attention, add an explicit assertion or comment near the DescrptDPA1 creation
(referencing DescrptDPA1 and attn_layer) that verifies/declares attn_layer==0
and documents the intentional limitation.
deepmd/pt_expt/entrypoints/main.py (1)

198-210: Add a smoke test for the new compress dispatch.

This branch depends on parser-provided fields (step, frequency, training_script) that are easy to mis-wire and aren't exercised anywhere in this diff. A tiny main() dispatch test would catch CLI regressions early.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/entrypoints/main.py` around lines 198 - 210, Add a small smoke
test that exercises the "compress" dispatch in main: call the main entrypoint
(or the function that reads FLAGS/FLAGS.command) with FLAGS.command set to
"compress" and set the parser-provided fields FLAGS.step, FLAGS.frequency,
FLAGS.training_script (and FLAGS.INPUT/FLAGS.output) to safe dummy values, then
assert that enable_compression (imported in the snippet) is invoked (by
patching/mocking it) or that main returns/executes without error; this ensures
the dispatch that calls enable_compression(input_file=..., stride=...,
extrapolate=..., check_frequency=..., training_script=...) is wired correctly
and prevents CLI regressions.
deepmd/pt_expt/descriptor/se_e2_a.py (3)

172-192: Add strict=True to zip for safety.

Same recommendation for the non-type_one_side branch.

♻️ Suggested fix
         else:
             atype_loc = atype_ext[:, :nloc].reshape(nfnl)
             for embedding_idx, (compress_data_ii, compress_info_ii) in enumerate(
-                zip(self.compress_data, self.compress_info)
+                zip(self.compress_data, self.compress_info, strict=True)
             ):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_e2_a.py` around lines 172 - 192, The zip over
self.compress_data and self.compress_info used in the loop
(zip(self.compress_data, self.compress_info) in the block that iterates
embedding_idx and calls torch.ops.deepmd.tabulate_fusion_se_a) should be made
safe by adding strict=True so mismatched lengths raise immediately; update that
zip call and the analogous zip call in the non-type_one_side branch to
zip(self.compress_data, self.compress_info, strict=True) to ensure length
mismatches fail fast and prevent silent truncation.

133-139: Prefix unused variable with underscore.

The diff variable is unpacked but never used.

♻️ Suggested fix
-        rr, diff, ww = self.env_mat.call(
+        rr, _diff, ww = self.env_mat.call(
             coord_ext,
             atype_ext,
             nlist,
             self.davg[...],
             self.dstd[...],
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_e2_a.py` around lines 133 - 139, The unpacked
return from self.env_mat.call assigns rr, diff, ww but diff is unused; update
the tuple unpacking at the env_mat.call invocation to prefix the unused value
(e.g., rr, _diff, ww or rr, _, ww) so the unused variable is clearly marked, and
verify no other references to diff remain in the surrounding code (look for rr,
diff, ww in the call site and any subsequent uses).

154-171: Add strict=True to zip for safety.

Since compress_data and compress_info are created together with matching lengths, adding strict=True serves as a runtime assertion that catches potential bugs if the invariant is ever broken.

♻️ Suggested fix
         if self.type_one_side:
             for embedding_idx, (compress_data_ii, compress_info_ii) in enumerate(
-                zip(self.compress_data, self.compress_info)
+                zip(self.compress_data, self.compress_info, strict=True)
             ):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_e2_a.py` around lines 154 - 171, In the loop
gated by self.type_one_side that currently does "for embedding_idx,
(compress_data_ii, compress_info_ii) in enumerate(zip(self.compress_data,
self.compress_info)):", change the zip call to use strict=True so the runtime
asserts the two sequences have identical lengths (i.e., zip(self.compress_data,
self.compress_info, strict=True)); update the loop header where compress_data
and compress_info are enumerated (referenced by embedding_idx, compress_data_ii,
compress_info_ii) so any mismatch will raise immediately while leaving the rest
of the logic (including calls to torch.ops.deepmd.tabulate_fusion_se_a and
accumulation into xyz_scatter) unchanged.
deepmd/pt_expt/descriptor/dpa1.py (2)

176-182: Prefix unused variable with underscore.

The diff variable is unpacked but never used.

♻️ Suggested fix
-        rr, diff, sw = self.se_atten.env_mat.call(
+        rr, _diff, sw = self.se_atten.env_mat.call(
             coord_ext,
             atype_ext,
             nlist,
             self.se_atten.mean[...],
             self.se_atten.stddev[...],
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/dpa1.py` around lines 176 - 182, The unpacked
variable diff returned by self.se_atten.env_mat.call(...) is unused; change the
unpacking to prefix that element with an underscore (e.g., _diff or _) so it's
clear it is intentionally ignored — update the tuple assignment rr, diff, sw =
self.se_atten.env_mat.call(...) to rr, _diff, sw =
self.se_atten.env_mat.call(...) (references: rr, diff/_diff, sw,
self.se_atten.env_mat.call, coord_ext, atype_ext, nlist, self.se_atten.mean,
self.se_atten.stddev).

63-65: Consider using explicit exception instead of assert for validation.

Using assert for input validation is problematic because assertions can be disabled with Python's -O flag. For production code validation that should always run, prefer explicit if/raise.

♻️ Suggested fix
-        assert not self.se_atten.resnet_dt, (
-            "Model compression error: descriptor resnet_dt must be false!"
-        )
+        if self.se_atten.resnet_dt:
+            raise ValueError("Model compression error: descriptor resnet_dt must be false!")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/dpa1.py` around lines 63 - 65, Replace the
runtime-only assert with an explicit validation that always runs: check the
boolean flag self.se_atten.resnet_dt in the constructor or init path in dpa1.py
and if it is True raise a clear exception (e.g., ValueError or RuntimeError)
with the same message "Model compression error: descriptor resnet_dt must be
false!" so the validation is enforced even when Python optimizations are
enabled.
deepmd/pt_expt/descriptor/se_r.py (2)

122-129: Prefix unused variable with underscore.

The diff variable is unpacked but never used.

♻️ Suggested fix
-        rr, diff, ww = self.env_mat.call(
+        rr, _diff, ww = self.env_mat.call(
             coord_ext,
             atype_ext,
             nlist,
             self.davg[...],
             self.dstd[...],
             True,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_r.py` around lines 122 - 129, The unpacked
variable named diff from the call to self.env_mat.call (assigned as rr, diff,
ww) is unused; change the unpacking to underscore-prefixed name(s) (e.g., rr,
_diff, ww or rr, _, ww) so the unused value is clearly marked. Update the
assignment site where self.env_mat.call is invoked (in the function/method
containing rr, diff, ww and using coord_ext, atype_ext, nlist, self.davg,
self.dstd) to use the underscore-prefixed variable and leave the rest of the
logic unchanged.

139-153: Add strict=True to zip for safety.

♻️ Suggested fix
         for ii, (compress_data_ii, compress_info_ii) in enumerate(
-            zip(self.compress_data, self.compress_info)
+            zip(self.compress_data, self.compress_info, strict=True)
         ):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_r.py` around lines 139 - 153, The loop zips
self.compress_data and self.compress_info without strict checking; change the
zip call in the for loop inside the method (the for ii, (compress_data_ii,
compress_info_ii) in enumerate(zip(self.compress_data, self.compress_info))) to
use zip(..., strict=True) so mismatched lengths raise immediately and avoid
silent misalignment when producing mm/ss/xyz_scatter in that block (refer to
variables compress_data_ii, compress_info_ii, sec, ss, and the
torch.ops.deepmd.tabulate_fusion_se_r call to locate the code).
deepmd/pt_expt/utils/tabulate.py (1)

40-47: Avoid mutable default argument and function call in defaults.

Using a mutable list [] as a default argument and calling ActivationFn("tanh") in the signature can cause subtle bugs. The mutable default is shared across all calls, and the function call default is evaluated once at definition time.

♻️ Suggested fix
     def __init__(
         self,
         descrpt: Any,
         neuron: list[int],
         type_one_side: bool = False,
-        exclude_types: list[list[int]] = [],
-        activation_fn: ActivationFn = ActivationFn("tanh"),
+        exclude_types: list[list[int]] | None = None,
+        activation_fn: ActivationFn | None = None,
     ) -> None:
+        if exclude_types is None:
+            exclude_types = []
+        if activation_fn is None:
+            activation_fn = ActivationFn("tanh")
         # Call BaseTabulate.__init__ directly (skip DPTabulatePT.__init__)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/utils/tabulate.py` around lines 40 - 47, The __init__
signature for the class uses a mutable default for exclude_types and calls
ActivationFn("tanh") at definition time; change the parameters to use
exclude_types: Optional[list[list[int]]] = None and activation_fn:
Optional[ActivationFn] = None, then inside __init__ set self.exclude_types = []
if exclude_types is None else exclude_types (or a shallow copy) and set
self.activation_fn = activation_fn if activation_fn is not None else
ActivationFn("tanh"); update any references to use these instance attributes
(referencing the __init__ method, parameter names exclude_types and
activation_fn, and ActivationFn) to avoid shared mutable state and premature
evaluation.
deepmd/pt_expt/descriptor/se_t_tebd.py (1)

159-165: Prefix unused variable with underscore.

The diff variable is unpacked but never used. Prefix it with an underscore to indicate it's intentionally ignored and satisfy the linter.

♻️ Suggested fix
-        rr, diff, sw = self.se_ttebd.env_mat.call(
+        rr, _diff, sw = self.se_ttebd.env_mat.call(
             coord_ext,
             atype_ext,
             nlist,
             self.se_ttebd.mean[...],
             self.se_ttebd.stddev[...],
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_t_tebd.py` around lines 159 - 165, The unpacking
from self.se_ttebd.env_mat.call currently assigns rr, diff, sw but diff is never
used; update the unpacking in the caller of self.se_ttebd.env_mat.call (where
rr, diff, sw are assigned) to prefix the unused variable with an underscore
(e.g., rr, _diff, sw or rr, _, sw) so the intent to ignore it is clear to
linters and readers; leave rr and sw as-is and ensure any references to the old
name aren’t used elsewhere.
deepmd/pt_expt/utils/tabulate_ops.py (1)

26-29: Consider adding a comment explaining the side-effect access.

The static analysis tool flags this as a useless expression (B018), but it intentionally triggers lazy loading of the custom op library. Adding a brief comment would clarify intent and silence the warning.

💡 Suggested improvement
 # Ensure the custom op library is loaded (if available)
 try:
-    torch.ops.deepmd
+    torch.ops.deepmd  # Access triggers lazy library loading
 except AttributeError:
     pass
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/utils/tabulate_ops.py` around lines 26 - 29, The try/except
that accesses torch.ops.deepmd is intentionally used to trigger lazy loading of
the custom op library, so add a short comment immediately above that block
clarifying this side-effect (e.g., "Access torch.ops.deepmd to force lazy-load
the custom op library; keep except to avoid import error") and optionally append
a linter suppression (e.g., # noqa: B018) to the line to silence the
static-analysis warning while leaving the existing try/except (torch.ops.deepmd
and the AttributeError handler) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deepmd/pt_expt/descriptor/dpa2.py`:
- Line 215: Unpackings in dpa2.py are creating unused variables causing RUF059;
change the unpack of nlist.shape in the scope where nframes, nloc, nnei =
nlist.shape to use a prefixed unused name for the third value (e.g., nframes,
nloc, _nnei = nlist.shape) and likewise rename the unused variable diff at the
other occurrence to _diff so intent is explicit; update both occurrences
referenced in the file and then run ruff check . and ruff format . before
committing.

In `@deepmd/pt_expt/descriptor/se_t.py`:
- Line 133: Rename the unused unpacked variable diff from the call rr, diff, ww
= self.env_mat.call(...) to rr, _diff, ww = self.env_mat.call(...) in the
compressed forward path to satisfy RUF059, and update the subsequent loop that
currently uses zip(param_list_a, param_list_b) to zip(param_list_a,
param_list_b, strict=True) (use the exact parameter list variable names present
around line 155) to enforce equal-length iteration and satisfy B905; make these
two edits inside the same compressed forward function/method where
self.env_mat.call and the zip loop occur (preserving all other logic).

In `@deepmd/pt_expt/entrypoints/compress.py`:
- Around line 91-98: The call to model.enable_compression currently passes
arguments in the wrong order so extrapolate is being used as min_nbor_dist;
replace the call to model.enable_compression(...) so that min_nbor_dist_val is
supplied as the first positional argument (per the enable_compression signature
in descriptor/se_t.py), followed by extrapolate, stride, stride * 10, and
check_frequency; update the invocation at the model.enable_compression(...) site
to pass min_nbor_dist_val first to ensure correct table bounds.

In `@source/tests/pt_expt/descriptor/test_dpa2.py`:
- Around line 243-244: The tuple unpacking of self.nlist.shape assigns unused
variables nf and nloc causing a RUF059 lint error; change the unpack to use
placeholders (e.g., "_, _, nnei = self.nlist.shape" or "_, _, nnei = ..." ) so
only nnei is kept and rng = np.random.default_rng(GLOBAL_SEED) remains
unchanged—update the line that currently reads "nf, nloc, nnei =
self.nlist.shape" to use underscores for the unused members.

---

Nitpick comments:
In `@deepmd/dpmodel/descriptor/se_atten_v2.py`:
- Line 199: Remove the redundant assignment to self.compress in
DescrptSeAttenV2.__init__; DescrptDPA1.__init__ already initializes
self.compress = False, so delete the line "self.compress = False" in the
DescrptSeAttenV2 constructor to avoid duplication while preserving behavior.

In `@deepmd/pt_expt/descriptor/dpa1.py`:
- Around line 176-182: The unpacked variable diff returned by
self.se_atten.env_mat.call(...) is unused; change the unpacking to prefix that
element with an underscore (e.g., _diff or _) so it's clear it is intentionally
ignored — update the tuple assignment rr, diff, sw =
self.se_atten.env_mat.call(...) to rr, _diff, sw =
self.se_atten.env_mat.call(...) (references: rr, diff/_diff, sw,
self.se_atten.env_mat.call, coord_ext, atype_ext, nlist, self.se_atten.mean,
self.se_atten.stddev).
- Around line 63-65: Replace the runtime-only assert with an explicit validation
that always runs: check the boolean flag self.se_atten.resnet_dt in the
constructor or init path in dpa1.py and if it is True raise a clear exception
(e.g., ValueError or RuntimeError) with the same message "Model compression
error: descriptor resnet_dt must be false!" so the validation is enforced even
when Python optimizations are enabled.

In `@deepmd/pt_expt/descriptor/se_e2_a.py`:
- Around line 172-192: The zip over self.compress_data and self.compress_info
used in the loop (zip(self.compress_data, self.compress_info) in the block that
iterates embedding_idx and calls torch.ops.deepmd.tabulate_fusion_se_a) should
be made safe by adding strict=True so mismatched lengths raise immediately;
update that zip call and the analogous zip call in the non-type_one_side branch
to zip(self.compress_data, self.compress_info, strict=True) to ensure length
mismatches fail fast and prevent silent truncation.
- Around line 133-139: The unpacked return from self.env_mat.call assigns rr,
diff, ww but diff is unused; update the tuple unpacking at the env_mat.call
invocation to prefix the unused value (e.g., rr, _diff, ww or rr, _, ww) so the
unused variable is clearly marked, and verify no other references to diff remain
in the surrounding code (look for rr, diff, ww in the call site and any
subsequent uses).
- Around line 154-171: In the loop gated by self.type_one_side that currently
does "for embedding_idx, (compress_data_ii, compress_info_ii) in
enumerate(zip(self.compress_data, self.compress_info)):", change the zip call to
use strict=True so the runtime asserts the two sequences have identical lengths
(i.e., zip(self.compress_data, self.compress_info, strict=True)); update the
loop header where compress_data and compress_info are enumerated (referenced by
embedding_idx, compress_data_ii, compress_info_ii) so any mismatch will raise
immediately while leaving the rest of the logic (including calls to
torch.ops.deepmd.tabulate_fusion_se_a and accumulation into xyz_scatter)
unchanged.

In `@deepmd/pt_expt/descriptor/se_r.py`:
- Around line 122-129: The unpacked variable named diff from the call to
self.env_mat.call (assigned as rr, diff, ww) is unused; change the unpacking to
underscore-prefixed name(s) (e.g., rr, _diff, ww or rr, _, ww) so the unused
value is clearly marked. Update the assignment site where self.env_mat.call is
invoked (in the function/method containing rr, diff, ww and using coord_ext,
atype_ext, nlist, self.davg, self.dstd) to use the underscore-prefixed variable
and leave the rest of the logic unchanged.
- Around line 139-153: The loop zips self.compress_data and self.compress_info
without strict checking; change the zip call in the for loop inside the method
(the for ii, (compress_data_ii, compress_info_ii) in
enumerate(zip(self.compress_data, self.compress_info))) to use zip(...,
strict=True) so mismatched lengths raise immediately and avoid silent
misalignment when producing mm/ss/xyz_scatter in that block (refer to variables
compress_data_ii, compress_info_ii, sec, ss, and the
torch.ops.deepmd.tabulate_fusion_se_r call to locate the code).

In `@deepmd/pt_expt/descriptor/se_t_tebd.py`:
- Around line 159-165: The unpacking from self.se_ttebd.env_mat.call currently
assigns rr, diff, sw but diff is never used; update the unpacking in the caller
of self.se_ttebd.env_mat.call (where rr, diff, sw are assigned) to prefix the
unused variable with an underscore (e.g., rr, _diff, sw or rr, _, sw) so the
intent to ignore it is clear to linters and readers; leave rr and sw as-is and
ensure any references to the old name aren’t used elsewhere.

In `@deepmd/pt_expt/entrypoints/main.py`:
- Around line 198-210: Add a small smoke test that exercises the "compress"
dispatch in main: call the main entrypoint (or the function that reads
FLAGS/FLAGS.command) with FLAGS.command set to "compress" and set the
parser-provided fields FLAGS.step, FLAGS.frequency, FLAGS.training_script (and
FLAGS.INPUT/FLAGS.output) to safe dummy values, then assert that
enable_compression (imported in the snippet) is invoked (by patching/mocking it)
or that main returns/executes without error; this ensures the dispatch that
calls enable_compression(input_file=..., stride=..., extrapolate=...,
check_frequency=..., training_script=...) is wired correctly and prevents CLI
regressions.

In `@deepmd/pt_expt/utils/tabulate_ops.py`:
- Around line 26-29: The try/except that accesses torch.ops.deepmd is
intentionally used to trigger lazy loading of the custom op library, so add a
short comment immediately above that block clarifying this side-effect (e.g.,
"Access torch.ops.deepmd to force lazy-load the custom op library; keep except
to avoid import error") and optionally append a linter suppression (e.g., #
noqa: B018) to the line to silence the static-analysis warning while leaving the
existing try/except (torch.ops.deepmd and the AttributeError handler) unchanged.

In `@deepmd/pt_expt/utils/tabulate.py`:
- Around line 40-47: The __init__ signature for the class uses a mutable default
for exclude_types and calls ActivationFn("tanh") at definition time; change the
parameters to use exclude_types: Optional[list[list[int]]] = None and
activation_fn: Optional[ActivationFn] = None, then inside __init__ set
self.exclude_types = [] if exclude_types is None else exclude_types (or a
shallow copy) and set self.activation_fn = activation_fn if activation_fn is not
None else ActivationFn("tanh"); update any references to use these instance
attributes (referencing the __init__ method, parameter names exclude_types and
activation_fn, and ActivationFn) to avoid shared mutable state and premature
evaluation.

In `@source/tests/pt_expt/descriptor/test_dpa1.py`:
- Around line 151-160: The test instantiates DescrptDPA1 with attn_layer=0 which
only covers the no-attention path; update the instantiation to use a nonzero
attention layer (e.g., attn_layer=2) to exercise the attention-enabled
compression path consistent with other tests, or if compression is intentionally
limited to no-attention, add an explicit assertion or comment near the
DescrptDPA1 creation (referencing DescrptDPA1 and attn_layer) that
verifies/declares attn_layer==0 and documents the intentional limitation.

In `@source/tests/pt_expt/descriptor/test_se_e2_a.py`:
- Around line 135-163: The test_compressed_forward currently only checks eager
outputs after dd0.enable_compression(0.5); add a compressed export/tracing check
by exporting or tracing the compressed DescrptSeA instance (e.g., using
torch.export or torch.fx.make_fx on dd0 after enable_compression) and run the
traced/exported graph on the same coord_ext/atype_ext/nlist inputs, then assert
the traced/export outputs match result_ref/result_cmp (use
torch.testing.assert_close with the same atol/rtol); locate the test function
test_compressed_forward and the DescrptSeA instance/enable_compression call to
insert this additional export/trace + assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 16ab0304-492e-4fb6-af81-6e1a61b3394a

📥 Commits

Reviewing files that changed from the base of the PR and between 4ce5c73 and adae1e0.

📒 Files selected for processing (26)
  • deepmd/dpmodel/descriptor/dpa1.py
  • deepmd/dpmodel/descriptor/dpa2.py
  • deepmd/dpmodel/descriptor/se_atten_v2.py
  • deepmd/dpmodel/descriptor/se_e2_a.py
  • deepmd/dpmodel/descriptor/se_r.py
  • deepmd/dpmodel/descriptor/se_t.py
  • deepmd/dpmodel/descriptor/se_t_tebd.py
  • deepmd/pt_expt/descriptor/dpa1.py
  • deepmd/pt_expt/descriptor/dpa2.py
  • deepmd/pt_expt/descriptor/se_atten_v2.py
  • deepmd/pt_expt/descriptor/se_e2_a.py
  • deepmd/pt_expt/descriptor/se_r.py
  • deepmd/pt_expt/descriptor/se_t.py
  • deepmd/pt_expt/descriptor/se_t_tebd.py
  • deepmd/pt_expt/entrypoints/compress.py
  • deepmd/pt_expt/entrypoints/main.py
  • deepmd/pt_expt/utils/__init__.py
  • deepmd/pt_expt/utils/tabulate.py
  • deepmd/pt_expt/utils/tabulate_ops.py
  • source/tests/pt_expt/descriptor/test_dpa1.py
  • source/tests/pt_expt/descriptor/test_dpa2.py
  • source/tests/pt_expt/descriptor/test_se_atten_v2.py
  • source/tests/pt_expt/descriptor/test_se_e2_a.py
  • source/tests/pt_expt/descriptor/test_se_r.py
  • source/tests/pt_expt/descriptor/test_se_t.py
  • source/tests/pt_expt/descriptor/test_se_t_tebd.py

then the rest (g1_shape_transform, repformers, etc.) proceeds normally.
"""
use_three_body = self.use_three_body
nframes, nloc, nnei = nlist.shape
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix Ruff RUF059 unused unpacked variables to avoid lint/CI noise.

Line [215] (nnei) and Line [327] (diff) are unpacked but never used. Prefix both with _ to make intent explicit.

♻️ Proposed fix
-        nframes, nloc, nnei = nlist.shape
+        nframes, nloc, _nnei = nlist.shape
...
-        rr, diff, sw = self.repinit.env_mat.call(
+        rr, _diff, sw = self.repinit.env_mat.call(

As per coding guidelines, **/*.py: Always run ruff check . and ruff format . before committing changes or CI will fail.

Also applies to: 327-327

🧰 Tools
🪛 Ruff (0.15.6)

[warning] 215-215: Unpacked variable nnei is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/dpa2.py` at line 215, Unpackings in dpa2.py are
creating unused variables causing RUF059; change the unpack of nlist.shape in
the scope where nframes, nloc, nnei = nlist.shape to use a prefixed unused name
for the third value (e.g., nframes, nloc, _nnei = nlist.shape) and likewise
rename the unused variable diff at the other occurrence to _diff so intent is
explicit; update both occurrences referenced in the file and then run ruff check
. and ruff format . before committing.

) -> Any:
"""Compressed forward using tabulate_fusion_se_t custom op."""
# env_mat: nf x nloc x nnei x 4
rr, diff, ww = self.env_mat.call(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Verify configured Python target is compatible with zip(strict=...)
fd -HI 'pyproject.toml|setup.cfg|tox.ini|.ruff.toml'
rg -n "requires-python|python_requires|target-version" || true

# 2) Re-scan this file for the flagged patterns
rg -nP 'rr,\s*diff,\s*ww\s*=' deepmd/pt_expt/descriptor/se_t.py || true
rg -nP 'zip\s*\(\s*self\.compress_data\s*,\s*self\.compress_info\s*\)' deepmd/pt_expt/descriptor/se_t.py || true

Repository: deepmodeling/deepmd-kit

Length of output: 802


🏁 Script executed:

cat -n deepmd/pt_expt/descriptor/se_t.py | sed -n '130,140p'

Repository: deepmodeling/deepmd-kit

Length of output: 478


🏁 Script executed:

cat -n deepmd/pt_expt/descriptor/se_t.py | sed -n '152,160p'

Repository: deepmodeling/deepmd-kit

Length of output: 469


🏁 Script executed:

cat -n deepmd/pt_expt/descriptor/se_t.py | sed -n '125,180p'

Repository: deepmodeling/deepmd-kit

Length of output: 2592


🏁 Script executed:

# Check if 'diff' variable is used after line 133 in the _call_compressed function
rg -A 50 'def _call_compressed' deepmd/pt_expt/descriptor/se_t.py | grep -n 'diff'

Repository: deepmodeling/deepmd-kit

Length of output: 110


🏁 Script executed:

# Verify that both ww and diff are unused in the function
rg -A 60 'def _call_compressed' deepmd/pt_expt/descriptor/se_t.py | grep -E '\b(ww|diff)\b'

Repository: deepmodeling/deepmd-kit

Length of output: 152


🏁 Script executed:

# Check the definition and usage of compress_data and compress_info
rg -B 5 -A 5 'compress_data|compress_info' deepmd/pt_expt/descriptor/se_t.py | head -80

Repository: deepmodeling/deepmd-kit

Length of output: 2664


🏁 Script executed:

# Verify ww is indeed used in the return statement
rg -A 65 'def _call_compressed' deepmd/pt_expt/descriptor/se_t.py | tail -10

Repository: deepmodeling/deepmd-kit

Length of output: 455


Address Ruff RUF059 and B905 violations in the compressed forward path.

Line 133 unpacks diff but never uses it; rename to _diff to indicate intentional non-use.
Line 155 should use zip(..., strict=True) to enforce that both parameter lists have equal length and satisfy the B905 check.

♻️ Proposed fix
-        rr, diff, ww = self.env_mat.call(
+        rr, _diff, ww = self.env_mat.call(
         for embedding_idx, (compress_data_ii, compress_info_ii) in enumerate(
-            zip(self.compress_data, self.compress_info)
+            zip(self.compress_data, self.compress_info, strict=True)
         ):

As per coding guidelines, **/*.py: Always run ruff check . and ruff format . before committing changes or CI will fail.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rr, diff, ww = self.env_mat.call(
rr, _diff, ww = self.env_mat.call(
🧰 Tools
🪛 Ruff (0.15.6)

[warning] 133-133: Unpacked variable diff is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/descriptor/se_t.py` at line 133, Rename the unused unpacked
variable diff from the call rr, diff, ww = self.env_mat.call(...) to rr, _diff,
ww = self.env_mat.call(...) in the compressed forward path to satisfy RUF059,
and update the subsequent loop that currently uses zip(param_list_a,
param_list_b) to zip(param_list_a, param_list_b, strict=True) (use the exact
parameter list variable names present around line 155) to enforce equal-length
iteration and satisfy B905; make these two edits inside the same compressed
forward function/method where self.env_mat.call and the zip loop occur
(preserving all other logic).

Comment on lines +91 to +98
# 3. Enable compression
log.info("Enabling compression...")
model.enable_compression(
extrapolate,
stride,
stride * 10,
check_frequency,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical bug: min_nbor_dist_val is not passed to enable_compression.

The computed min_nbor_dist_val is never used. According to the descriptor's enable_compression signature (see deepmd/pt_expt/descriptor/se_t.py lines 30-37), min_nbor_dist must be the first positional argument. Currently, extrapolate is being passed as the first argument, which will be interpreted as min_nbor_dist, causing incorrect table bounds.

🐛 Proposed fix
     # 3. Enable compression
     log.info("Enabling compression...")
     model.enable_compression(
+        min_nbor_dist_val,
         extrapolate,
         stride,
         stride * 10,
         check_frequency,
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 3. Enable compression
log.info("Enabling compression...")
model.enable_compression(
extrapolate,
stride,
stride * 10,
check_frequency,
)
# 3. Enable compression
log.info("Enabling compression...")
model.enable_compression(
min_nbor_dist_val,
extrapolate,
stride,
stride * 10,
check_frequency,
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/entrypoints/compress.py` around lines 91 - 98, The call to
model.enable_compression currently passes arguments in the wrong order so
extrapolate is being used as min_nbor_dist; replace the call to
model.enable_compression(...) so that min_nbor_dist_val is supplied as the first
positional argument (per the enable_compression signature in
descriptor/se_t.py), followed by extrapolate, stride, stride * 10, and
check_frequency; update the invocation at the model.enable_compression(...) site
to pass min_nbor_dist_val first to ensure correct table bounds.

Comment on lines +243 to +244
rng = np.random.default_rng(GLOBAL_SEED)
nf, nloc, nnei = self.nlist.shape
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove the unused shape tuple members.

Line 244 leaves nf and nloc unused, and Ruff is already flagging this as RUF059. Use _ placeholders so this test doesn't fail the Python lint gate.

🧹 Minimal fix
-        nf, nloc, nnei = self.nlist.shape
+        _, _, nnei = self.nlist.shape
As per coding guidelines, `**/*.py`: Always run `ruff check .` and `ruff format .` before committing changes or CI will fail.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rng = np.random.default_rng(GLOBAL_SEED)
nf, nloc, nnei = self.nlist.shape
rng = np.random.default_rng(GLOBAL_SEED)
_, _, nnei = self.nlist.shape
🧰 Tools
🪛 Ruff (0.15.6)

[warning] 244-244: Unpacked variable nf is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


[warning] 244-244: Unpacked variable nloc is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/tests/pt_expt/descriptor/test_dpa2.py` around lines 243 - 244, The
tuple unpacking of self.nlist.shape assigns unused variables nf and nloc causing
a RUF059 lint error; change the unpack to use placeholders (e.g., "_, _, nnei =
self.nlist.shape" or "_, _, nnei = ..." ) so only nnei is kept and rng =
np.random.default_rng(GLOBAL_SEED) remains unchanged—update the line that
currently reads "nf, nloc, nnei = self.nlist.shape" to use underscores for the
unused members.

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 91.21523% with 60 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.37%. Comparing base (09345bf) to head (c343a10).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
deepmd/pt_expt/descriptor/dpa2.py 85.03% 19 Missing ⚠️
deepmd/pt_expt/entrypoints/compress.py 58.06% 13 Missing ⚠️
deepmd/pt_expt/descriptor/dpa1.py 90.19% 10 Missing ⚠️
deepmd/pt_expt/utils/tabulate_ops.py 79.41% 7 Missing ⚠️
deepmd/pt_expt/entrypoints/main.py 0.00% 3 Missing ⚠️
deepmd/pt_expt/utils/tabulate.py 92.68% 3 Missing ⚠️
deepmd/pt_expt/descriptor/se_t_tebd.py 97.67% 2 Missing ⚠️
deepmd/pt_expt/descriptor/se_e2_a.py 98.80% 1 Missing ⚠️
deepmd/pt_expt/descriptor/se_r.py 98.24% 1 Missing ⚠️
deepmd/pt_expt/descriptor/se_t.py 98.61% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5323      +/-   ##
==========================================
+ Coverage   82.29%   82.37%   +0.07%     
==========================================
  Files         775      778       +3     
  Lines       77627    78311     +684     
  Branches     3676     3675       -1     
==========================================
+ Hits        63887    64512     +625     
- Misses      12566    12625      +59     
  Partials     1174     1174              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

… compression pipeline

- torch_module decorator auto-registers dpmodel base classes in the
  converter registry, so auto-wrapped parent objects (e.g. atomic models)
  get pt_expt sub-components with enable_compression support
- Store min_nbor_dist as a buffer on the model (property with -1.0
  sentinel), matching the pt backend behavior for serialization
- Clean up compress entry point: remove dead fallback, set min_nbor_dist
  on model before calling enable_compression
- Add end-to-end model compression tests: model.enable_compression,
  freeze→compress→eval pipeline, min_nbor_dist round-trip
@wanghan-iapcm wanghan-iapcm marked this pull request as ready for review March 17, 2026 16:08
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
deepmd/pt_expt/entrypoints/compress.py (1)

91-97: ⚠️ Potential issue | 🔴 Critical

Critical bug: min_nbor_dist is not passed to enable_compression.

The enable_compression method on descriptors (see deepmd/pt_expt/descriptor/se_e2_a.py:29-36) expects min_nbor_dist as the first positional argument:

def enable_compression(
    self,
    min_nbor_dist: float,
    table_extrapolate: float = 5,
    table_stride_1: float = 0.01,
    table_stride_2: float = 0.1,
    check_frequency: int = -1,
) -> None:

Currently, extrapolate is passed as the first argument, which will be interpreted as min_nbor_dist, causing incorrect table bounds during compression.

🐛 Proposed fix
     # 3. Enable compression
     log.info("Enabling compression...")
     model.enable_compression(
+        min_nbor_dist,
         extrapolate,
         stride,
         stride * 10,
         check_frequency,
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/pt_expt/entrypoints/compress.py` around lines 91 - 97, The call to
model.enable_compression is passing arguments in the wrong order — the
descriptor method enable_compression expects min_nbor_dist as the first
parameter. Update the call to include the min_nbor_dist variable as the first
argument (so the call becomes model.enable_compression(min_nbor_dist,
extrapolate, stride, stride * 10, check_frequency)), ensuring the parameters
align with the enable_compression signature in descriptor/se_e2_a.py.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@deepmd/pt_expt/entrypoints/compress.py`:
- Around line 91-97: The call to model.enable_compression is passing arguments
in the wrong order — the descriptor method enable_compression expects
min_nbor_dist as the first parameter. Update the call to include the
min_nbor_dist variable as the first argument (so the call becomes
model.enable_compression(min_nbor_dist, extrapolate, stride, stride * 10,
check_frequency)), ensuring the parameters align with the enable_compression
signature in descriptor/se_e2_a.py.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d0856493-8293-4f4e-81a0-8f835b074124

📥 Commits

Reviewing files that changed from the base of the PR and between adae1e0 and c343a10.

📒 Files selected for processing (4)
  • deepmd/pt_expt/common.py
  • deepmd/pt_expt/entrypoints/compress.py
  • deepmd/pt_expt/model/make_model.py
  • source/tests/pt_expt/model/test_model_compression.py

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c343a10521

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

meta/fake tensor implementations for custom ops.
"""
# --- tabulate_fusion_se_a ---
if hasattr(torch.ops.deepmd, "tabulate_fusion_se_a"):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register fake tabulate ops after custom ops are loaded

_register_fake_if_available() only installs fake kernels when hasattr(torch.ops.deepmd, ...) is already true, but in pt_expt the module is imported early via deepmd.pt_expt.common._ensure_registrations() before deepmd.pt loads the custom library (deepmd/pt/__init__.py). In that import order every check is skipped and registration is never retried, so later symbolic tracing in deserialize_to_file(..., tracing_mode="symbolic") can hit torch.ops.deepmd.tabulate_fusion_* without fake/meta implementations and fail during compression export.

Useful? React with 👍 / 👎.

Comment on lines +101 to +104
data = {
"model": model.serialize(),
"model_def_script": model_dict.get("model_def_script"),
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve min_nbor_dist when writing compressed .pte metadata

The output payload omits min_nbor_dist even though this command reads that field on input (model_dict.get("min_nbor_dist")), and BaseModel.serialize() does not include model-level neighbor stats. As a result, a once-compressed .pte can lose neighbor-distance metadata in model.json, so re-running dp --pt_expt compress on that artifact may incorrectly require --training-script again.

Useful? React with 👍 / 👎.

@njzjz
Copy link
Member

njzjz commented Mar 18, 2026

pre-commit.ci autofix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants