Skip to content

Commit 8a05bd7

Browse files
Jammy2211claude
authored andcommitted
prompt: file MultiStartGradient resume FoM sanity-check bug
Reproduced on main with the stock example Analysis: a killed mid-run search refuses to resume because the FoM sanity check compares a stored log-likelihood against the multi-start chi-squared convention (a consistent -2x relationship). Found while verifying the resume path of the NaN step counters in #1472; independent of that change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 2b8caf4 commit 8a05bd7

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# MultiStartGradient cannot resume a killed mid-run search — FoM sanity check compares log-likelihood against chi-squared
2+
3+
Type: bug
4+
Target: PyAutoFit
5+
Repos:
6+
- PyAutoFit
7+
Difficulty: small
8+
Autonomy: supervised
9+
Priority: normal
10+
Status: formalised
11+
12+
`MultiStartGradient` (`af.MultiStartAdam` / `af.MultiStartProdigy`) raises
13+
`SearchException` when resuming a search that was killed mid-run, so the resume
14+
path is unusable. Long pixelized campaigns are exactly the runs that get
15+
interrupted, and this is the mechanism meant to recover them.
16+
17+
## Reproduce
18+
19+
Confirmed on `main` (PyAutoFit `39a187b2d`), with the **stock** `af.ex.Analysis`
20+
— no custom analysis needed.
21+
22+
1. Run a named `af.MultiStartAdam` with `iterations_per_full_update=2` so
23+
checkpoints are written during the run (the default single-chunk cadence only
24+
checkpoints at the end, so a mid-run kill leaves nothing to resume from).
25+
2. `kill -9` it around step 10 of 400. This leaves
26+
`files/search_internal/search_internal.dill` and **no** `.completed` marker —
27+
the only state a real resume starts from, since a search that finishes
28+
deletes its checkpoint.
29+
3. Re-run the identical script. It fails:
30+
31+
```
32+
autofit.exc.SearchException:
33+
Figure of merit sanity check failed.
34+
Old Figure of Merit = -2692.9547224874896
35+
New Figure of Merit = 5390.281252235021
36+
```
37+
38+
Reproduced twice, with matching structure:
39+
40+
| analysis | old FoM | new FoM | ratio |
41+
|---|---|---|---|
42+
| stock `af.ex.Analysis` | -2692.9547224874896 | 5390.281252235021 | -2.0000x |
43+
| a custom analysis | -2915.2793638973044 | 5834.81444744653 | -2.0006x |
44+
45+
## Diagnosis (starting point, not a conclusion)
46+
47+
The consistent **-2x** relationship points at a units mismatch rather than a
48+
genuine likelihood change. `MultiStartGradient` builds its `Fitness` with
49+
`fom_is_log_likelihood=False` and `convert_to_chi_squared=True`, i.e. its
50+
figure-of-merit is `-2 * log_posterior` (a chi-squared). The stored "old" value
51+
looks like a **log-likelihood** while the freshly computed "new" value looks
52+
like the **chi-squared**, so `Fitness.check_log_likelihood`
53+
(`autofit/non_linear/fitness.py:614`, called from `__init__` at line 171)
54+
appears to be comparing a stored log-likelihood against a value in the
55+
multi-start FoM convention.
56+
57+
Verify that before fixing — the fix is either at the point the old value is
58+
persisted or at the point the comparison converts, and picking the wrong one
59+
would paper over a real check. Note `check_log_likelihood` exists to catch a
60+
genuinely changed likelihood function between runs (the documented
61+
"multi-start resume chains do not survive library upgrades that touch FoM
62+
bookkeeping" behaviour in `autolens_profiling/scripts/misc/searches/README.md`).
63+
The fix must keep that guard working for the case it was built for; the bug is
64+
that it fires on an **unchanged** likelihood in the same process generation.
65+
66+
## Scope
67+
68+
- Confirm which side carries the wrong convention, with a test that resumes a
69+
killed run and asserts it continues rather than raising.
70+
- The library unit suite is NumPy-only and `_fit` needs jax + optax, so the
71+
regression test likely belongs in `autofit_workspace_test` alongside
72+
`scripts/searches/MultiStartResurrect.py` (`ENV: real_search jax`), with the
73+
kill/resume driven as a subprocess.
74+
75+
## Provenance
76+
77+
Found while verifying the resume path of the value-NaN / gradient-NaN step
78+
counters added in PyAutoFit#1472. Those counters restore via
79+
`search_internal.get(..., 0)` and are designed to keep accumulating across a
80+
resume; that behaviour **cannot be demonstrated end-to-end until this is
81+
fixed**, so #1472 ships with the resume accumulation covered only by unit tests
82+
over hand-built `search_internal` dicts. Re-check it here once resume works.
83+
84+
This bug is independent of #1472 — reproduced on `main` without those changes.

0 commit comments

Comments
 (0)