Overview
Phase 2 of 2 of the multi-start gradient auto-convergence work (phase 1 shipped: PyAutoFit#1407 + autofit_workspace_test#60). The results contract the author flagged as "must ship together": make the auto-convergence outcome inspectable and keep the aggregator robust to the variable-length runs early-stopping now produces. Single cohesive PR (the generic phaser over-split it; the contract is one deliverable).
Plan
- Add
converged: bool + stop_reason ('converged'|'max_steps') + the convergence settings to samples_info (the loop already persists stop_reason in search_internal).
- Surface the
fom_history convergence trace in the standard result artifacts so users can verify the plateau — carry fom_history into samples_info and add an MLE plotter that draws the global-best figure-of-merit vs step.
- Harden aggregator / samples_summary for variable-length runs (different
total_steps across datasets); guard the known zero-weight / NaN diagnostic-row IndexError edge (Fit#275 lineage; the multi-start searches write zero-weight rows for the non-best starts).
- Tests: numpy-only unit tests (samples_info keys, aggregator variable-length + zero-weight edge); extend the JAX validation to round-trip
converged/stop_reason/trace through the results DB.
Detailed implementation plan
Affected Repositories
- PyAutoFit (primary)
- autofit_workspace_test (JAX validation extension)
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoFit |
main |
clean |
| ./autofit_workspace_test |
main |
6 regenerated dataset artifacts (inconsequential) |
Suggested branch: feature/multistart-gradient-convergence-results-phase-2
Implementation Steps
search.py samples_via_internal_from (~L507, samples_info): read stop_reason = search_internal.get("stop_reason"); add converged (stop_reason == "converged"), stop_reason, the convergence settings dict (check_for_convergence/window/rtol/atol/min_steps), and the fom_history trace (list) to samples_info.
autofit/non_linear/plot/mle_plotters.py: add figure_of_merit_vs_iteration(samples, ...) that reads samples.samples_info.get("fom_history") and plots best figure-of-merit (or -0.5*fom = log-posterior) vs step, marking the plateau; no-op when absent (LBFGS/Drawer have no fom_history).
autofit/non_linear/search/mle/abstract_mle.py plot_results (~L19): wire the new plotter, gated by should_plot("figure_of_merit_vs_iteration"); guard on fom_history presence so generic MLE searches are unaffected.
autofit/aggregator/search_output.py (~L320, except IndexError): confirm/extend the zero-weight/NaN diagnostic-row guard for variable-length multi-start results; add a regression test rather than widening a silent guard.
- Config: mirror any new
should_plot key into packaged autofit/config/visualize/plots.yaml (and note for downstream workspaces).
Key Files
autofit/non_linear/search/mle/multi_start_gradient/search.py — samples_info
autofit/non_linear/plot/mle_plotters.py — new convergence-trace plotter
autofit/non_linear/search/mle/abstract_mle.py — plot_results wiring
autofit/aggregator/search_output.py — zero-weight/NaN IndexError guard
test_autofit/non_linear/search/mle/test_multi_start_gradient.py + test_autofit/aggregator/ — unit tests
autofit_workspace_test/scripts/jax_assertions/multi_start_gradient_auto_convergence.py — extend round-trip check
Testing
python -m pytest test_autofit/ (numpy-only unit tests for samples_info + aggregator)
- autofit_workspace_test JAX validation extension (converged/stop_reason/trace round-trip)
Original Prompt
Click to expand starting prompt
Auto-convergence for the multi-start gradient searches — Phase 2: results-contract + aggregator hardening
Type: feature
Target: PyAutoFit
Repos:
- PyAutoFit
- autofit_workspace_test
Difficulty: medium
Autonomy: supervised
Priority: normal
Status: formalised
Phase 2 of 2 — the results contract the prompt author flagged as "part 2, must
ship together" with the convergence loop
(multistart_gradient_auto_convergence_phase_1.md). Do not issue this until
phase 1 nears shipping (no bulk-issuing a prompt series). Depends on the
phase-1 stop_reason / converged locals and the MultiStartGradientConvergence
settings object landing first.
Goal
Make the auto-convergence outcome inspectable and keep the aggregator robust to
the variable-length runs early stopping now produces.
Scope
- samples_info: add
converged: bool + stop_reason ('converged' |
'max_steps') + the convergence settings to samples_info
(search.py:507, samples_via_internal_from).
- Result artifacts: surface the
fom_history convergence trace in the
standard result artifacts so users can visually verify the plateau (the loop
already tracks fom_history).
- Aggregator / samples_summary coverage: extend test coverage for
variable-length runs (different total_steps across datasets). Guard the known
zero-weight / NaN diagnostic-row aggregator IndexError edge — see
PyAutoFit#275 (_quick_fit consolidation, samples-weight-threshold) — since
the multi-start searches write zero-weight diagnostic rows for the non-best
starts.
Testing
- Unit tests (numpy-only): samples_info carries the new keys; aggregator
handles variable-length runs + the zero-weight diagnostic-row edge without
IndexError.
- JAX validation (autofit_workspace_test): aggregate a small set of MGE
early-stopped runs and confirm the convergence trace + converged/stop_reason
round-trip through the results database.
Deliverable
Library PR (PyAutoFit) + aggregator test coverage. Workspace-facing artifact
example follows once the API lands.
Overview
Phase 2 of 2 of the multi-start gradient auto-convergence work (phase 1 shipped: PyAutoFit#1407 + autofit_workspace_test#60). The results contract the author flagged as "must ship together": make the auto-convergence outcome inspectable and keep the aggregator robust to the variable-length runs early-stopping now produces. Single cohesive PR (the generic phaser over-split it; the contract is one deliverable).
Plan
converged: bool+stop_reason('converged'|'max_steps') + the convergence settings tosamples_info(the loop already persistsstop_reasoninsearch_internal).fom_historyconvergence trace in the standard result artifacts so users can verify the plateau — carryfom_historyintosamples_infoand add an MLE plotter that draws the global-best figure-of-merit vs step.total_stepsacross datasets); guard the known zero-weight / NaN diagnostic-rowIndexErroredge (Fit#275 lineage; the multi-start searches write zero-weight rows for the non-best starts).converged/stop_reason/trace through the results DB.Detailed implementation plan
Affected Repositories
Branch Survey
Suggested branch:
feature/multistart-gradient-convergence-results-phase-2Implementation Steps
search.pysamples_via_internal_from(~L507,samples_info): readstop_reason = search_internal.get("stop_reason"); addconverged(stop_reason == "converged"),stop_reason, the convergence settings dict (check_for_convergence/window/rtol/atol/min_steps), and thefom_historytrace (list) tosamples_info.autofit/non_linear/plot/mle_plotters.py: addfigure_of_merit_vs_iteration(samples, ...)that readssamples.samples_info.get("fom_history")and plots best figure-of-merit (or-0.5*fom= log-posterior) vs step, marking the plateau; no-op when absent (LBFGS/Drawer have nofom_history).autofit/non_linear/search/mle/abstract_mle.pyplot_results(~L19): wire the new plotter, gated byshould_plot("figure_of_merit_vs_iteration"); guard onfom_historypresence so generic MLE searches are unaffected.autofit/aggregator/search_output.py(~L320,except IndexError): confirm/extend the zero-weight/NaN diagnostic-row guard for variable-length multi-start results; add a regression test rather than widening a silent guard.should_plotkey into packagedautofit/config/visualize/plots.yaml(and note for downstream workspaces).Key Files
autofit/non_linear/search/mle/multi_start_gradient/search.py—samples_infoautofit/non_linear/plot/mle_plotters.py— new convergence-trace plotterautofit/non_linear/search/mle/abstract_mle.py—plot_resultswiringautofit/aggregator/search_output.py— zero-weight/NaNIndexErrorguardtest_autofit/non_linear/search/mle/test_multi_start_gradient.py+test_autofit/aggregator/— unit testsautofit_workspace_test/scripts/jax_assertions/multi_start_gradient_auto_convergence.py— extend round-trip checkTesting
python -m pytest test_autofit/(numpy-only unit tests for samples_info + aggregator)Original Prompt
Click to expand starting prompt
Auto-convergence for the multi-start gradient searches — Phase 2: results-contract + aggregator hardening
Type: feature
Target: PyAutoFit
Repos:
Difficulty: medium
Autonomy: supervised
Priority: normal
Status: formalised
Phase 2 of 2 — the results contract the prompt author flagged as "part 2, must
ship together" with the convergence loop
(
multistart_gradient_auto_convergence_phase_1.md). Do not issue this untilphase 1 nears shipping (no bulk-issuing a prompt series). Depends on the
phase-1
stop_reason/convergedlocals and theMultiStartGradientConvergencesettings object landing first.
Goal
Make the auto-convergence outcome inspectable and keep the aggregator robust to
the variable-length runs early stopping now produces.
Scope
converged: bool+stop_reason('converged'|'max_steps') + the convergence settings tosamples_info(
search.py:507,samples_via_internal_from).fom_historyconvergence trace in thestandard result artifacts so users can visually verify the plateau (the loop
already tracks
fom_history).variable-length runs (different
total_stepsacross datasets). Guard the knownzero-weight / NaN diagnostic-row aggregator
IndexErroredge — seePyAutoFit#275 (
_quick_fitconsolidation, samples-weight-threshold) — sincethe multi-start searches write zero-weight diagnostic rows for the non-best
starts.
Testing
handles variable-length runs + the zero-weight diagnostic-row edge without
IndexError.early-stopped runs and confirm the convergence trace +
converged/stop_reasonround-trip through the results database.
Deliverable
Library PR (PyAutoFit) + aggregator test coverage. Workspace-facing artifact
example follows once the API lands.