You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PyBNF's refine = 1 post-fit polish currently offers exactly one local method — Nelder–Mead Simplex (_refine_best_fit in pybnf/pybnf.py → SimplexAlgorithm). Add Powell's method as a second, user-selectable refiner that complements (does not replace) Simplex.
Why Powell (and why these constraints)
The refiner's role is local polish from an already-good point found by the global search. PyBNF objectives are typically black-box (simulator outputs, no analytic gradient) and often noisy (SSA / replicate-averaged), which rules out gradient local-optimizers (BFGS/L-BFGS) unless finite-differenced — fragile under simulation noise. Powell's method is derivative-free (conjugate-direction) like Nelder–Mead, fits the same black-box/noisy regime, and frequently converges better on smooth-ish objectives. Both are available via scipy.optimize.minimize(method='Powell'|'Nelder-Mead').
(Levenberg–Marquardt was considered as a third option — highest value for least-squares objectives — but it is gradient-based and objective-restricted to sum-of-squares (chi_sq/sos); meaningless for kl/neg_bin/likelihood objfuncs. Out of scope here; revisit separately if requested.)
Likely surface: a refine_method = sim | powell config key (default sim, backward-compatible). The selected refiner's config schema is what _build_config conditionally pulls in (generalizing today's hardwired SimplexConfig overlay to "the chosen refiner's schema") — at which point the registry cross-dependency mechanism (ADR-0006 considered-option (b)) may finally be worth building.
Keep .conf backward compatibility: refine = 1 with no refine_method ⇒ Simplex, byte-identical to today.
Scope / acceptance (rough — to be planned in its own session)
Summary
PyBNF's
refine = 1post-fit polish currently offers exactly one local method — Nelder–Mead Simplex (_refine_best_fitinpybnf/pybnf.py→SimplexAlgorithm). Add Powell's method as a second, user-selectable refiner that complements (does not replace) Simplex.Why Powell (and why these constraints)
The refiner's role is local polish from an already-good point found by the global search. PyBNF objectives are typically black-box (simulator outputs, no analytic gradient) and often noisy (SSA / replicate-averaged), which rules out gradient local-optimizers (BFGS/L-BFGS) unless finite-differenced — fragile under simulation noise. Powell's method is derivative-free (conjugate-direction) like Nelder–Mead, fits the same black-box/noisy regime, and frequently converges better on smooth-ish objectives. Both are available via
scipy.optimize.minimize(method='Powell'|'Nelder-Mead').(Levenberg–Marquardt was considered as a third option — highest value for least-squares objectives — but it is gradient-based and objective-restricted to sum-of-squares (
chi_sq/sos); meaningless forkl/neg_bin/likelihood objfuncs. Out of scope here; revisit separately if requested.)Design notes (from the #399 grill, 2026-06-04)
_REFINER_SCHEMA/_refine_pulls_in(d)seam inconfig.pyso adding a refiner is a localized change, not a rewrite.refine_method = sim | powellconfig key (defaultsim, backward-compatible). The selected refiner's config schema is what_build_configconditionally pulls in (generalizing today's hardwiredSimplexConfigoverlay to "the chosen refiner's schema") — at which point the registry cross-dependency mechanism (ADR-0006 considered-option (b)) may finally be worth building..confbackward compatibility:refine = 1with norefine_method⇒ Simplex, byte-identical to today.Scope / acceptance (rough — to be planned in its own session)
refine_methodconfig key + grammar/schema; defaultsim._refine_best_fit(likely a thinscipy.optimize.minimize(method='Powell')driver respecting bounds / parameter scales)._REFINER_SCHEMAseam from M2.1 Stage (c): narrow each fit_type's effective config to its own keys (per-method narrowing, ADR-0006 #1) #399 to dispatch onrefine_method.refine_method = powellon a non-sim fit over an AnalyticalModel target reaches the mode (mirrors the Simplex refine test).Depends on #399 (the refiner seam). Separate session. Any new runtime dep → add to
.github/actions/setup-pybnf(scipy is already a dep).🤖 Filed with Claude Code