Skip to content

Report zero-iteration Laplace convergence as an update, not a skip (#1711) - #1712

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/ep-zero-success-factors
Oct 23, 2026
Merged

Jammy2211 merged 1 commit into
mainfrom
feature/ep-zero-success-factors

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Closes #1711. On the analytic_gaussian wave-1 ensemble (ruling R-20260910-04), 19/400 seed-legs ended with a factor that never recorded a SUCCESS update. Diagnosis on the exactly-Gaussian leg A (seed 140): the factor is the mu prior, whose Laplace optimiser met its gradient tolerance at the start point (iter=0) because the posterior mean lies within 2e-3 of the prior mean; the projection was accepted and the message updated (N(50, 22.4) → N(49.9994, 10.0)), and the final posterior matches the closed form to a = 5e-5, b = 2e-5. That is a converged update, yet optimise_quasi_newton reported it with updated=False, the same as a skipped line search. The library's STALE tracking already treated it correctly (it reads the post-projection status), so this PR aligns the Newton status with that and documents the semantics; no EP update maths, projection or Laplace numerics change, and post-fix numerics are bit-identical on the repro seeds.

The second shape in the ruling (HierarchicalFactor with only BAD_PROJECTION, leg-B seeds 29/71/106/152/178/193) is the Laplace-on-scatter boundary state (#1405) and is cured by LaplaceOptimiser(projection="moments") (#1656): seeds 29 and 71 re-run under moments give HierarchicalFactor0 {SUCCESS: 28} and {SUCCESS: 23} with no STALE warning. Two findings, two causes; only the first needed a library change.

The ensemble witness's criterion 6 ("zero SUCCESS = stale") is what conflated the two; the amended wording (SUCCESS or NO_CHANGE counts as a completed update, BAD_PROJECTION-only stays a miss) goes to the Cortex task and ensemble_seed.py separately. The seed-140 regression leg lands in autofit_workspace_test (scripts/graphical/analytic_gaussian.py).

API Changes

No public API change. optimise_quasi_newton now returns Status(updated=True, flag=StatusFlag.NO_CHANGE) when a stop condition is met before the first step with success=True (previously updated=False). NO_CHANGE rows in ep_history.csv were already written with updated=True after projection, so recorded histories do not change.
See full details below.

Test Plan

  • New test_zero_iteration_convergence_is_reported_as_an_update (red before, green after) and test_factor_at_its_mode_on_first_visit_is_not_stale (guard: NO_CHANGE rows with updated=True, no STALE in log or ep_diagnostics.results, mean and width match closed form) in test_autofit/graphical/functionality/test_factor_failure_recovery.py
  • pytest test_autofit/graphical -q -x: 322 passed
  • Full pytest test_autofit -n 4: 3561 passed, 1 skipped, 1 xfailed
  • Science repro (scratch copy of analytic_gaussian scripts, worktree PyAutoFit): seed 140 leg A and leg-B seeds 29/38/71 under mode and moments; sidecar numerics identical before and after the change
Full API Changes (for automation & release notes)

Removed

  • none

Added

  • none

Changed

  • autofit.graphical.laplace.newton.optimise_quasi_newton — a stop condition satisfied at i == 0 with success=True now yields Status(updated=True, flag=NO_CHANGE) instead of updated=False.
  • autofit.graphical.utils.StatusFlag.NO_CHANGE — documented as a converged update (start point already at the mode; projection still runs and may move the message), not a skip.
  • autofit.graphical.expectation_propagation.optimiser.EPOptimiser._stale_factor_warnings — docstring: "updated" is not the same as "flagged SUCCESS"; no logic change.

Migration

  • none

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

…1711)

optimise_quasi_newton met its stop condition at the start point (iter=0)
and returned updated=False, the same status as a failed line search, so a
factor already at its mode looked like a stale one to anything reading the
Newton status. The projection of that point is accepted and its message
updated, and EP's own stale tracking already read the post-projection
status, so only the Newton status and the NO_CHANGE docstrings change; no
update maths, projection or Laplace numerics are touched.

Evidence: analytic_gaussian wave-1 leg A seed 140 (the mu prior; posterior
mean within 2e-3 of the prior mean; closed-form match a=5e-5, b=2e-5). The
BAD_PROJECTION-only HierarchicalFactor shape on leg B is the #1405 scatter
state cured by projection="moments" (#1656), verified on seeds 29 and 71.

Adds two tests in test_factor_failure_recovery.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Jammy2211

Copy link
Copy Markdown
Collaborator Author

Workspace regression: PyAutoLabs/autofit_workspace_test#115 · Cortex criterion-6 amendment: PyAutoLabs/PyAutoCortex#75 · Insight ledger reconciliation: PyAutoLabs/PyAutoInsight#38

@Jammy2211
Jammy2211 merged commit abb8bfa into main Oct 23, 2026
5 checks passed
@Jammy2211
Jammy2211 deleted the feature/ep-zero-success-factors branch October 23, 2026 14:32
@pyautolabs-bot pyautolabs-bot Bot removed the pending-release PR queued for the next release build label Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: EP counts zero-iteration Laplace convergence (NO_CHANGE) as a stale/skipped update

1 participant