Skip to content

refactor: NSS onto Fitness, trace preflight and x64 check (search-extensibility A3b, stacked on A3) - #1681

Merged
Jammy2211 merged 4 commits into
mainfrom
feature/search-ext-a3b-nss-preflight
Oct 8, 2026
Merged

Jammy2211 merged 4 commits into
mainfrom
feature/search-ext-a3b-nss-preflight

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Closes #1678

Search-extensibility phase A3b. Stacked on A3 (#1680, itself stacked on A2 #1679): review and merge those first. The diff against feature/search-ext-a3-samples-checkpointer is the three commits below plus a merge of the updated A3 branch.

Summary

  1. NSS onto Fitness and run(ctx) (aeadaae48)
    • NSS now samples through its fit's scalar objective (Fitness.objective via make_fitness, from its declared objective_target and invalid_value). It no longer uses the inline nss_log_likelihood_from closure, so NSS shares the one guard path: NaN/inf handling, the configured magnitude ceiling and the traced xp.where assertion penalty. An assertion no longer raises TracerBoolConversionError under jit.
    • NSS moves onto run(ctx) + raw_samples_from and resumes from ctx.resume (NativeFileCheckpointer("nss_checkpoint.pkl")).
    • The bridge builds the Fitness before sampling. The resume likelihood sanity check therefore runs at the start of the run.
    • make_fitness is now the only place under autofit/non_linear/search that builds a Fitness.
    • Removed nss_log_likelihood_from, which had no external users.
  2. JAX trace preflight and x64 check (8bc269743, new autofit/non_linear/search/preflight.py). For a JAX analysis only, these run in start_resume_fit after the test-mode bypass and the REQUIRED gate.
    • check_x64 warns once per process when jax_enable_x64 is off. It raises for a search declaring the new requires_fp64 capability (default False; no search sets it).
    • trace_preflight runs one jax.eval_shape of the declared objective kind (batched or scalar) at the prior medians, plus the gradient kind when gradient == uses. Nothing is compiled or executed.
    • A likelihood that cannot be traced raises SearchException, which names the search and the kind and is chained to the tracer error.
    • PYAUTO_JAX_PREFLIGHT=0 skips the preflight. PYAUTO_JAX_PREFLIGHT_PROBE=1 adds an eager numerical probe, off by default.
    • docs/design/run_ctx.md gains Revision 2. The signature and members are unchanged.
  3. Dynesty single-core control flow (c89cbc430)
    • AbstractDynesty._fit chose the single-core path by raising a RuntimeError inside a try. That meant any RuntimeError raised during a pooled run (including JAX's XlaRuntimeError) silently restarted the run single-core.
    • The path is now chosen by a private _SingleCoreRun sentinel. Only a RuntimeError from creating the pool still falls back, chained as the cause.

Tests run

  • pytest test_autofit -q -x on the merged branch (local, numpy 2.2.6, JAX present): 3555 passed, 1 skipped, 4 xfailed.
  • New and changed tests:
    • nss/test_nss_fitness.py: assertions under jit, plus an NSS fit with assertions under jit.
    • test_preflight.py:
      • an np.asarray likelihood raises with the error chained;
      • batched and gradient kinds;
      • the env-flag skip, the numpy no-op and the test-mode-2 bypass;
      • the REQUIRED gate runs before the preflight;
      • the probe is off by default and warns on an invalid value;
      • fp32 warns once, and raises when requires_fp64 is set.
    • nest/test_dynesty_single_core_sentinel.py.
    • Updated nss/test_checkpoint.py, nss/test_log_likelihood_ceiling.py and test_make_fitness.py.
  • Not run here: the phase witness (NSS fitting gaussian_x3_blend with its ordered-centre assertions through the autofit_inference harness). The unit-scale analogue (test_nss_fit_with_assertions_runs_under_jit) passes.

Decisions taken

  • New capability requires_fp64 (default False, no search sets it): added to the manifest in a schema-compatible way. fp32 only warns until a search opts in.
  • Numerical probe off by default (PYAUTO_JAX_PREFLIGHT_PROBE=1 turns it on). The default preflight stays eval_shape only, with no likelihood evaluation, so it costs no compile.
  • Preflight placement: after the test-mode bypass and after the REQUIRED gate, so the D2 regression (REQUIRED + PYAUTO_DISABLE_JAX=1 + PYAUTO_TEST_MODE=2 completes) is untouched.
  • The bridge repeats the REQUIRED gate before building the Fitness, for callers that invoke _fit directly.
  • nss_log_likelihood_from removed outright rather than deprecated, because it had no external users.
  • Dynesty fallback is narrowed to a RuntimeError raised while creating the pool. A failure during the run now surfaces instead of silently restarting single-core.

🤖 Generated with Claude Code

Jammy2211 and others added 4 commits October 8, 2026 18:48
NSS samples through its fit's scalar objective (Fitness.objective via
make_fitness, from its declared objective_target and invalid_value) instead
of the inline nss_log_likelihood_from closure, so it shares the one guard
path: NaN/inf, the configured magnitude ceiling and the traced xp.where
assertion penalty (an assertion no longer raises TracerBoolConversionError
under jit). NSS moves onto run(ctx) + raw_samples_from, resuming from
ctx.resume (its NativeFileCheckpointer("nss_checkpoint.pkl")); the bridge
builds the Fitness before sampling, so the resume likelihood sanity check
now runs at the start of the run. make_fitness is the only Fitness
construction site left under autofit/non_linear/search. The bridge repeats
the REQUIRED gate before building the Fitness for direct _fit callers.

Removed: nss_log_likelihood_from (no external users).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
NonLinearSearch.start_resume_fit, after the test-mode bypass and the
REQUIRED gate, now runs (autofit/non_linear/search/preflight.py, only for a
JAX analysis):

- check_x64: warn once per process when jax_enable_x64 is off; raise for a
  search declaring the new capability requires_fp64 (default False, none
  set; added to the manifest, schema-compatible);
- trace_preflight: one jax.eval_shape of the declared objective kind
  (batched for a batched search, else scalar) at the prior medians, plus its
  gradient kind when gradient == uses; no compile or execution. A
  non-traceable likelihood raises SearchException naming the search and the
  kind, chained (raise ... from e) to the tracer error. PYAUTO_JAX_PREFLIGHT=0
  skips it; PYAUTO_JAX_PREFLIGHT_PROBE=1 adds an eager numerical probe at the
  prior medians (off by default) that warns on the invalid value.

run_ctx.md gains Revision 2 (NSS on the bridge, the bridge's REQUIRED gate,
the preflight placement); signature and members unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
AbstractDynesty._fit selected its single-core path with `raise RuntimeError`
inside a try, so `except RuntimeError` also caught any RuntimeError raised
while a pooled run executed (genuine dynesty errors, JAX's XlaRuntimeError)
and silently restarted the run single-core. The control flow is now the
private _SingleCoreRun sentinel (not a RuntimeError subclass); only a
RuntimeError from creating the pool still falls back, chained as its cause.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ness) into A3b

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Jammy2211 Jammy2211 added pending-release PR queued for the next release build decision-taken labels Oct 8, 2026
Base automatically changed from feature/search-ext-a3-samples-checkpointer to main October 8, 2026 19:53
@Jammy2211
Jammy2211 merged commit 7d05672 into main Oct 8, 2026
5 checks passed
@Jammy2211
Jammy2211 deleted the feature/search-ext-a3b-nss-preflight branch October 8, 2026 20:05
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: NSS onto Fitness, trace preflight and x64 check (search-extensibility A3b)

1 participant