Skip to content

fix: OptimisationState limits guard, VariableData.any, redundant limits overrides #1531

Description

@Jammy2211

Overview

Three tidy-ups PyAutoFit#1527 left behind, plus one live bug found while doing the first.

VariableData.any reduced through var_all, so it meant "is there a variable whose elements are all True" rather than "is any element True". OptimisationState.valid asks (parameters < lower_limit).any() — so a parameter vector with some components outside their limits was reported valid, and only a variable violating on every component was caught. MeanField's valid.any() under-reported the same way.

That bug, not the truthiness guard the #1527 follow-up list named, is why the Laplace limits check under-enforced. It surfaced only because rewriting the guard needed a test, and OptimisationState.valid had no test coverage at all — the process lesson from #1477, third time it has paid out in this lineage.

Plan

  • Fix VariableData.any to reduce through var_any, restoring the limits check in OptimisationState.valid and the validity count in MeanField.
  • Rewrite OptimisationState.valid's guards from truthiness to is not None, so "was a limit supplied?" is stated rather than implied by VariableData happening to be a dict.
  • Delete the three limits overrides fix: LogGaussianPrior declares its own (0, inf) support #1527 made exact duplicates of the base, once the float() coercion difference is measured rather than assumed.
  • Cover both OptimisationState.valid and VariableData.any, which had no tests, and verify the new tests by inversion.
Detailed implementation plan

Work Classification

Library — PyAutoFit only. No workspace changes.

Affected Repositories

  • PyAutoFit (primary)

Branch Survey

Repository Current Branch Dirty?
./PyAutoFit claude/loggaussian-prior-support-ngh59x (from main @ 34d6dff) clean

Branch: claude/loggaussian-prior-support-ngh59x

Worktree root: none — this ran web-github against a direct clone at /home/user/pyautofit.

Implementation Steps

  1. autofit/mapper/variable.pyVariableData.any dispatches to var_any, not var_all. The other four .any() call sites in the library (utils.py:539, multi_start_gradient/search.py:468, latent.py:274, and the numpy paths) are on numpy arrays and are untouched; the only VariableData consumers are line_search.py and mean_field.py:501, both of which want a real any.
  2. autofit/graphical/laplace/line_search.pyvalid's two guards become is not None, with a docstring recording why truthiness worked only by accident.
  3. autofit/mapper/prior/{uniform,log_uniform,truncated_gaussian}.py — delete the limits overrides and the now-unused Tuple imports.
  4. test_autofit/graphical/test_optimisation_state_valid.py — new; 8 tests over valid (no limits, empty limits, inside, below, above, on-the-bound, a 0.0 lower limit) and VariableData.any / .all().

Key Files

Measured, not assumed

  • The limits deletion is not a type change. The base coerces with float() and the overrides did not, which looked like a downstream risk via TruncatedGaussianPrior(mean, sigma, *limits) in prior passing. All three priors already store Python floats, so the coercion is a no-op; and under jax.jit a prior never reaches limits at all, because tree_unflatten__init__ calls float() on the tracer and raises ConcretizationTypeError first — with or without this change.
  • The guard rewrite is behaviour-preserving. The only case whose guard differs is the empty VariableData: falsy under the old form, so the comparison was skipped; run under the new form, where an empty comparison's .any() is False. valid returns True either way.
  • The tests fail without the fix. Reverting var_any to var_all fails 3 of the 8.

Validation

  • Full suite: NUMBA_CACHE_DIR=… MPLCONFIGDIR=… python3 -m pytest -x -q test_autofit/2186 passed, 36 skipped (baseline on main: 2178 / 36).
  • Readiness gate: PyAutoHeart is not present in this session, so the documented fallback applies — per-repo pytest -x as the gate, GREEN.

Scope note

This covers two PyAutoMind prompts, against the usual one-prompt-one-PR rule, because both are the same cleanup left by #1527 and the second is three line deletions:

  • draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md
  • draft/refactor/autofit/redundant_prior_limits_overrides.md

It also widens past both to fix VariableData.any, without which the guard being tidied does not actually work. Happy to split if a reviewer prefers.

Not done here

  • The check_limits EP question — the prior declares (0, inf) while its message stays at ±inf. Measured: EP is not wrong today, because the message's own density returns a clean -inf at negative values rather than NaN. Tracked in draft/research/graphical_ep/transformed_message_declares_support.md.
  • message.logpdf(0.0) is -1.798e308 where the prior says -inf. Noted, unverified, out of scope.

Origin

Follow-ups 1 and 2 from complete/2026/08/loggaussian-prior-declares-own-support.md (this repo's #1526 / #1527).

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