Skip to content

fix(tf2): reject unsupported custom neighbor lists - #5858

Merged
njzjz merged 2 commits into
deepmodeling:masterfrom
njzjz-bot:fix/tf2-deepeval-neighbor-list-5667
Jul 30, 2026
Merged

fix(tf2): reject unsupported custom neighbor lists#5858
njzjz merged 2 commits into
deepmodeling:masterfrom
njzjz-bot:fix/tf2-deepeval-neighbor-list-5667

Conversation

@njzjz-bot

@njzjz-bot njzjz-bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Closes #5667.

Summary

  • reject custom ASE neighbor lists in the TF2 SavedModel inference constructor
  • explain that SavedModel lower calls accept tensors and require a dedicated ASE-to-extended-tensors adapter plus output fold-back
  • add a focused regression test proving rejection happens before model loading

Why existing tests missed this

  • TestDeepPotNeighborList exercises the TensorFlow 1 .pb inference path, not TF2 SavedModel inference.
  • TF2 consistency tests use the SavedModel native neighbor construction path and never pass the constructor ASE API.
  • live TF2 model tests use the internal tensor-compatible NeighborList, which is distinct from the inference constructor ASE object.
  • TF2 test collection is conditional, further reducing coverage of this backend-specific constructor path.

Validation

  • pytest source/tests/tf2/test_deep_eval.py::test_custom_neighbor_list_is_rejected_before_model_loading -v
  • ruff format .
  • ruff check .
  • git diff --check

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

Summary by CodeRabbit

  • Bug Fixes
    • TensorFlow 2 SavedModel inference clearly rejects unsupported custom neighbor-list configurations.
    • The user-facing error message indicates that the model’s built-in neighbor-list builder must be used.
  • Tests
    • Added a TensorFlow 2 inference adapter test to verify custom neighbor-list rejection occurs before model loading (with environment gating for TF2-only runs).

Fail fast when TF2 SavedModel inference receives a custom neighbor_list. The
exported tensor signatures cannot carry an ASE Python object, so accepting it
would silently fall back to native neighbor construction.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@dosubot dosubot Bot added the bug label Jul 17, 2026
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a1df693-6f64-4c57-b7d2-02f27114c577

📥 Commits

Reviewing files that changed from the base of the PR and between ccb21ee and 69ff61e.

📒 Files selected for processing (2)
  • deepmd/tf2/infer/deep_eval.py
  • source/tests/tf2/test_deep_eval.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • deepmd/tf2/infer/deep_eval.py

📝 Walkthrough

Walkthrough

DeepEval now explicitly rejects custom neighbor lists for TensorFlow 2 SavedModel inference. A gated regression test verifies the exception and message before model loading.

Changes

TF2 neighbor list validation

Layer / File(s) Summary
Reject unsupported custom neighbor lists
deepmd/tf2/infer/deep_eval.py, source/tests/tf2/test_deep_eval.py
DeepEval raises NotImplementedError when neighbor_list is provided, and the test verifies the "custom ASE neighbor_list" message pattern before model loading.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: wanghan-iapcm

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: TF2 DeepEval now rejects unsupported custom neighbor lists.
Linked Issues check ✅ Passed The PR satisfies #5667 by explicitly rejecting non-None neighbor_list values and adding regression coverage.
Out of Scope Changes check ✅ Passed The changes stay focused on TF2 neighbor-list rejection and the corresponding test.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.96%. Comparing base (6c3b985) to head (69ff61e).
⚠️ Report is 47 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5858      +/-   ##
==========================================
+ Coverage   78.58%   78.96%   +0.37%     
==========================================
  Files        1050     1069      +19     
  Lines      120637   124072    +3435     
  Branches     4356     4527     +171     
==========================================
+ Hits        94801    97971    +3170     
- Misses      24278    24482     +204     
- Partials     1558     1619      +61     

☔ View full report in Codecov by Harness.
📢 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.

@njzjz

njzjz commented Jul 18, 2026

Copy link
Copy Markdown
Member

Possible reviewers based on changed lines, exact file history, and exact-file review history:

  • @wanghan-iapcm — 1 commits on changed files (deepmd/tf2/infer/deep_eval.py).

No review request was made automatically.

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

@njzjz
njzjz requested review from wanghan-iapcm and removed request for wanghan-iapcm July 18, 2026 07:26

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The fix itself is good. The guard is correctly ordered (after the extension check, before TF2SavedModelWrapper touches the filesystem), is not None is the right predicate for a keyword-only argument that defaults to None, there is no bypass through DeepPot / DeepEval / deepmd/calculator.py, and NotImplementedError matches both the codebase convention for an unsupported backend feature and what #5667 asked for. I ran the new test against the base commit and it fails there (OSError: SavedModel file does not exist at: sentinel.savedmodeltf/..., i.e. no NotImplementedError and execution reaches the loader) and passes at the head - a real regression test. Using a sentinel filename to prove the raise precedes model loading, without needing a fixture, is a nice touch.

One blocking item, inline: the new test file drops the module guard that isolates this directory, and one CI job collects it without the corresponding --ignore.

Smaller notes, none blocking:

  1. The code comment says supporting this "requires an ASE-to-extended-tensors adapter and output fold-back". Both already exist in-tree: _build_nlist_ase in deepmd/pt_expt/infer/deep_eval.py is pure NumPy and returns exactly (extended_coord, extended_atype, nlist, mapping); deepmd/tf2/utils/serialization.py already traces call_lower(coord, atype, nlist, mapping, fparam, aparam) with fully dynamic TensorSpec shapes; and communicate_extended_output lives in deepmd/tf2/transform_output.py and is already used for fold-back in deepmd/tf2/model/make_model.py. Real support is still non-trivial glue - _eval_model currently calls the high-level call, not call_lower, and the adapter's module imports torch at top level so it would need extracting - but it is composition of existing parts, not new architecture. Worth softening the comment so the next maintainer does not read it as blocked.

  2. The shared docstrings still promise this unconditionally - DeepEvalBackend.__init__ and DeepEval.__init__ in deepmd/infer/deep_eval.py, and DeepPot in deepmd/infer/deep_pot.py, all say "The ASE neighbor list class to produce the neighbor list. If None, the neighbor list will be built natively in the model." A one-line backend caveat there would stop DeepPot("model.savedmodeltf", neighbor_list=nl) being a documented call that now raises.

  3. The test that would prevent this recurring is TestDeepPotNeighborList in source/tests/infer/test_models.py, which is parameterized to .pb only while the base TestDeepPot runs a four-way matrix. Extending that matrix is what would catch the next backend to copy the signature forward; a tf2-only "it raises" test cannot.

  4. Out of scope for this PR, but worth a follow-up issue: after this merges the same call behaves three ways - implemented for .pb and .pt2, silently ignored for .pth, .pd, .dp and .savedmodel, and raising for .savedmodeltf. Note deepmd/pt/infer/deep_eval.py belongs in the second group despite storing the value: its only read is ase_provided = self.neighbor_list is not None, so the ASE object itself is never used to build a neighbor list. pd, jax and dpmodel additionally document the parameter while ignoring it.

Comment thread source/tests/tf2/test_deep_eval.py
Address the outstanding requested-change review comments.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.6
Model: gpt-5.6-sol
Reasoning-Effort: xhigh

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The guard is in the right place -- import pytest, then the DP_TEST_TF2_ONLY skip with allow_module_level=True, then the deepmd imports. That ordering is the whole point, since the import itself is what pulls in TensorFlow, and it now matches test_training.py. The test_cuda.yml job that runs a bare python -m pytest source/tests will skip this module at collection rather than loading the TF2 eager stack into a process that also holds TF1, PyTorch, JAX and Paddle.

Thanks for correcting the implementation comment too. Saying the wrapper "does not yet connect the existing adapter and output fold-back helpers" is accurate, where the original read as though the pieces did not exist.

The guard ordering inside DeepEval.__init__ is unchanged: extension check, then the neighbor_list rejection, then TF2SavedModelWrapper, so nothing touches the filesystem before the raise.

My remaining notes are unaddressed and I am not asking for them here -- the docstrings in deepmd/infer/deep_eval.py still promise the parameter unconditionally, and TestDeepPotNeighborList is still .pb-only. Both are really facets of the wider problem that neighbor_list now behaves three different ways depending on the model extension, so I have opened #5941 to track that rather than expanding this PR.

@njzjz
njzjz added this pull request to the merge queue Jul 30, 2026
Merged via the queue into deepmodeling:master with commit 1b3c9e9 Jul 30, 2026
58 checks passed
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.

[Code scan] Do not silently ignore TF2 DeepEval neighbor_list

3 participants