Skip to content

bug: EP log_evidence sums a non-finite term silently (NaN evidence on analytic_gaussian seeds 25/172) #1713

Closed

Description

@Jammy2211

Overview

Two seeds of the 200-seed N=5 analytic_gaussian ensemble (RAL job 342413, autofit 2026.8.17.1; PyAutoCortex ruling R-20260910-04) emitted, repeatedly, RuntimeWarning: invalid value encountered in add from EPMeanField.log_evidence (autofit/graphical/expectation_propagation/ep_mean_field.py:336): 7 times on seed 25, 19 on seed 172. Both seeds' parity results are otherwise fine and neither is in the stale-factor set, so the evidence accumulator goes non-finite on healthy-looking runs, and only SLURM's .err capture caught it. This task finds which term is non-finite and why, decides whether the readout is biased or merely NaN, and makes the library report it once by name instead of warn-and-sum. Reporting only: no change to the EP update maths, projections or messages (the human's 2026-10-23 instruction: EP is trusted).

Second of the three wave-1 ruling findings of the graphical-ep campaign (PyAutoInsight tasks/ep_campaign_state_2026-10-23.md); the first shipped as PyAutoFit#1711/#1712.

Plan

  • Reproduce seeds 25 and 172 (--columns ep) on current main from a scratch copy of the science scripts (/mnt/c/Users/Jammy/Science/active/analytic_gaussian; the prompt predates the active/ layout), under -W error::RuntimeWarning to get the traceback at the first non-finite term; dump per-variable and per-factor evidence terms on that sweep.
  • Diagnose the term (degenerate message, inf−inf between log partitions, NaN sampler logz) and whether the final posterior is affected (compare against results/ens_n5/seed_0025.json).
  • Library: log_evidence detects non-finite terms before summing, warns once per run naming the variable/factor, returns NaN explicitly without numpy warning spam; diagnostics records the names in ep_diagnostics.results; EPHistory.evidence_divergence returns inf for a NaN so it can never claim convergence. If the root cause is a degenerate message from an accepted projection, report it as new scope rather than touching the projection.
  • Unit test (red before, green after): no RuntimeWarning, one named warning, NaN evidence, inf divergence.
  • Workspace regression in autofit_workspace_test/scripts/graphical/analytic_gaussian.py: no RuntimeWarning from ep_mean_field and finite final evidence on seed 0 (seed 25 only if ≤ 60 s).
  • Ship library first, then workspace; ledger lines in Insight (companion state file) and Cortex (analytic_gaussian Log).

Tier: glance — merge mode: auto-merge on green if the Witness passes, post-merge summary.

Detailed implementation plan

Affected Repositories

  • PyAutoFit (primary)
  • autofit_workspace_test (regression)

Branch Survey

Repository Current Branch Dirty?
./fit/PyAutoFit main (abb8bfa) clean
./fit/autofit_workspace_test main (97cb781) clean

Suggested branch: feature/ep-evidence-nonfinite — worktree ~/Code/PyAutoLabs-wt/ep-evidence-nonfinite/

Implementation Steps

  1. Repro: scratch copy of scripts/ + config/; python3 -W error::RuntimeWarning scripts/ensemble_seed.py --seed 25 --columns ep (and 172) with the worktree PyAutoFit on PYTHONPATH, NUMBA_CACHE_DIR=/tmp/numba_cache MPLCONFIGDIR=/tmp/matplotlib; then a normal run with EPMeanField.log_evidence monkeypatched to log each term (variable name, message mean/variance/limits, factor log_norm). Never write into the science checkout's results/.
  2. Library (autofit/graphical/expectation_propagation/): ep_mean_field.py log_evidence (~lines 310–336) — per-term dicts, np.isfinite scan, one logger.warning per instance naming variables/factors, np.errstate(invalid="ignore") around the sum, explicit float("nan"); diagnostics.py (~131) — record evidence_nonfinite: <names> in ep_diagnostics.results; history.py evidence_divergence (~152) — NaN → inf.
  3. Tests in test_autofit/graphical/functionality/ (new test_evidence_nonfinite.py or in test_history.py): build an EPMeanField from a tiny graph, inject a non-finite log_norm / degenerate message; assert no RuntimeWarning, one named warning, NaN evidence, inf divergence. pytest test_autofit/graphical -q -x, then the full suite.
  4. Workspace: scripts/graphical/analytic_gaussian.py — warnings.catch_warnings(record=True) around the EP column, assert no RuntimeWarning whose filename ends in ep_mean_field.py, assert np.isfinite on the final log_evidence; seed 25 leg only if ≤ 60 s.
  5. Ledgers: append a 2026-10-23 row to the findings follow-ups table in PyAutoInsight tasks/ep_campaign_state_2026-10-23.md (render with the worktree's own bin/pyauto-insight board + PYAUTO_BRAIN), and a Log note in PyAutoCortex projects/analytic_gaussian.md.

Key Files

  • autofit/graphical/expectation_propagation/ep_mean_field.py — variable_evidence, factor_evidence, log_evidence
  • autofit/messages/abstract.py:380 — log_normalisation (read only)
  • autofit/graphical/expectation_propagation/history.py — evidence_divergence, evidences
  • autofit/graphical/expectation_propagation/diagnostics.py — results file
  • autofit_workspace_test/scripts/graphical/analytic_gaussian.py — regression

Witness

python3 scripts/ensemble_seed.py --seed 25 --columns ep and the same for seed 172 complete with no RuntimeWarning from ep_mean_field.py, and the EP evidence they record is finite — or the non-finite term is raised/logged with the variable named rather than warned-and-summed, with a regression in autofit_workspace_test/scripts/graphical/ asserting the EP evidence of a converged fit is finite.

Lineage

PyAutoCortex ruling R-20260910-04; PyAutoFit#1405; PyAutoFit#1711/#1712 (sibling finding); Mind prompt draft/bug/autofit/nan_in_ep_evidence_accumulation_invalid_value.md.

Original Prompt

Click to expand starting prompt

NaN in EP evidence accumulation: invalid value encountered in add in ep_mean_field.py on analytic_gaussian seeds 25 and 172

Type: bug
Target: PyAutoFit
Repos:

  • PyAutoFit
  • autofit_workspace_test
    Themes:
  • graphical-ep
    Difficulty: medium
    Autonomy: supervised
    Priority: medium
    Status: formalised
    Consequence: glance
    Witness: python3 scripts/ensemble_seed.py --seed 25 --columns ep and the same for seed 172 complete with no RuntimeWarning from ep_mean_field.py, and the EP evidence they record is finite — or the non-finite term is raised/logged with the variable named rather than warned-and-summed, with a regression in autofit_workspace_test/scripts/graphical/ asserting the EP evidence of a converged fit is finite.
    Review-minutes: 3
    Unattended: ready
    Epic: graphical-ep
    Filed: 2026-09-10

Why

Two seeds of the 200-seed N=5 ensemble behind
PyAutoCortex/tasks/analytic_gaussian/ensemble_parity.md (RAL job 342413_[0-199],
autofit 2026.8.17.1) emitted a numpy warning from the EP evidence accumulator, repeatedly.
Verbatim from hpc/batch_cpu/error/error.342413_25.err (line 1, repeated 7x in that file and
10x in error.342413_172.err):

/mnt/ral/jnightin/PyAuto/PyAutoFit/autofit/graphical/expectation_propagation/ep_mean_field.py:336: RuntimeWarning: invalid value encountered in add
  return factor_evidence + sum(variable_evidence.values())

invalid value encountered in add is numpy's NaN-producing-add: at least one term of
factor_evidence or of variable_evidence.values() is non-finite (NaN, or a ±inf pair), and
the library warns and keeps going, folding the result into the EP evidence readout.

This is a separate symptom from the zero-SUCCESS stale-factor finding of the same ruling:
neither seed 25 nor seed 172 appears in that finding's 19 stale seed-legs, and both seeds'
parity results are otherwise fine — they are not among the outliers on any row. So the evidence
accumulator is going non-finite on runs that otherwise look healthy, which is exactly the case a
warning is least likely to be noticed in.

Ruled 2026-09-10 as PyAutoCortex/rulings/2026/09/R-20260910-04.md (accept with findings).

Repro

Science project analytic_gaussian at /mnt/c/Users/Jammy/Science/analytic_gaussian
(git main, no remote), results committed at b44390d:

cd /mnt/c/Users/Jammy/Science/analytic_gaussian
python3 scripts/ensemble_seed.py --seed 25 --columns ep
python3 scripts/ensemble_seed.py --seed 172 --columns ep

Run them with python3 -W error::RuntimeWarning (or np.seterr(invalid="raise")) to get the
traceback at the moment the term goes non-finite. The witness scripts are borrowed verbatim from
autofit_workspace_test/scripts/graphical/analytic_{reference,ep_minimal,autofit}.py @ 165a19e;
the per-seed sidecars are results/ens_n5/seed_0025.json and seed_0172.json.

Ask

  • Where does the non-finite variable evidence come from? Which variable's
    variable_evidence entry is NaN/inf, and what makes it so — a degenerate (zero- or
    negative-variance) message, a log of a non-positive normalisation, an inf−inf cancellation?
  • Should this warn and continue at all? Decide between guarding it (skip / clamp the term
    with a named reason), raising it (a non-finite evidence is a broken fit and should say so), or
    logging it with the offending variable named. Warn-and-sum is the one option that leaves no
    usable trace in a batch run — the only reason it was caught here is that SLURM kept the
    .err files.
  • Does it bias the EP evidence readout? If the accumulated evidence is used for
    convergence, model comparison or the reported log_evidence, a silently-NaN term is a
    correctness problem, not cosmetic. Say which, with the evidence for it.
  • Add the regression named in the Witness above.

Lineage

  • PyAutoCortex/rulings/2026/09/R-20260910-04.md — the ruling that surfaced it
  • PyAutoFit#1405 — the open Laplace-on-scatter collapse basin (leg B's known bias); this is not
    that, but the same EP path

🤖 Generated with Claude Code

Activity

  1. Jammy2211 commented on Oct 23, 2026

    @Jammy2211
    CollaboratorAuthor

    Library PR Created

    PyAutoFit PR #1714 (feature/ep-evidence-nonfinite, commit 42548b3, label pending-release).

    Diagnosis: reproduces on main under the default mode projection (not under moments). The scatter sigma's variable evidence goes +inf on sweep 4 (step 39 on seed 25, step 40 on seed 172) because one HierarchicalFactor member sends it an accepted TruncatedNormalMessage with mean −7374.18, sigma 384.36 on [0, 100] (support ~19 sigma into the tail); TruncatedNormalMessage.log_partition computes log(Φ(b) − Φ(a)) directly, which underflows to −inf (true log Z ≈ −187.93, full log-partition ≈ 2.07 via log_ndtr). Every adjacent factor term becomes −inf and the sum NaN. Posterior rows bit-identical to the committed sidecars; evidence_tol is None so stopping is KL-only; a NaN divergence already failed every comparison. Readout only.

    Shipped (reporting only): named once-per-fit warning + evidence_nonfinite: diagnostics line + explicit NaN, evidence_divergence inf on NaN. No message arithmetic, projection or optimiser change. The tail-stable log_partition cure is message maths and is filed as PyAutoMind/draft/bug/autofit/truncated_normal_log_partition_tail_underflow.md (judge tier, human decision).

    Tests: 7 new (6 red before), test_autofit/graphical 329 passed, full suite serial 3890 passed / 1 skipped / 1 xfailed. Heart YELLOW at ship (manifest drift; PyAutoLens release validation stale), the same two reasons the human acknowledged earlier today.

    Next: autofit_workspace_test regression PR (seed-0 legs assert no ep_mean_field RuntimeWarning + finite evidence; new seed-25 mode leg, 3 s, asserts the term is named).

  2. Jammy2211 commented on Oct 23, 2026

    @Jammy2211
    CollaboratorAuthor

    CI on #1714 (sha 42548b3): docs + generated-search-docs + unittest 3.12 + unittest 3.13 all success; unittest-nojax failed on one unrelated test — test_autofit/non_linear/search/test_start_points.py::test_plot_false_skips_the_plot, InitializerException: The initial samples all have the same figure of merit (1 failed, 3572 passed, 180 skipped). The test draws n=2 unseeded start points (InitializerBall with np.random.default_rng()) on flat data and raises when the two figures of merit coincide; it passes 3/3 locally under a nojax emulation on this head, main's nojax leg was green on 39970496109 an hour earlier, and the EP evidence change is not on its path. Filed as PyAutoMind/draft/bug/autofit/start_points_test_unseeded_identical_figures_of_merit_flake.md. Workspace PR autofit_workspace_test#116 is green (smoke 3.12/3.13) and waits on the library-first gate.

    Per the merge rules a re-run of a red leg is the human's call: gh run rerun 39976190947 --failed -R PyAutoLabs/PyAutoFit.

  3. added a commit that references this issue on Oct 23, 2026
  4. Jammy2211 commented on Oct 23, 2026

    @Jammy2211
    CollaboratorAuthor

    Shipped — 2026-10-23

    PRs

    Upstream PR

    #1714 (merged 919ebd4, pending-release)

    Summary

    The NaN evidence on seeds 25/172 is a readout defect: an accepted truncated-normal message on the scatter sigma sits ~19 sigma into its tail, its log_partition underflows to −inf, the variable's evidence goes +inf and the sum NaN, while the posterior stays bit-identical. The library now names the non-finite term once per fit (log + ep_diagnostics.results), returns an explicit NaN without numpy warnings, and evidence_divergence is inf on a NaN. The workspace script asserts finite evidence and no ep_mean_field RuntimeWarning on seed 0, and that the term is named on seed 25 under the mode projection.

    Session Notes

    • No message arithmetic, projection or optimiser changes (EP trusted). The cure of the term itself — a tail-stable log_partition via log_ndtr — is message maths and is filed for the human: PyAutoMind/draft/bug/autofit/truncated_normal_log_partition_tail_underflow.md.
    • The nojax leg flaked once on an unrelated unseeded start-points test; re-run green on the human's call; filed as draft/bug/autofit/start_points_test_unseeded_identical_figures_of_merit_flake.md.
    • Glance-tier merge by the shipping session under the approved plan; revert = git revert 919ebd452 on PyAutoFit main and the DOC: module not found #116 merge commit on autofit_workspace_test main.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions