Skip to content

feat(jax): support DPA4 training#5748

Open
njzjz wants to merge 3 commits into
deepmodeling:masterfrom
njzjz:feat/dpa4-jax-train
Open

feat(jax): support DPA4 training#5748
njzjz wants to merge 3 commits into
deepmodeling:masterfrom
njzjz:feat/dpa4-jax-train

Conversation

@njzjz

@njzjz njzjz commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Add JAX wrappers/registrations for DPA4/SeZM descriptor and dpa4_ener fitting.
  • Route model.type: dpa4/sezm through the JAX model factory.
  • Handle DPA4 force shape normalization and zero-size checkpoint leaves in the JAX trainer.
  • Enable DPA4 JAX coverage in the existing source/tests/consistent/descriptor/test_dpa4.py and source/tests/consistent/fitting/test_dpa4_ener.py files.

Benchmark

1000-step DPA4 water benchmark, batch size 1, srun --gres=gpu:1 dp, GPU: NVIDIA GeForce RTX 5090.

Backend Command Total wall time Batch 1000 avg Notes
JAX dp --jax train input_jax.json --skip-neighbor-stat 95.860 s 0.0362 s/step Includes initial JAX compile; batch 1 avg 37.0563 s/step.
TF2 dp --tf2 train input_tf2.json --skip-neighbor-stat 164.388 s 0.0148 s/step Includes TF/XLA/PTX compile; batch 1 avg 149.4788 s/step.

PT/pt-expt are not included in the final comparison per follow-up scope.

Validation

  • ruff format .
  • ruff check .
  • python -m py_compile deepmd/jax/descriptor/dpa4.py deepmd/jax/fitting/dpa4_ener.py deepmd/jax/model/model.py deepmd/jax/train/trainer.py source/tests/consistent/descriptor/test_dpa4.py source/tests/consistent/fitting/test_dpa4_ener.py
  • dp --jax train /tmp/deepmd_dpa4_tiny_1vnghlkx/input_jax.json --skip-neighbor-stat -o /tmp/deepmd_dpa4_tiny_1vnghlkx/out_jax.json
  • srun --gres=gpu:1 dp --jax train /tmp/deepmd_dpa4_bench_1000/input_jax.json --skip-neighbor-stat -o /tmp/deepmd_dpa4_bench_1000/out_jax.json

Note: collecting the existing DPA4 consistent test module in this local environment segfaults while importing the existing PT DPA4/Triton path before -k jax selection is applied, so I validated the new JAX path with the smoke train and benchmark above.

Summary by CodeRabbit

  • New Features

    • Added public JAX exports for the DPA4 descriptor and DPA4/SeZM energy fitting support.
    • Extended JAX model routing with SeZM/DPA4 aliases, including automatic descriptor and fitting-net defaults.
  • Bug Fixes

    • Improved JAX training loss evaluation by matching model/label force tensor shapes.
    • Hardened JAX checkpoint save/restore to correctly handle empty or zero-size tensor leaves.
  • Tests

    • Updated DPA4 and DPA4 energy consistency tests to run on the JAX backend only when available.

@dosubot dosubot Bot added the new feature label Jul 7, 2026

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added the Python label Jul 7, 2026
Comment thread deepmd/jax/descriptor/dpa4.py Fixed
Comment thread deepmd/jax/descriptor/dpa4.py Fixed
Comment thread deepmd/jax/descriptor/dpa4.py Fixed
ClassVar,
)

import deepmd.jax.utils.network as _jax_network # noqa: F401
@coderabbitai

coderabbitai Bot commented Jul 7, 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

Run ID: 400fb8b0-306e-413f-80e5-d9fa31251154

📥 Commits

Reviewing files that changed from the base of the PR and between 590823b and 1c8c03b.

📒 Files selected for processing (1)
  • deepmd/jax/descriptor/dpa4.py
💤 Files with no reviewable changes (1)
  • deepmd/jax/descriptor/dpa4.py

📝 Walkthrough

Walkthrough

Adds JAX/Flax wrappers and registrations for DPA4/SeZM descriptor and fitting components, routes model construction through a SeZM model factory, updates trainer force handling and checkpoint serialization, restores omitted zero-size checkpoint leaves, and extends consistency tests to run JAX backends.

Changes

DPA4/SeZM JAX Support

Layer / File(s) Summary
DPA4 descriptor JAX wrappers and trainable-parameter promotion
deepmd/jax/descriptor/dpa4.py, deepmd/jax/descriptor/__init__.py
Adds Flax wrapper classes, dpmodel mappings, recursive trainable-parameter promotion, and public export of DescrptDPA4.
DPA4 energy fitting wrappers and model routing
deepmd/jax/fitting/dpa4_ener.py, deepmd/jax/fitting/__init__.py, deepmd/jax/model/ener_model.py, deepmd/jax/model/model.py
Adds fitting wrappers and registrations, registers energy model aliases, and adds SeZM/DPA4 configuration normalization and dispatch.
Trainer force-shape matching and checkpoint fixes
deepmd/jax/train/trainer.py
Normalizes force output shapes for loss evaluation and removes zero-size leaves before checkpoint writes.
Zero-size checkpoint restoration
deepmd/jax/utils/serialization.py
Restores omitted zero-size state subtrees during JAX checkpoint deserialization.
JAX backend consistency tests
source/tests/consistent/descriptor/test_dpa4.py, source/tests/consistent/fitting/test_dpa4_ener.py
Conditionally imports JAX implementations, enables backend checks when available, and adds JAX evaluation methods.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant get_model
  participant get_sezm_model
  participant get_standard_model
  participant EnergyModel

  Caller->>get_model: SeZM or DPA4 configuration
  get_model->>get_sezm_model: route configuration
  get_sezm_model->>get_sezm_model: validate and normalize fields
  get_sezm_model->>get_standard_model: pass normalized configuration
  get_standard_model->>EnergyModel: construct registered model
  EnergyModel-->>Caller: model instance
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: wanghan-iapcm, iProzd

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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: adding JAX support for DPA4 training.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@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: 3

🧹 Nitpick comments (1)
deepmd/jax/descriptor/dpa4.py (1)

216-249: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

New promotion logic appears untested by the accompanying consistency test.

_promote_trainable/_promote_trainable_lists (and therefore the whole _promote_trainable_tree path wired into DescrptDPA4.__init__/deserialize) are gated by getattr(module, "trainable", True) and return immediately when trainable is falsy. The DPA4 descriptor consistency test fixture (source/tests/consistent/descriptor/test_dpa4.py) constructs the descriptor with "trainable": False, which means this newly added promotion path is never actually exercised by the new JAX consistency coverage introduced in this PR stack.

Also applies to: 270-283

🤖 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 `@deepmd/jax/descriptor/dpa4.py` around lines 216 - 249, The new
trainable-promotion path in _promote_trainable, _promote_trainable_lists, and
_promote_trainable_tree is skipped whenever module.trainable is false, so the
current DPA4 consistency coverage does not exercise it. Update the descriptor
consistency test in test_dpa4.py to include a trainable-true case (or otherwise
invoke DescrptDPA4.__init__ and deserialize with trainable enabled) so the
promotion logic is actually covered and validated.
🤖 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 `@deepmd/jax/fitting/dpa4_ener.py`:
- Around line 52-54: The SeZMEnergyFittingNet.__setattr__ override is currently
a no-op because it only forwards to super without adding any behavior. Either
remove this dead override entirely, or implement the intended
trainable-parameter promotion logic for SeZMEnergyFittingNet and any related
GLUFittingNet internals so this wrapper matches the descriptor-side pattern used
by _promote_trainable_tree.

In `@deepmd/jax/model/model.py`:
- Around line 127-130: The defaults in model/model.py do not handle explicit
null values for descriptor and fitting_net, so the subsequent setdefault calls
can fail on None. Update the initialization in the model config parsing logic
around the data.setdefault usage to normalize both fields with
data.get("descriptor") or {} and data.get("fitting_net") or {} before calling
setdefault on their type keys, keeping the existing dpa4 and dpa4_ener defaults
intact.

In `@deepmd/jax/train/trainer.py`:
- Line 836: The checkpoint save path in trainer.py is dropping zero-size leaves
via _drop_zero_size_array_leaves(state), but the restart flow that rebuilds the
model and calls replace_by_pure_dict() expects those paths to exist. Update the
save/restore logic around the trainer state so zero-size entries are either
preserved in the saved state or explicitly reinserted during restore before
replace_by_pure_dict() runs, using the existing checkpoint/state handling code
paths in trainer.py.

---

Nitpick comments:
In `@deepmd/jax/descriptor/dpa4.py`:
- Around line 216-249: The new trainable-promotion path in _promote_trainable,
_promote_trainable_lists, and _promote_trainable_tree is skipped whenever
module.trainable is false, so the current DPA4 consistency coverage does not
exercise it. Update the descriptor consistency test in test_dpa4.py to include a
trainable-true case (or otherwise invoke DescrptDPA4.__init__ and deserialize
with trainable enabled) so the promotion logic is actually covered and
validated.
🪄 Autofix (Beta)

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

Run ID: 5aa70f59-efa8-4ae4-ac9f-cf147f7b123b

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd6556 and 51ee5de.

📒 Files selected for processing (9)
  • deepmd/jax/descriptor/__init__.py
  • deepmd/jax/descriptor/dpa4.py
  • deepmd/jax/fitting/__init__.py
  • deepmd/jax/fitting/dpa4_ener.py
  • deepmd/jax/model/ener_model.py
  • deepmd/jax/model/model.py
  • deepmd/jax/train/trainer.py
  • source/tests/consistent/descriptor/test_dpa4.py
  • source/tests/consistent/fitting/test_dpa4_ener.py

Comment thread deepmd/jax/fitting/dpa4_ener.py Outdated
Comment thread deepmd/jax/model/model.py Outdated
Comment thread deepmd/jax/train/trainer.py
@njzjz

njzjz commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

Pushed follow-up commit 590823bb2 for the JAX CI/review feedback.\n\nChanges:\n- Avoid direct nnx.List usage when the installed Flax NNX does not provide it.\n- Remove the no-op SeZMEnergyFittingNet.__setattr__ override.\n- Normalize explicit null descriptor / fitting_net blocks before setting DPA4 defaults.\n- Reinsert missing zero-size state leaves during JAX checkpoint restore, matching the save-side filtering.\n- Reverted the extra source/tests/consistent churn from this follow-up, per request.\n\nLocal validation:\n- JAX smoke for DPA4 descriptor construction, fitting construction, DPA4 model construction with fitting_net: null, and zero-size state reinsertion passed.\n- ruff format . && ruff check . && git diff --check passed.\n\nCI is rerunning.

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.89668% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.49%. Comparing base (1cd6556) to head (1c8c03b).
⚠️ Report is 9 commits behind head on master.

Files with missing lines Patch % Lines
deepmd/jax/model/model.py 3.12% 31 Missing ⚠️
deepmd/jax/train/trainer.py 11.11% 24 Missing ⚠️
deepmd/jax/descriptor/dpa4.py 88.34% 19 Missing ⚠️
deepmd/jax/utils/serialization.py 45.83% 13 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5748      +/-   ##
==========================================
- Coverage   79.61%   79.49%   -0.12%     
==========================================
  Files        1014     1017       +3     
  Lines      115360   116009     +649     
  Branches     4274     4274              
==========================================
+ Hits        91843    92223     +380     
- Misses      21976    22238     +262     
- Partials     1541     1548       +7     

☔ 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.


import numpy as np

import deepmd.jax.utils.exclude_mask as _jax_exclude_mask # noqa: F401
)


_TRAINABLE_ATTRS: dict[str, tuple[str, ...]] = {

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.

Non-blocking (correctness — silently frozen params): promotion here is strictly allowlist-gated — _promote_trainable_tree promotes only the attributes named in _TRAINABLE_ATTRS / _TRAINABLE_LIST_ATTRS; anything else stays an ArrayAPIVariable (not an nnx.Param), so nnx.Optimizer(..., wrt=nnx.Param) / nnx.grad never update it — it is silently frozen during training. A few trainable dpmodel params appear to be missing from the maps:

  • FrameContract.weight / FrameExpand.weight — trainable in deepmd/dpmodel/descriptor/dpa4_nn/grid_net.py (pt registers them as nn.Parameter), reachable when node_wise_so3 / message_node_so3 instantiate a mode=="cross" grid net. Neither class is a key in the maps.
  • SeZMInteractionBlock.adam_ffn_layer_scales — a trainable LayerScale list when layer_scale=True (dpa4_nn/block.py), also listed in dpa4_nn/lora.py's unfreeze registry. The maps already cover the analogous SO2Convolution.adam_so2_layer_scales, but not this one.

These are opt-in (non-default) features, so a stock DPA4 config is unaffected — but when a user enables them, training silently skips those weights, and the forward-only consistency tests cannot catch it (a leaf gives identical output whether it is a Param or a Variable). Consider driving promotion from the dpmodel trainable flag / mirroring pt's nn.Parameter set rather than a hand-maintained allowlist (which will keep silently dropping future params), and adding a test that asserts the differentiated leaf set equals the trainable dpmodel param set.

return _NO_ZERO_SIZE_LEAF


def _restore_missing_zero_size_leaves(template: Any, restored: Any) -> Any:

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.

Non-blocking (test coverage): the new stateful glue in this PR ships without dedicated tests — only the two forward-consistency tests were added and source/tests/jax/ was not touched. Per CLAUDE.md ("UTs should cover all code paths, including both branches of boolean conditions"), worth covering:

  • the zero-size drop -> restore round-trip: _drop_zero_size_array_leaves (jax/train/trainer.py) paired with _restore_missing_zero_size_leaves / _zero_size_subtree here. This mutates checkpoint state on save and reconstructs it on load, so a mismatch would silently corrupt a restored model — and it is trivially testable as a pure round-trip assertion.
  • get_sezm_model's guard branches (spin / bridging / lora / use_compile / preset_out_bias, plus the descriptor/fitting/exclude_types mismatches) — source/tests/jax/test_model_factory.py is the established home for routing/validation tests.
  • _match_label_shapes and the force-reshape branch in _evaluate_model_dict (both the reshape and no-op branches).

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.

4 participants