Commit f7d8a53
fix(dpmodel): guard empty magnetic loss masks (#5798)
## Summary
- replace zero magnetic-mask denominators with Array API-safe nonzero
denominators
- make all-empty magnetic-force masks contribute finite zero instead of
NaN for both MSE and MAE losses
- preserve the existing display semantics when the magnetic-force label
itself is absent
- add backend-neutral NumPy and Torch Array API regression coverage
## Root cause and fix
`EnergySpinLoss` masks non-magnetic atoms out of the force-magnitude
residual, then divides its reductions by `n_valid * 3`. When a batch
contains no magnetic atoms, both numerator and denominator are zero,
producing NaN. Multiplying that value by a zero prefactor or a
missing-label flag does not recover it because `0 * NaN` is still NaN.
The implementation now constructs:
```python
safe_n_valid = xp.where(n_valid > 0, n_valid, xp.ones_like(n_valid))
```
and uses it for every global magnetic-force MSE/MAE reduction. The
masked numerator is already zero for an empty set, so the contribution
becomes exactly zero. Guarding the denominator itself is important:
array backends may evaluate both branches of a later `where`, and an
unselected divide-by-zero can still produce invalid values or gradients.
The expression is compatible with Array API namespaces and JAX tracing;
non-empty batches retain the exact previous formula.
This PR intentionally does not change the native PyTorch loss or
redefine its missing-label display metrics. When `find_force_mag == 0`,
the total loss is finite zero while `display_if_exist` continues to
report NaN for the unavailable magnetic metric.
## Why existing tests missed this
Existing spin-loss mask tests always selected at least one magnetic atom
in each batch. They covered partial masks and all-magnetic batches, but
never the global zero denominator.
The new backend-neutral regression parameterizes:
- `mse` and `mae`;
- float32 and float64; and
- `find_force_mag` present and absent.
It verifies a finite zero total loss in all eight combinations, zero
magnetic metrics for a present-but-empty label, and the existing NaN
display behavior for an absent label. A separate pt_expt test exercises
the same shared dpmodel implementation through the Torch Array API
namespace.
On the previous implementation, all eight backend-neutral cases fail
with NaN. With the fix, the combined new and related targeted tests
pass.
## Validation
- old backend-neutral regression: 8 failed with NaN as expected
- fixed NumPy and pt_expt targeted tests: 20 passed
- `array_api_strict` MSE/MAE empty-mask scenarios passed
- JAX was not installed locally; the denominator expression was kept
free of Python data-dependent branching for JIT compatibility
- a broader consistent-loss run produced 35 passed / 45 skipped plus 2
unrelated pre-existing MAE parity failures because the shared editable
native-PT install came from another worktree; neither failure exercises
the empty-mask path
- `ruff format --check .`
- `ruff check .`
- `git diff --check`
Closes #5637.
Coding agent: Codex
Codex version: codex-cli 0.144.1
Model: gpt-5.6-sol
Reasoning effort: xhigh
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Fixed magnetic-force loss calculations when no magnetic atoms are
present.
- Prevented NaN or invalid loss values in both MSE and MAE modes.
- Ensured magnetic-force metrics and overall loss report finite zero
values for empty magnetic masks.
- Aligned results across supported computational backends.
- **Tests**
- Added regression coverage across supported precisions and loss
configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: njzjz-bot <njzjz-bot@users.noreply.github.com>
Co-authored-by: njzjz-bot <njzjz.bot@gmail.com>1 parent 4a0b95d commit f7d8a53
5 files changed
Lines changed: 167 additions & 12 deletions
File tree
- deepmd
- dpmodel/loss
- pt/loss
- source/tests
- common/dpmodel
- consistent/loss
- pt_expt/loss
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
284 | 284 | | |
285 | 285 | | |
286 | 286 | | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
287 | 291 | | |
288 | | - | |
| 292 | + | |
289 | 293 | | |
290 | 294 | | |
291 | 295 | | |
292 | 296 | | |
293 | 297 | | |
294 | | - | |
| 298 | + | |
295 | 299 | | |
296 | 300 | | |
297 | 301 | | |
298 | 302 | | |
299 | 303 | | |
300 | 304 | | |
301 | 305 | | |
302 | | - | |
| 306 | + | |
303 | 307 | | |
304 | 308 | | |
305 | 309 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
368 | 368 | | |
369 | 369 | | |
370 | 370 | | |
371 | | - | |
372 | | - | |
373 | | - | |
374 | | - | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
375 | 374 | | |
376 | 375 | | |
377 | 376 | | |
378 | 377 | | |
379 | | - | |
| 378 | + | |
380 | 379 | | |
381 | 380 | | |
382 | 381 | | |
| |||
385 | 384 | | |
386 | 385 | | |
387 | 386 | | |
388 | | - | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
389 | 390 | | |
390 | 391 | | |
391 | 392 | | |
392 | | - | |
393 | | - | |
394 | | - | |
| 393 | + | |
395 | 394 | | |
396 | 395 | | |
397 | 396 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
229 | 229 | | |
230 | 230 | | |
231 | 231 | | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
232 | 284 | | |
233 | 285 | | |
234 | 286 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
0 commit comments