Skip to content

fix(dpmodel): exclude virtual atoms from neighbor stats - #5834

Open
njzjz-bot wants to merge 2 commits into
deepmodeling:masterfrom
njzjz-bot:fix/dpmodel-neighbor-stat-virtual-atoms-5630
Open

fix(dpmodel): exclude virtual atoms from neighbor stats#5834
njzjz-bot wants to merge 2 commits into
deepmodeling:masterfrom
njzjz-bot:fix/dpmodel-neighbor-stat-virtual-atoms-5630

Conversation

@njzjz-bot

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

Copy link
Copy Markdown
Contributor

Summary

  • require both center and neighbor atoms to be real before distance and neighbor-count reductions
  • preserve periodic self-image neighbors while excluding only the original self pair
  • use numeric count masks for strict Array API reductions
  • add periodic/non-periodic and mixed/non-mixed virtual-atom regression coverage

Why existing tests missed this

The existing neighbor-stat tests build datasets containing only type-0 real atoms. They validate aggregate CLI statistics across cutoffs and backends, so neither a negative center type nor a negative neighbor type ever reaches NeighborStatOP. The new geometry contains an overlapping virtual neighbor that previously forced the minimum distance to zero and a virtual center that previously inflated the type-0 maximum neighbor count from one to two.

Validation

  • ruff format .
  • ruff check .
  • focused NumPy and strict Array API virtual-atom tests
  • existing NumPy, PyTorch, and JAX neighbor-stat suites, including periodic self-image behavior
  • TensorFlow C++ core regression test
  • total focused result: 6 passed with 30 subtests
  • manual NumPy, Array API Strict, PyTorch, and JAX virtual-atom scenario

Closes #5630

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

Summary by CodeRabbit

  • Bug Fixes

    • Virtual atoms and invalid neighbor pairs are now excluded from distance and neighbor-count calculations.
    • Neighbor statistics now correctly handle self-pairs and virtual entries across mixed and non-mixed type modes.
    • Invalid distances are safely ignored when determining minimum neighbor distances.
  • Tests

    • Added coverage for virtual-atom masking, distance calculations, neighbor counts, output shapes, and array metadata preservation.

Mask virtual centers and neighbors before distance and count reductions. Add periodic, mixed-type, and strict Array API regression coverage.

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 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

NeighborStatOP now masks self-pairs and virtual atoms before distance and neighbor-count reductions. New tests cover mixed and per-type modes, periodic and non-periodic cells, Paddle execution, and Array API output consistency.

Changes

Virtual atom neighbor statistics

Layer / File(s) Summary
Valid-pair masking and reductions
deepmd/dpmodel/utils/neighbor_stat.py, deepmd/pd/utils/neighbor_stat.py
Distance and neighbor-count calculations exclude self-pairs, virtual centers, and virtual neighbors. Invalid distances become infinite.
Neighbor statistics regression coverage
source/tests/common/dpmodel/test_neighbor_stat.py, source/tests/common/dpmodel/array_api/test_neighbor_stat.py, source/tests/pd/test_neighbor_stat.py
Tests verify virtual atoms are excluded across cell configurations, counting modes, Paddle execution, and Array API properties.

Estimated code review effort: 3 (Moderate) | ~25 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 and concisely describes the main change: excluding virtual atoms from dpmodel neighbor statistics.
Linked Issues check ✅ Passed The changes satisfy issue #5630 by masking virtual centers and neighbors before distance and count reductions in mixed and non-mixed modes.
Out of Scope Changes check ✅ Passed The implementation and regression tests remain focused on excluding virtual atoms from neighbor statistics across supported backends and modes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.38%. Comparing base (6c3b985) to head (8b594d7).
⚠️ Report is 83 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5834      +/-   ##
==========================================
+ Coverage   78.58%   79.38%   +0.80%     
==========================================
  Files        1050     1085      +35     
  Lines      120637   126576    +5939     
  Branches     4356     4592     +236     
==========================================
+ Hits        94801   100487    +5686     
- Misses      24278    24434     +156     
- Partials     1558     1655      +97     

☔ 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/dpmodel/utils/neighbor_stat.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
@njzjz
njzjz requested a review from wanghan-iapcm August 10, 2026 03:44

@njzjz-bot njzjz-bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reviewed head 7ddcaae1f677abd65abf7a11b82c55d8f125d726 with three independent full subagent reviews. No actionable findings were identified in this dpmodel-scoped diff. The pair mask correctly excludes virtual centers and neighbors, preserves periodic self-image neighbors, and keeps strict Array API count reductions numeric. One reviewer noted that Paddle has an analogous independent pre-existing implementation; that is outside this PR's declared dpmodel scope and should be tracked separately rather than block this fix. All current checks pass. Because this PR was opened by the active njzjz-bot account, this is a comment-only review rather than a self-approval event.

The quota is about to reset, so I am concentrating the remaining token budget on these reviews.

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

Comment thread deepmd/dpmodel/utils/neighbor_stat.py

@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 right, and the regression coverage is real: I ran source/tests/common/dpmodel/test_neighbor_stat.py against the pre-fix neighbor_stat.py and it fails, then against this branch and it passes, so the test genuinely exposes the bug rather than merely accompanying the fix. Requiring both ends of a pair to be real also converges dpmodel onto what the TF backend has done since #3275 (virtual_type_mask_i/virtual_type_mask_j), so this is an alignment rather than a new convention.

One gap, in the inline comment: the Paddle backend keeps its own copy of this operator and does not get the fix.

A second, related point that is why nothing in CI will notice. source/tests/consistent/test_neighbor_stat.py is the only test that compares tf/pt/dp/jax/pd against one another, and its gen_sys() builds np.repeat(0, 27), so no negative type ever reaches NeighborStatOP. Both tests this PR adds call the dpmodel operator directly. That is precisely how the original gap survived unnoticed, and it will keep the Paddle divergence invisible too. Adding the virtual-atom geometry to run_neighbor_stat there, where it runs across all five backends, would close both at once.

Comment on lines +95 to +96
real_center = atype >= 0
real_neighbor = extend_atype >= 0

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.

real_center and real_neighbor are the fix, and both are correct. The same operator is duplicated in Paddle, which this PR does not touch: deepmd/pd/utils/neighbor_stat.py#L88-L112. It is a hand-written paddle.nn.Layer, not an import of this class, so it does not inherit the change. deepmd/pt, deepmd/jax and deepmd/pt_expt all import NeighborStatOP from here and do pick it up, which leaves Paddle as the only backend behind.

That copy is line-for-line the pre-fix logic: it masks only the eye(nloc, nall) self pair before min_rr2, and its non-mixed branch never excludes virtual centers at all. On this PR's own fixture (coord = [(0,0,0), (0,0,0), (1,0,0), (3,0,0)], atype = [0,-1,0,1], rcut = 1.1) Paddle still returns min_rr2 = [0, 0, 1, 4] and max_nnei = [[2]], against [1, inf, 1, 4] and [[1]] everywhere else. So dp --pd neighbor-stat on a mixed-type dataset whose padding slot lands on a real atom still aborts with RuntimeError: Some atoms are overlapping, and where it does not abort it picks a strictly larger auto-sel than the other backends for the same data. Paddle's own build_neighbor_list already relocates virtual atoms, so that extra sel is pure waste.

CLAUDE.md asks for this explicitly: "Before editing shared code (deepmd/dpmodel/, base classes), find every importer and run all affected backends' tests: backends routinely re-export or subclass generic classes, so one change ripples across tf/pt/pt_expt/dpmodel/jax/pd." The same point came up on #5854 and #5856 and was handled in a follow-up commit each time; the cleanest resolution here is the same.

@njzjz-bot njzjz-bot Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8b594d7. The Paddle implementation now excludes self pairs, virtual centers, and virtual neighbors before both the minimum-distance and neighbor-count reductions, matching the generic operator. I also added direct Paddle coverage for periodic/nonperiodic inputs and mixed/type-separated counts using this fixture.

Validation:

  • pytest source/tests/pd/test_neighbor_stat.py -q: 2 passed, 12 subtests passed
  • generic neighbor-stat regressions: 2 passed, 6 subtests passed
  • ruff format . and ruff check .: passed

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

Apply the same real-center and real-neighbor masking as the generic neighbor-stat operator, and cover periodic, nonperiodic, mixed, and type-separated Paddle cases.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/tests/pd/test_neighbor_stat.py`:
- Around line 99-113: Extend the NeighborStatOP parameterized cell cases in the
test around NeighborStatOP to include a periodic cell with at least one axis
shorter than rcut. Add expected assertions for that case verifying the original
self-pair remains excluded while an in-cutoff periodic self-image increases
min_rr2 and max_nnei, without changing the existing non-periodic and large-cell
expectations.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 807eb0a0-ff77-4621-a5e2-69e23fa0dcad

📥 Commits

Reviewing files that changed from the base of the PR and between 7ddcaae and 8b594d7.

📒 Files selected for processing (2)
  • deepmd/pd/utils/neighbor_stat.py
  • source/tests/pd/test_neighbor_stat.py

Comment on lines +99 to +113
for cell in (
None,
10.0 * paddle.eye(3, dtype=paddle.float64).reshape([1, 9]).to(DEVICE),
):
for mixed_types in (False, True):
with self.subTest(cell=cell is not None, mixed_types=mixed_types):
min_rr2, max_nnei = NeighborStatOP(
ntypes=2,
rcut=1.1,
mixed_types=mixed_types,
)(coord, atype, cell)

np.testing.assert_allclose(min_rr2.numpy(), expected_min_rr2)
expected_max_nnei = [[1]] if mixed_types else [[1, 0]]
np.testing.assert_array_equal(max_nnei.numpy(), expected_max_nnei)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add a periodic self-image regression case.

Line 101 uses cell edges of 10.0 with rcut=1.1. Each periodic self-image is outside the cutoff. This test executes the periodic path, but it cannot verify that the implementation excludes only the original self-pair and retains periodic self-images.

Add a case with at least one cell axis shorter than rcut. Assert that the original self-pair is excluded and that an in-cutoff periodic self-image contributes to min_rr2 and max_nnei.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/tests/pd/test_neighbor_stat.py` around lines 99 - 113, Extend the
NeighborStatOP parameterized cell cases in the test around NeighborStatOP to
include a periodic cell with at least one axis shorter than rcut. Add expected
assertions for that case verifying the original self-pair remains excluded while
an in-cutoff periodic self-image increases min_rr2 and max_nnei, without
changing the existing non-periodic and large-cell expectations.

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] Ignore virtual atoms in dpmodel neighbor statistics

3 participants