8000
Skip to content

fix: neural optimizer reports actual convergence instead of hardcoded 0L (#107) - #115

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/issue-107
Aug 10, 2026
Merged

fix: neural optimizer reports actual convergence instead of hardcoded 0L (#107)#115
github-actions[bot] merged 1 commit into
mainfrom
fix/issue-107

Conversation

@seansorek
Copy link
Copy Markdown
Owner

Closes #107

Problem

run_neural_optimization()'s Adam training loop already tracks stall/patience and early-stops on plateau, but the dict it returned never included a converged flag. .optimize_neural() in R/optimizer.R then hardcoded convergence = 0L, so any mlp/conv/spline_gam/irl fit that ran out of its epoch budget while still improving was reported as converged — indistinguishable from an actual plateau. This is the same failure mode #92 fixed on the parametric (Adam) path.

Fix

  • run_neural_optimization() now returns "converged": stall >= patience (mirrors the parametric Adam branch).
  • .optimize_neural() now maps convergence <- if (isTRUE(result$converged)) 0L else 1L instead of the literal 0L.

Tests

  • Python: added test_not_converged_when_epochs_exhausted_while_improving to test_neural_optimize.py, and added "converged" to the expected-keys set in test_mlp_returns_expected_keys; extended test_mlp_early_stopping to assert converged is True.
  • R: added two testthat cases in test-optimizer.R mocking ds_jax_neural_optimize() to return converged = TRUE / converged = FALSE and asserting .optimize_neural()'s convergence field maps to 0L / 1L respectively.

Ran locally: inst/python/diffiscape_jax/tests/test_neural_optimize.py — 11 passed. tests/testthat/test-optimizer.R with NOT_CRAN=true — 70 passed, 0 failed.

@codecov
codecov Bot commented Aug 7, 2026
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.72%. Comparing base (48b3648) to head (000cd6c).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
Flag Coverage Δ
jax 89.53% <100.00%> (+0.48%) ⬆️
python 57.50% <ø> (ø)
r 76.73% <100.00%> (+5.84%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
R package 76.73% <88.00%> (+5.84%) ⬆️
Python torch backend 57.50% <ø> (ø)
Python JAX backend 89.53% <96.58%> (+0.48%) ⬆️
Files with missing lines Coverage Δ
R/optimizer.R 91.85% <100.00%> (+33.42%) ⬆️
inst/python/diffiscape_jax/optimize.py 96.32% <ø> (-0.11%) ⬇️
...ython/diffiscape_jax/tests/test_neural_optimize.py 100.00% <100.00%> (ø)

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@seansorek seansorek left a comment
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fix is correct. Reviewed all changed files (Python optimizer, Python tests, R optimizer, R tests).

Python (optimize.py):
"converged": stall >= patience is the right expression. The training loop exits when stall >= patience (plateau detected → early stop) or when the epoch budget is exhausted. After the loop, stall >= patience is True only in the first case. This mirrors the parametric Adam branch that already had this logic (per the PR description referencing #92).

R (optimizer.R):
convergence <- if (isTRUE(result$converged)) 0L else 1L follows R's optim() convention (0 = success, 1 = failure/non-convergence). The isTRUE() guard handles NULL safely if Python ever returns a result without the converged key.

Tests:

  • Python: test_not_converged_when_epochs_exhausted_while_improving uses n_epochs=5, patience=1000 — the budget runs out before any plateau, so stall < patience at loop exit → converged=False. ✓
  • Python: existing test_mlp_early_stopping now also asserts converged=True (zero LR → no progress → stall reaches patience). ✓
  • Python: test_mlp_returns_expected_keys updated to expect "converged" in the result dict. ✓
  • R: Two mocked tests cover the True0L and False1L mappings cleanly.

CI: all R-CMD-check and Python CI checks passing. ✅

LGTM.

@github-actions
github-actions Bot merged commit c5dbb7d into main Aug 10, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Neural gradient path always reports convergence = 0L, even when it hit the epoch cap

1 participant

0