Commit d155800
refactor(loss): extract masked per-frame reduction idioms to cut nested branching (#5783)
Resolves #5768. Follow-up to #5738.
## What this does
The mixed_type padding fix (#5738) added a masked (`maskf is not None`)
branch to every term of the shared losses. Combined with the existing
`has_*` / `mse`|`mae` / `use_huber` conditions, that produced deeply
nested, duplicated per-frame arithmetic repeated across energy, force,
virial, atom_ener, atom_pref, dos, tensor, and again across `ener_spin`,
in both the dpmodel and pt backends.
This PR extracts the three recurring masked reduction "idioms" into a
single shared `array_api_compat` module,
`deepmd/dpmodel/loss/reduction.py`:
- `masked_atom_mean(elem, maskf, ncomp)` — idiom 1: per-atom masked mean
over `ncomp` components (force, atom_ener, atom_pref, local dos/tensor,
spin real-force).
- `per_frame_component_mean(err)` — idiom 2 primitive: per-frame mean
over components; the caller applies the extensive
`1/real_natoms**norm_exp` weighting (energy, virial).
- `masked_atom_num(mask, natoms, dtype)` — idiom 3 companion: the
display-only atom-count divisor for already-reduced global quantities
(global dos/tensor).
Both backends import the same module; the pt backend calls it with torch
tensors, and `array_api_compat`'s torch-namespace dispatch preserves
autograd and produces bit-identical results.
## Numerics are preserved
This is a pure readability/maintainability refactor with no intended
behavior change. Each helper implements only the masked branch; every
call site keeps its original non-masked `else` expression verbatim, so
the "bit-identical for non-mixed batches" guarantee from #5738 holds
(defaulting the mask to all-ones would change the reduction order at the
ULP level and is deliberately avoided). Operand order and the
mask-application point are preserved in every conversion.
## Testing
The safety net is the grad-accumulation-invariant and
no-op-for-non-mixed tests added in #5738 plus the cross-backend
consistency loss tests, all of which stay green:
- `source/tests/common/dpmodel/test_loss_padding.py`,
`source/tests/pt/test_loss_padding.py`
- `source/tests/consistent/loss` (dpmodel <-> pt <-> tf): 244 passed,
176 skipped — unchanged, the load-bearing bit-identity guard
- new direct unit tests for the three helpers (numpy + torch, autograd,
bit-identity vs the torch-native inline form):
`source/tests/common/dpmodel/test_loss_reduction.py`
## Known limitations
- The shared module lives under `deepmd/dpmodel/loss` and is imported by
`deepmd/pt/loss` — the first such cross-backend import in the loss
layer. This is intentional (single source of truth for the reduction
math, consistent with `deepmd/dpmodel` being the designated shared-math
layer), but it does couple the pt loss backend to `array_api_compat`.
- The Huber-masked sub-branches, the `f_use_norm` force variant, the
generalized-force (`gf`) input masking, and the spin magnetic-force
(`mask_mag`) block are intentionally NOT routed through the helpers —
they are not one of the three idioms.
- The TensorFlow (`deepmd/tf`) loss backend is out of scope; it does not
use the mixed_type padding mask.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Improvements**
* Improved masked loss reductions across energy, forces, virial, tensor,
DOS, and spin training by switching to shared masked/per-frame reduction
helpers.
* Standardized masked atom normalization and per-frame aggregation for
consistent RMSE/MAE reporting in local and global DOS/tensor
computations.
* Preserved behavior and control flow across NumPy/JAX and PyTorch
execution paths.
* **Tests**
* Added coverage for masked mean, per-frame component mean, and
atom-count normalization (including `None` masks), plus PyTorch autograd
checks and no-NaN behavior on all-padding frames.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Han Wang <wang_han@iapcm.ac.cn>1 parent 34d1fdb commit d155800
10 files changed
Lines changed: 393 additions & 222 deletions
File tree
- deepmd
- dpmodel/loss
- pt/loss
- source/tests/common/dpmodel
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
14 | 18 | | |
15 | 19 | | |
16 | 20 | | |
| |||
134 | 138 | | |
135 | 139 | | |
136 | 140 | | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
142 | 144 | | |
143 | 145 | | |
144 | 146 | | |
| |||
161 | 163 | | |
162 | 164 | | |
163 | 165 | | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
169 | 169 | | |
170 | 170 | | |
171 | 171 | | |
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
| 184 | + | |
190 | 185 | | |
191 | 186 | | |
192 | 187 | | |
| |||
204 | 199 | | |
205 | 200 | | |
206 | 201 | | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
| 202 | + | |
213 | 203 | | |
214 | 204 | | |
215 | 205 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
14 | 18 | | |
15 | 19 | | |
16 | 20 | | |
| |||
296 | 300 | | |
297 | 301 | | |
298 | 302 | | |
299 | | - | |
| 303 | + | |
300 | 304 | | |
301 | 305 | | |
302 | 306 | | |
| |||
327 | 331 | | |
328 | 332 | | |
329 | 333 | | |
330 | | - | |
331 | | - | |
332 | | - | |
| 334 | + | |
333 | 335 | | |
334 | 336 | | |
335 | 337 | | |
| |||
346 | 348 | | |
347 | 349 | | |
348 | 350 | | |
349 | | - | |
350 | | - | |
| 351 | + | |
351 | 352 | | |
352 | 353 | | |
353 | 354 | | |
| |||
362 | 363 | | |
363 | 364 | | |
364 | 365 | | |
365 | | - | |
366 | | - | |
367 | | - | |
368 | | - | |
| 366 | + | |
369 | 367 | | |
370 | 368 | | |
371 | 369 | | |
| |||
435 | 433 | | |
436 | 434 | | |
437 | 435 | | |
438 | | - | |
439 | 436 | | |
440 | | - | |
441 | | - | |
442 | | - | |
443 | | - | |
| 437 | + | |
444 | 438 | | |
445 | 439 | | |
446 | 440 | | |
| |||
474 | 468 | | |
475 | 469 | | |
476 | 470 | | |
477 | | - | |
478 | | - | |
479 | | - | |
480 | | - | |
481 | | - | |
| 471 | + | |
482 | 472 | | |
483 | 473 | | |
484 | 474 | | |
| |||
494 | 484 | | |
495 | 485 | | |
496 | 486 | | |
497 | | - | |
| 487 | + | |
498 | 488 | | |
499 | 489 | | |
500 | 490 | | |
| |||
526 | 516 | | |
527 | 517 | | |
528 | 518 | | |
529 | | - | |
530 | | - | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
531 | 522 | | |
532 | 523 | | |
533 | 524 | | |
| |||
546 | 537 | | |
547 | 538 | | |
548 | 539 | | |
549 | | - | |
550 | | - | |
| 540 | + | |
551 | 541 | | |
552 | 542 | | |
553 | 543 | | |
| |||
565 | 555 | | |
566 | 556 | | |
567 | 557 | | |
568 | | - | |
569 | | - | |
570 | 558 | | |
571 | | - | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
572 | 562 | | |
573 | 563 | | |
574 | 564 | | |
| |||
609 | 599 | | |
610 | 600 | | |
611 | 601 | | |
612 | | - | |
613 | | - | |
614 | | - | |
615 | | - | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
616 | 605 | | |
617 | 606 | | |
618 | 607 | | |
| |||
637 | 626 | | |
638 | 627 | | |
639 | 628 | | |
640 | | - | |
641 | | - | |
642 | | - | |
643 | | - | |
644 | | - | |
645 | | - | |
646 | | - | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
647 | 632 | | |
648 | 633 | | |
649 | 634 | | |
| |||
660 | 645 | | |
661 | 646 | | |
662 | 647 | | |
663 | | - | |
664 | | - | |
665 | | - | |
666 | | - | |
667 | | - | |
| 648 | + | |
668 | 649 | | |
669 | 650 | | |
670 | 651 | | |
| |||
0 commit comments