Skip to content

fix: natural_logpdf clamps a genuine -inf to -1.8e308 #1533

Description

@Jammy2211

Overview

AbstractMessage.natural_logpdf (autofit/messages/interface.py:98) reduces through

xp.nan_to_num(log_base + eta_t - log_partition, nan=-xp.inf)

The nan=-xp.inf is deliberate — an out-of-support NaN is zero density. But posinf/neginf are left at their defaults, and nan_to_num replaces a genuine -inf with negative float max. The call therefore does the opposite of its intent for exactly the inputs that already had the right answer:

value reaching the reduction intended actual
NaN -inf -inf
-inf -inf -1.7976931348623157e+308

Measured on main @ 6e2d8c8

LogGaussianPrior(0.4, 1.3):

value message.logpdf why
-1.0 -inf log(-1) is NaN → nan=-inf applies
0.0 -1.7976931348623157e+308 log(0) is -inf → default neginf clamps

That asymmetry is the proof of mechanism, and the clamped value is exactly -sys.float_info.max.

UniformPrior and LogUniformPrior return -inf correctly outside their boxes, so this is not visible for every family — it needs an expression that reaches -inf rather than NaN.

Why it matters

-1.8e308 is finite, and isfinite is the test a lot of this codebase branches on:

  • optax.apply_if_finite — the mechanism autofit/non_linear/clipper.py is built around. That module's entire premise is that leaving the prior support makes the objective non-finite.
  • Two such terms summed overflow to -inf while one does not, so behaviour depended on how many parameters were out of support.

Plan

  • Pass neginf=-xp.inf, posinf=xp.inf so only NaN is replaced.
  • Cover both halves of the reduction, pin that in-support values are untouched, and assert the general property that no prior family reports a finite density off its support.

Origin

Loose end from #1532, noted in its Shipped comment and not filed at the time. PyAutoMind prompt: draft/bug/priors/natural_logpdf_clamps_neginf.md.

Deliberately out of scope

TruncatedGaussianPrior's message returns finite logpdf well outside its limits (-8.20 at -1.0 for a (0, 3) support). That is separate looseness in TruncatedNormalMessage, not this clamp — the prior-level log_prior_from_value is correct there, which is why the P6 property tests pass. Not addressed here, and the new general-property test deliberately excludes it rather than papering over it.

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