Skip to content

fix(priors): width-modifier safety + strict sigma>0 (Phase 2 of #1331) #1346

Description

@Jammy2211

Overview

Phase 2 of the #1331 priors/messages batch — the behaviour-changing pair, split from Phase 1 (#1344, merged via #1345) because it alters prior-passing widths in search chaining (SLaM-style pipelines). Verdicts: bug/priors/06 + 08 on #1330; decisions resolved on #1331 (Decision 5: minimal — abs(mean) + optional configurable absolute floor; Decision 2: reject σ ≤ 0 strictly).

Sequencing constraint (from the Phase-1 impact analysis): the width fix (D5) lands with the strict σ check (D2) in one PR so today's silent σ=0 degeneracy (a mean=0 chained parameter freezes into a delta function) never becomes an unexplained hard crash: where a computed width is still ≤ 0, prior passing raises a clear, parameter-named PriorException pointing at the width-modifier config — not a deep MessageException.

Plan

  • RelativeWidthModifier: sigma = value * abs(mean), plus opt-in absolute_floor (YAML-configurable; engages when value*abs(mean) falls below it). Negative means no longer produce negative sigma (today 0.5 * -1.0 = -0.5 flows silently into GaussianPrior and flips value_for).
  • Prior passing (gaussian_prior_model_for_arguments width application): if the final width ≤ 0 (e.g. mean=0, no floor), raise PriorException naming the parameter path with remediation guidance (set an absolute width modifier or an absolute_floor).
  • NormalMessage.__init__: enforce σ > 0 (numpy path; JAX defers to NaN propagation — the existing assert_sigma_non_negative helper's lax.cond branch is broken by design and is retired). GaussianPrior(mean, sigma<=0) and GaussianPrior.with_limits(5, 5) become loud.
  • TruncatedNormalMessage: extend its existing < 0 rejection to <= 0 so the two classes agree (strictly).
  • Regression tests: mean=0 chained parameter (clear error without floor; correct width with floor), abs(mean) on negative means, floor engagement + config round-trip, strict ctor rejection on both message classes.
  • Full test_autofit/ suite; pre-flight grep for tests that deliberately construct σ ≤ 0 messages (they may encode point-mass behaviour — FixedMessage is the sanctioned path for that).
Detailed implementation plan

Affected Repositories

  • PyAutoLabs/PyAutoFit (primary)

Branch Survey

Repository Current Branch Dirty?
./PyAutoFit main (post-#1345, c0b6c94) clean

Suggested branch: feature/prior-width-safety

Parallel disjoint PyAutoFit claim alongside ep-graphical-docs (#1334) + ep-diagnostics (#1335), both autofit/graphical — this work is autofit/mapper/prior/width_modifier.py, autofit/mapper/prior_model/abstract.py (width application site), autofit/messages/{normal,truncated_normal}.py. Zero overlap.

Implementation Steps

  1. autofit/mapper/prior/width_modifier.pyRelativeWidthModifier.__init__(value, absolute_floor=None); __call__ returns max(value * abs(mean), floor) when floor set, else value * abs(mean); from_dict/dict round-trip the optional key.
  2. autofit/mapper/prior_model/abstract.py — after width = width_modifier(mean) (and the explicit r/a paths), raise exc.PriorException naming the prior path if width <= 0.
  3. autofit/messages/normal.py — numpy-path strict sigma > 0 enforcement in NormalMessage.__init__; retire the broken assert_sigma_non_negative JAX branch.
  4. autofit/messages/truncated_normal.py< 0<= 0.
  5. Tests per the plan bullet above (numpy-only).

Behaviour changes (user-visible, disclosed)

  • Chained fits with parameters whose posterior median is negative get a positive passed width (previously negative sigma, silently sign-flipping value_for). Sampling of affected chained fits changes — for the better.
  • Chained fits with a mean=0 unconfigured parameter now fail loudly with guidance (previously silently froze the parameter via a delta-function prior).
  • GaussianPrior/NormalMessage with σ ≤ 0 raise at construction.

Autonomy: supervised (--auto, human instructed live 2026-07-10 after Phase-1 merge; heart-ack 6-reason set recorded in active.md this chain).

🤖 Generated with Claude Code

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