Skip to content

feat: optimization-course solver suite (8 methods), uncertainty helpers, noise-robustness example - #127

Merged
Radiationsafety merged 6 commits into
mainfrom
feature/optimization-course-methods
Sep 18, 2026
Merged

Radiationsafety merged 6 commits into
mainfrom
feature/optimization-course-methods

Conversation

@Radiationsafety

Copy link
Copy Markdown
Owner

Summary

Ports a suite of classic and modern numerical-optimization methods (from the MFTI "Optimization Methods" course) to the Bonner-sphere unfolding problem, adds uncertainty/diagnostics helpers, and ships three executed example notebooks including a noise-robustness study.

Branch: feature/optimization-course-methods (2 commits, 26 files, +8189/-159)

New solvers (Detector.unfold_*)

Method Key idea Regularization supported
unfold_pgd projected gradient descent + backtracking L2
unfold_frank_wolfe conditional gradient, simplex-constrained implicit (flux total)
unfold_mirror_descent entropic / L2 / p-norm mirror maps implicit (entropy)
unfold_admm L1 + TV + nonnegativity splitting L1 + TV
unfold_lbfgsb bound-constrained quasi-Newton L2
unfold_coordinate_descent coordinate-wise nonneg minimization L2
unfold_subgradient diminishing-step subgradient L1 / L2
unfold_extragradient Korpelevich saddle-point formulation, robust radius tuning L2

Helper modules

  • _line_search.py — golden-section / dichotomy / Brent 1-D search (used by Morozov λ-tuning)
  • regularization_1d.pyselect_regularization_1d with GCV / discrepancy / predictive criteria + Hutchinson trace estimation
  • _dual_diagnostics.pynnls_duality_gap, nnls_kkt_residuals (solution certificates)
  • _montecarlo.pymonte_carlo_uncertainty with variance reduction (antithetic / control variates)
  • _matrix_utils.pyestimate_total_fluence (data-driven default for FW / entropy MD)

Examples (all executed, 0 errors)

  • 52-optimization-course-methods — solver comparison, duality-gap/KKT certificates, Morozov discrepancy λ-tuning (λ* ≈ 3.7e-3)
  • 53-montecarlo-20-spectra — 20 MC spectra per solver, spaghetti/band plots, variance-reduction factors
  • 54-noise-robustness — noise sweep 2/5/10/20 % × 20 paired Gaussian realizations shared across 11 solvers (8 new + MLEM/Landweber/CVXPY baselines); bias-variance decomposition of spectrum RMSE, degradation curves, dose-rate errors, robust-radius tuning for extragradient. Main finding: unregularized schemes (MLEM/Landweber/FW) are variance-dominated; L1+TV ADMM and robust extragradient give markedly flatter error curves at the cost of a small systematic bias.

Fixed

Default total-fluence estimate in unfold_frank_wolfe and entropy unfold_mirror_descent: the previous uniform-response heuristic mean(b)/mean(A)·n_bins over-estimated the simplex level by ~100x on log-spaced GSF grids (215 vs 2.25 for ISO Cf-252), pinning both solvers to a wrong scale. The default is now a data-driven NNLS estimate via estimate_total_fluence; the old heuristic remains as an explicit fallback. Note: this changes results of default calls (previously mis-scaled) — flagged for review.

Testing

  • tests/test_optimization_methods.py: 75 tests, all green
  • ruff clean; notebooks re-executed with the fluence fix
  • No regression introduced vs baseline c5df2ff^ (known pre-existing skips are optional-dependency related)

New unfolding methods ported from the MIPT "Optimization Methods in
Machine Learning" course (fall 2025):

- projected gradient descent (unfold_pgd): orthant/box/simplex
  projections, Armijo backtracking (lecture 9 / HW 14)
- Frank-Wolfe with away steps and exact line search (lecture 9 / HW 14)
- mirror descent with entropy/l2/pnorm mirror maps (lecture 10 / HW 16)
- consensus ADMM with L1 + total variation + nonnegativity, adaptive
  rho (lecture 11 / HW 18)
- L-BFGS-B quasi-Newton with box bounds (lecture 7 / HW 10)
- cyclic/randomized NNLS coordinate descent (lecture 15)
- subgradient method with Polyak/diminishing steps (lecture 8 / HW 12)
- extragradient on the robust saddle formulation (lecture 13 / HW 20)

Supporting machinery:
- 1-D line search: golden section / dichotomy / Brent (_line_search)
- regularization selection by 1-D optimization (GCV / discrepancy /
  predictive criteria, Hutchinson trace estimate) - regularization_1d
- NNLS duality-gap and KKT diagnostics (_dual_diagnostics)
- Monte-Carlo variance reduction (antithetic + control variate,
  lecture 14) in _montecarlo, wired through run_unfolding

Examples:
- 52-optimization-course-methods.ipynb: solver comparison, duality
  certificates, regularization selection
- 53-montecarlo-20-spectra.ipynb: 20 Monte-Carlo spectra per solver,
  variance-reduction comparison, doserate distribution

75 new tests in tests/test_optimization_methods.py (all green).
New example: examples/54-noise-robustness.ipynb
- noise sweep delta in {2, 5, 10, 20}% x 20 paired Gaussian realizations
  shared by all solvers; 8 new solvers + MLEM/Landweber/CVXPY baselines
- bias-variance decomposition of the spectrum RMSE (normalized by the
  solver's own noiseless solution) and the dose-rate error
- RMSE/bias/spread tables, degradation curves, spaghetti plots at 10%
- robust-radius tuning for the extragradient saddle formulation:
  matching delta_rob to the true noise level minimizes the error

Fixed: default total-fluence estimate in Frank-Wolfe and entropy mirror
descent. The previous uniform-response heuristic mean(b)/mean(A)*n_bins
over-estimated the simplex level by ~100x on log-spaced GSF grids (215 vs
2.25 for ISO Cf-252), pinning both solvers to a wrong scale (dose rate
off by 2x / 144x). The default is now a data-driven NNLS estimate
(estimate_total_fluence helper in _matrix_utils); the old heuristic is
kept only as a fallback. Notebooks 52/53 re-executed with the fix.

75 optimization-method tests green; ruff clean.
@netlify

netlify Bot commented Sep 18, 2026

Copy link
Copy Markdown

Deploy Preview for bssunfold ready!

Name Link
🔨 Latest commit 2157779
🔍 Latest deploy log https://app.netlify.com/projects/bssunfold/deploys/6aace800dad41e000805a708
😎 Deploy Preview https://deploy-preview-127--bssunfold.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codacy-production

codacy-production Bot commented Sep 18, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 high · 1 medium

Alerts:
⚠ 2 issues (≤ 0 issues of at least minor severity)

Results:
2 new issues

Category Results
ErrorProne 1 high
Security 1 medium

View in Codacy

🟢 Metrics 229 complexity · 39 duplication

Metric Results
Complexity 229
Duplication 39

View in Codacy

🟢 Coverage 92.81% diff coverage · +0.06% coverage variation

Metric Results
Coverage variation +0.06% coverage variation (-1.00%)
Diff coverage 92.81% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (cc8d149) 13276 12219 92.04%
Head commit (2157779) 14121 (+845) 13005 (+786) 92.10% (+0.06%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#127) 848 787 92.81%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Konstantin Chizhov and others added 4 commits September 18, 2026 10:14
Resolve conflicts with the CUQIpy Bayesian unfolding integration (main):
- detector.py: keep all 8 optimization-course solvers + unfold_cuqi
- README.md: unified method table (cuqi = #80, optimization-course = #81-88,
  88 methods total) and merged feature bullets
- CHANGELOG.md: renumber our solver-comparison example 52 -> 55 (main's
  52-cuqi-iaea took the number)
- uv.lock: regenerated from the merged pyproject (adds cuqipy git source)

Post-merge checks: py_compile OK, 75 optimization tests passed,
ruff src/ tests/ clean (CI gate), smoke: pgd/FW sums and dose rates sane.
@Radiationsafety
Radiationsafety merged commit b997655 into main Sep 18, 2026
27 of 30 checks passed
@Radiationsafety
Radiationsafety deleted the feature/optimization-course-methods branch September 18, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant