Skip to content

TransformedMessage.logpdf/pdf omit the transform Jacobian (base-space density at physical coordinates) #1498

Description

@Jammy2211

Found while implementing the #1497 property sweep (priors/messages census follow-up; hubs #1330/#1331). Same failure shape as #1331-04: a generic-interface density path is silently wrong while the direct paths are right.

The finding

TransformedMessage inherits MessageInterface.logpdf (natural-parameter path). Its overrides forward natural_parameters, calc_log_base_measure and to_canonical_form to the base message — to_canonical_form is @transform-decorated, so the physical input is mapped to base coordinates — but no log_det change-of-variables term is ever added. The result: logpdf(x) at a physical x returns the base-space density at the mapped point, not the physical density. Meanwhile TransformedMessage.factor(x) does it correctly (base.logpdf(transform(x)) + log_det).

Reproduction on main @ 7d4d931 (numbers exact):

import numpy as np
from scipy.integrate import quad
import autofit as af

m = af.UniformPrior(0.0, 1.0).message      # TransformedMessage(UniformNormalMessage, LinearShift)

m.logpdf(np.asarray(0.7))                  # -1.05644  — physical density is 1.0, expect 0.0
m.factor(np.asarray(0.7))                  #  0.0      — correct

quad(lambda x: float(m.pdf(np.asarray(x))), 0.0, 1.0)   # 0.282095 = 1/(2*sqrt(pi)), not 1.0
quad(lambda x: float(np.exp(m.factor(np.asarray(x)))), 0.0, 1.0)  # 1.000000

For LogUniformPrior(0.01, 100) the generic-path pdf integrates to 15.83. Every TransformedMessage-wrapped prior (Uniform, LogUniform, LogGaussian) is affected; plain NormalMessage / TruncatedNormalMessage priors are not.

Doc contradiction

The composed_transform.py module docstring (added in #1334) states: "Evaluating logpdf(x) at a physical x runs the same three steps in reverse (physical → base) via _transform, accumulating the log-Jacobian log_det of each transform for the change of variables." The code does not accumulate it. Either the docstring describes the intended contract (then logpdf needs the log_det term) or logpdf is deliberately base-space for EP message arithmetic (then the docstring and pdf() are wrong/misleading).

What needs adjudication before a fix

  • Which callers consume TransformedMessage.logpdf/pdf vs factor? The EP factor-graph path uses factor/exp_factor (correct). If any EP projection / log_norm path evaluates logpdf on transformed messages at physical coordinates, it inherits this error.
  • logpdf_gradient returns the base log_likelihood with a Jacobian-corrected gradient — a third convention (gradient physical, value base-space) worth settling in the same pass.
  • Note value_for, cdf, log_prior_from_value and factor are all verified correct — user-facing sampling and MCMC/MLE log-priors are unaffected. The exposure is anything treating transformed_message.pdf() as a physical density.

The #1497 property tests assert the physical-density path via factor for transformed messages and cite this issue at the site; when this is resolved they can be tightened to assert logpdf directly.

Sequencing suggestion: adjudicate alongside the parked single-source-density design (census C1/C4, prompts 12/13) — this is a fourth density-convention divergence of exactly the kind that design exists to eliminate.

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