Repository navigation
refactor: samples adapter and Checkpointer/resume split (search-extensibility A3, stacked on A2) - #1680
Merged
Conversation
…and SMC Stored backend internals from small real fits, with the samples.csv and samples_info.json that samples_via_internal_from produces from them on main (0dbf258). The A3 samples adapter must reproduce samples.csv byte for byte. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…once Add autofit/non_linear/samples/adapter.py: RawSamples(parameters, log_likelihood | log_posterior, weights, info) and samples_from_raw, the one place for the log prior (log_prior_list_from), LL from a log posterior, the #1628 length checks, recognition of the -1e99/-1e30/-inf sentinels and time / class_path injection. Representations and weights are passed through as the backend stores them; nothing is renormalised (D13). NonLinearSearch.samples_via_internal_from is now implemented once on top of a per-search raw_samples_from, with samples_cls declared on the family bases, SMC and NSS. Every search (Emcee, Zeus, BlackJAXNUTS, SMC, Dynesty, Nautilus, NSS, BFGS/LBFGS, Drawer, MultiStart) is ported; the golden samples.csv fixtures stay byte-identical. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One burn-in/thinning implementation for the chain searches: ChainPosterior wraps emcee/zeus get_chain/get_log_prob (from_sampler) or stored chains (from_arrays, emcee's slicing, draw- or chain-major), requests the log values with the same discard/thin as the draws (#1628) and falls back to the whole chain when burn-in removal leaves nothing. Emcee gains Zeus's empty-chain fallback. Adds adapter and ChainPosterior unit tests; goldens unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
autofit/non_linear/checkpoint.py adds the three archive strategies chosen per search by the checkpointer class attribute (the A1 capability slot): DillCheckpointer (default; Nautilus strips its pools), PickleCheckpointer (BlackJAXNUTS, SMC) and NativeFileCheckpointer(filename, loader) (emcee HDF, dynesty savestate, nautilus hdf5, NSS pkl), each with save/load/exists/ finalize/discard and retain_after_completion. Writes are atomic; NullPaths and DatabasePaths store nothing (paths.stores_search_internal). resume_state is separate from the archive: Dynesty (savestate.save), Nautilus (checkpoint.hdf5), NSS (nss_checkpoint.pkl), MultiStart and Emcee (their archive file). NUTS and SMC have none and stay resumable=False. NonLinearSearch.output_search_internal finalizes the archive and discards a completed run's resume state; post_fit_output keeps search_internal/ when the archive retains itself or output.search_internal is on. Deletes the NUTS/SMC duplicate pickle code, the Emcee no-op and the Dynesty / Nautilus output_search_internal overrides. A completed Dynesty folder now loads its samples from the archive (search_internal.dill), falling back to the savestate only for an unfinished run, so the two A0a(ii) Dynesty strict xfails are deleted and pass. The checkpointer joins the capability manifest and the generated search docs (Archive column). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nstance Delete the three conf.instance["output"]["search_internal"] = True mutations (emcee/search.py, nuts/search.py, smc/search.py). They existed so a completed fit kept the archive its samples are rebuilt from, and they leaked to every later search in the process. The archives now declare retain_after_completion instead (previous commit), so the three CONFIG_MUTATION_XFAIL strict xfails are deleted and pass. Adds an end-to-end check that, with output.search_internal off, a completed Emcee fit keeps its HDF archive while a following Drawer fit drops its dill. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n; D5 design note DirectoryPaths.load_search_internal no longer knows any backend format: it loads through the owning search's checkpointer (paths.search), so Result.search_internal and samples_via_internal_from read emcee's HDF backend, the NUTS/SMC pickle or the dill through one path, and the NUTS/SMC load_search_internal overrides go. Format-aware legacy detection is kept, not replaced by a dill-only fallback: the inline emcee probe (directory.py 238-246) moved to checkpoint.load_legacy_search_internal (hdf, then pickle, then dill), used for a folder opened without its search and when a checkpointer's own file is absent, so pre-A3 outputs still load. Tests cover directory (injected and legacy), emcee HDF, zipped, database, null and summary-only outputs. docs/design/checkpointing.md records the archive/resume split, atomic writes, corruption handling and retention precedence (D5). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…kpointer branch Stacks phase A3 on phase A2 so the chain merges A2 -> A3 -> A3b. Conflicts resolved: - abstract_search.py: keep A2's start_points, _pools and bridge; keep A3's samples_via_internal_from (implemented once over the adapter), raw_samples_from and load_search_internal; one samples_cls and one base raw_samples_from (A2's duplicates dropped); A2's info_from hook kept. - drawer/nautilus search.py: stay on run(ctx); one raw_samples_from each, A3's (adapter RawSamples with label), with info routed through A2's info_from hook. - emcee/nautilus/smc search.py imports: A3's checkpoint imports plus A2's removal of the Fitness import and SMC's jax_objective import. - fit_context.py: A2's provisional RawSamples/samples_from_raw deleted; the bridge re-exports A3's samples/adapter.py. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tegies
ctx.checkpointer is the search's archive strategy (NonLinearSearch.checkpointer)
and ctx.resume its resume_state when the interrupted run's file exists, else
None; the Checkpointer handle rather than the loaded state, since a backend may
reopen its own native file (Nautilus). The base info_from returns {} (the
frozen contract's dict), which the adapter's RawSamples.info expects.
run_ctx.md Revision 1 now names the wired members and the adapter.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The golden samples.csv comparison was byte for byte, but NumPy dispatches its vectorised exp/log loops per CPU (AVX2 vs AVX-512), so the same NumPy on a GitHub runner reproduced the dynesty and nautilus weights (and two nautilus log priors) 1 ULP away from the AVX2-generated fixtures (max relative difference 2.2e-16). Compare the header and row count exactly and every value with rtol=1e-12, atol=0 instead: still far below any renormalisation, reordering or dropped row (decision D13). samples_info is compared exactly except for the ESS / bulk / tail ESS / R-hat diagnostics that blackjax.diagnostics computes (blackjax 1.7 changed the rank-normalised estimators); for those only the shape is pinned. Add docs/design/checkpointing.md to the Searches toctree (the new toc.not_included Sphinx warning that pushed the count to 31 > 30). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from
feature/search-ext-a2-objective-bridge
to
main
October 8, 2026 19:53
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase A3 of the search-extensibility epic (PyAutoFit#1677), stacked on #1679 (A2): this PR's base is
feature/search-ext-a2-objective-bridge; merge #1679 first and GitHub retargets this one tomain. Six A3 commits plus a merge of A2 and one reconciliation commit.Phase A3 builds on A2's
run(ctx)bridge. It replaces A2's provisional samples types with the single samples adapter (autofit/non_linear/samples/adapter.py:RawSamples,samples_from_raw, one log-prior helper, sentinel recognition without rewriting,ChainPosterior.thinshared by Emcee, Zeus and NUTS with the empty-chain fallback). Every search implements oneraw_samples_from;samples_via_internal_fromis written once onNonLinearSearch. Byte-identical goldensamples.csvfixtures (generated on main, their own first commit) cover Emcee, Dynesty, Nautilus, NUTS and SMC. A search's archive (checkpointer:DillCheckpointer,PickleCheckpointerfor NUTS/SMC,NativeFileCheckpointerfor emcee HDF / dynesty savestate / nautilus hdf5 / NSS pkl; atomic writes,retain_after_completion) is separated from itsresume_state, with corruption and retention precedence indocs/design/checkpointing.md(D5); a completed Dynesty folder keeps what it needs to reload. Constructing Emcee, BlackJAXNUTS or SMC no longer mutatesconf.instance. Pre-A3 output folders load through format-aware legacy detection (the HDF probe moved, not replaced); loading is injected for directory, database (no-op), null, zipped and summary-only paths.FitContext.checkpointerandFitContext.resumeare now wired from these strategies anddocs/design/run_ctx.mdRevision 1 says so.--autorun at effective level safe; tier judge → human/prm.Strict xfails: 9 → 4. Deleted (now passing): the three
CONFIG_MUTATION_XFAIL(Emcee, BlackJAXNUTS, SMC) and the two DynestyDISK_INTERNAL_XFAIL. The four import-timeAutoCorrelationsSettingsxfails are A4's and stay strict. Golden identifiers unchanged.API Changes
Added
autofit.non_linear.samples.adapter(RawSampleswith optionallog_prior/samples_kwargs/label,samples_from_raw,log_prior_list_from,is_invalid/invalid_count,INVALID_SENTINELS,ChainPosterior),autofit.non_linear.checkpoint(DillCheckpointer(strip_attributes=),PickleCheckpointer,NativeFileCheckpointer(filename, loader)withsave/load/exists/finalize/discard,retain_after_completion,path;load_legacy_search_internal,checkpointer_kind),NonLinearSearch.{checkpointer,resume_state,samples_cls,raw_samples_from,load_search_internal,retains_search_internal}, thecheckpointermanifest capability (dill|pickle|native:<file>; schema-compatible addition; generated docs gain an "Archive" column),paths.stores_search_internal(False forNullPaths/DatabasePaths). Changed: concrete searches implementraw_samples_frominstead ofsamples_via_internal_from(direct overrides still work); Emcee/NUTS/SMC keep their archive regardless of config; a completed Dynesty folder loads from dill, savestate only for an unfinished run; NUTS/SMC write no pickle onDatabasePaths; Emcee gains the empty-chain fallback and Zeus applies it in test mode; Zeus computes autocorrelations once per conversion; Nautilus's resume loader raisesNotImplementedError(nautilus reopens its own file); baseinfo_fromreturns{}.resumablevalues unchanged (Zeus stays False; resume not proven).See full details below.
Test Plan
pytest test_autofit -xon the integrated head: 3531 passed, 1 skipped, 4 xfailedtest_adapter,test_checkpoint×2,test_search_internal_loading,test_fit_context, plus A2's probe/timing/make_fitness/BFGS tests: 117 passedtest_autofit/non_linear: 1253 passed, 136 skipped, 2 xfailed;import autofitloads no optional backendaf.Emcee(),af.BlackJAXNUTS(),af.SMC()leaveconf.instance["output"]["search_internal"]unchanged (re-run by the reviewer){Emcee,Zeus,DynestyStatic,Nautilus,Nautilus_jax,Drawer,SMC}.pyunder test mode exit 0 (BlackJAXNUTS and MultiStartResurrect fail their own asserts identically on main)Full API Changes (for automation & release notes)
Removed
RawSamples/samples_from_rawinsearch/fit_context.pyare re-exported from the adapter)Added
NonLinearSearchhooks,checkpointercapability,paths.stores_search_internal)docs/design/checkpointing.mdMigration
samples_via_internal_from(model, search_internal)→ After: implementraw_samples_from(model, internal) -> RawSamples; the base converts onceconf.instance["output"]["search_internal"] = Trueset by constructing Emcee/NUTS/SMC → After: never; those searches retain their archive viacheckpointer.retain_after_completionsavestate.save→ After: from the dill archive once complete; savestate only resumes an unfinished runValidation checklist (--auto run — plan was not pre-approved)
release validation incomplete: no rehearsal for current sourceGenerated by the PyAutoLabs agent workflow.
🤖 Generated with Claude Code