PyAutoFit: add n_effective to DynestyDynamic, and fix truncated dynamic runs under a finite iterations_per_full_update #33
Unanswered
samlange04
asked this question in
Ideas & Proposals
Replies: 1 comment
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Package: PyAutoFit (
autofit), currentmain(710f4b343), dynesty 2.1.5. Branch: https://github.com/samlange04/PyAutoFit/tree/feature/dynesty-dynamic-n-effectiveProposal
Expose dynesty's
n_effectiveonDynestyDynamic, and fix a bug it exposed: with a finiteiterations_per_full_update,DynestyDynamicreturns a truncated baseline run instead of a complete dynamic run.1.
n_effectiveonDynestyDynamicdynesty's dynamic sampler keeps adding batches of live points until its stopping function judges the estimated effective sample size (ESS) to have reached
n_effective. The default in dynesty 2.x ismax(10000, ndim ** 2). PyAutoFit currently has no way to set it, so the only ways to make a dynamic run cheaper aremaxcall/maxbatch, which cut the run instead of lowering its target.For the lens models I run (10 to 20 parameters) a target of a few thousand effective samples gives the same posterior at a fraction of the batch phase. The branch adds
n_effective: Optional[int] = NonetoDynestyDynamic.__init__, forwarded torun_nestedonly when notNone, so the default behaviour is unchanged. It is not an identifier field. Following #1202, there is no yaml default; it is an explicit__init__default like the other dynamic arguments.DynestyStaticis deliberately left alone: dynesty acceptsn_effectiveonNestedSampler.run_nestedbut emits a DeprecationWarning saying it will be removed.2. Bug: a finite
iterations_per_full_updatetruncates a dynamic runSetting
iterations_per_full_update(so output is written on the fly) makesDynestyDynamicstop after a cut-short baseline run, with an ESS of about 1 and no batches. Three things in dynesty combine:DynamicNestedSampler.run_nestedcomparesmaxcallwith its cumulative call countself.ncall, carried over between calls. The static sampler's counter resets per call, which is whatrun_search_internalassumes. So the second chunk is handed a budget it has already spent and returns without sampling; the "no new calls" criterion then finishes the search.maxcallcut short cannot be continued: the nextrun_nestedcall restarts it (sample_initialcallsreset()), andresume=Truerefuses because the previous call ended inRUN_DONE.results.ncallundercounts the sampler's ownncall(it omits each batch's live-point initialisation), so comparing it against a cumulative budget misjudges whether a chunk converged.Proposed fix
Per-sampler hooks on
AbstractDynesty(total_calls_from,maxcall_from,chunk_kwargs,chunk_is_finished) whose defaults reproduce the current static behaviour exactly.DynestyDynamicoverrides them: the first chunk of a chunked run completes the whole baseline (maxbatch=0, no budget, since it cannot be resumed), then the batch phase is chunked by cumulative budgettotal_calls + iterations_per_full_update, and a batch chunk counts as finished only if it stopped strictly inside its budget. Intermediate output for dynamic runs therefore starts after the baseline.Measured on a 3-parameter Gaussian with
iterations_per_full_update=1500: baseline + 3 batch chunks + 1 no-op chunk, ESS 3587 against 3944 for the same run in a single chunk, same logZ and parameters.DynestyStaticverified unaffected (4 chunks of ~1500 calls, same result as before).Happy to split the two into separate PRs if preferred, but the bug only matters for users who would also want
n_effective(anyone trying to make a dynamic run both observable and affordable), and the fix is what makesn_effectivetestable with on-the-fly output.All reactions