You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TransformedMessage.factor_gradient (autofit/messages/composed_transform.py:360-378) crashes on any call: it unpacks four values from self._transform_det_jac(x), which returns three. Found 2026-08-19 while running the caller inventory that #1498 asked for. It has zero production callers in autofit/, so no live search or EP path is affected — this is dead code that fails on first use, the same "never run end-to-end" shape as #1331 finding 01 (LogGaussianPrior.with_limits).
Reproducer on main @ 21288bb:
importautofitasafaf.UniformPrior(0.0, 2.0).message.factor_gradient(1.3)
# ValueError: not enough values to unpack (expected 4, got 3)
_transform_det_jac (composed_transform.py:281-289) returns (x, logd, logd_jacs) where logd_jacs is a list of (logd_grad, jac) tuples. Even past the unpack, the body's flat-list handling (the for jac in reversed(jacs): grad = grad * jac loop and the final grad + logd_grad) does not match the structure _transform_det_jac actually produces.
Option A — repair. Make it the physical-density gradient companion of factor: value base.logpdf(T(x)) + log_det, gradient chain-ruled through the transform stack plus the log_det gradient. Add it to the test: property-based correctness sweep over every Prior subclass #1497 property sweep, checked against a numerical derivative of factor.
As with the #1330/#1331 census, this finding is AI-generated. Before any fix, please sanity-check (a) the reproducer, and (b) the zero-production-callers claim — note the factor_gradient in autofit/graphical/laplace/line_search.py is an unrelated FactorApproximation-level interface, not this method, and the only exp_factor use is a single test.
If repair: rewrite factor_gradient to consume (x, logd, logd_jacs) as _transform_det_jac returns it — accumulate grad = grad * jac and logd_grad contributions per transform (mirroring how logpdf_gradient walks its jacobians), returning (base_logpdf + logd, chain_ruled_grad + total_logd_grad).
Same shape as census finding A1 (#1331-01): a code path that has never run end-to-end, dead on arrival.
TransformedMessage.factor_gradient (composed_transform.py:360-378, main @ 21288bb) unpacks four values from self._transform_det_jac(x) but _transform_det_jac (composed_transform.py:281-289) returns three: (x, logd, logd_jacs) where logd_jacs is a list of (logd_grad, jac) tuples. Any call raises ValueError: not enough values to unpack (expected 4, got 3).
Even past the unpack, the body is wrong for the actual return structure: the for jac in reversed(jacs): grad = grad * jac loop and the final grad + logd_grad assume flat lists that _transform_det_jac does not produce.
Exposure: zero production callers in autofit/ — the factor_gradient in graphical/laplace/line_search.py is an unrelated FactorApproximation-level interface, and the only exp_factor use is one test. So this is dead code that crashes if ever exercised; no live search or EP path is affected.
The fix (two options, adjudicate with #1498): (1) repair it to be the physical-density gradient companion of factor and add it to the #1497 property sweep; (2) delete it — if the #1498/#1500 adjudication lands a single-source log_density contract, a hand-rolled second gradient path is exactly the kind of duplicate this cleanup exists to remove. Do not fix silently ahead of the #1498 contract decision.
Overview
TransformedMessage.factor_gradient(autofit/messages/composed_transform.py:360-378) crashes on any call: it unpacks four values fromself._transform_det_jac(x), which returns three. Found 2026-08-19 while running the caller inventory that #1498 asked for. It has zero production callers inautofit/, so no live search or EP path is affected — this is dead code that fails on first use, the same "never run end-to-end" shape as #1331 finding 01 (LogGaussianPrior.with_limits).Reproducer on
main@21288bb:The offending unpack:
_transform_det_jac(composed_transform.py:281-289) returns(x, logd, logd_jacs)wherelogd_jacsis a list of(logd_grad, jac)tuples. Even past the unpack, the body's flat-list handling (thefor jac in reversed(jacs): grad = grad * jacloop and the finalgrad + logd_grad) does not match the structure_transform_det_jacactually produces.Plan
factor: valuebase.logpdf(T(x)) + log_det, gradient chain-ruled through the transform stack plus thelog_detgradient. Add it to the test: property-based correctness sweep over every Prior subclass #1497 property sweep, checked against a numerical derivative offactor.log_densitycontract, a second hand-rolled gradient path is exactly the duplication that design exists to remove.External verification requested
As with the #1330/#1331 census, this finding is AI-generated. Before any fix, please sanity-check (a) the reproducer, and (b) the zero-production-callers claim — note the
factor_gradientinautofit/graphical/laplace/line_search.pyis an unrelatedFactorApproximation-level interface, not this method, and the onlyexp_factoruse is a single test.Detailed implementation plan
Affected Repositories
Branch Survey
Suggested branch:
fix/transformed-message-factor-gradientImplementation Steps
factor_gradientto consume(x, logd, logd_jacs)as_transform_det_jacreturns it — accumulategrad = grad * jacandlogd_gradcontributions per transform (mirroring howlogpdf_gradientwalks its jacobians), returning(base_logpdf + logd, chain_ruled_grad + total_logd_grad).test_autofit/mapper/prior/test_prior_properties.py(test: property-based correctness sweep over every Prior subclass #1497 sweep) with afactor_gradient-vs-numerical-derivative-of-factorproperty over everyTransformedMessage-wrapped prior.factor_gradient(and re-checkexp_factor's single test usage while in the file).Key Files
autofit/messages/composed_transform.py—factor_gradient(360-378),_transform_det_jac(281-289), correct siblingfactor(343-358)test_autofit/mapper/prior/test_prior_properties.py— test: property-based correctness sweep over every Prior subclass #1497 property sweep to extend if repairedOriginal Prompt
Click to expand starting prompt
PyAutoMind
draft/bug/priors/16_transformed_message_factor_gradient_unpack.md:Same shape as census finding A1 (#1331-01): a code path that has never run end-to-end, dead on arrival.
TransformedMessage.factor_gradient(composed_transform.py:360-378, main @21288bb) unpacks four values fromself._transform_det_jac(x)but_transform_det_jac(composed_transform.py:281-289) returns three:(x, logd, logd_jacs)wherelogd_jacsis a list of(logd_grad, jac)tuples. Any call raisesValueError: not enough values to unpack (expected 4, got 3).Even past the unpack, the body is wrong for the actual return structure: the
for jac in reversed(jacs): grad = grad * jacloop and the finalgrad + logd_gradassume flat lists that_transform_det_jacdoes not produce.Exposure: zero production callers in
autofit/— thefactor_gradientingraphical/laplace/line_search.pyis an unrelated FactorApproximation-level interface, and the onlyexp_factoruse is one test. So this is dead code that crashes if ever exercised; no live search or EP path is affected.The fix (two options, adjudicate with #1498): (1) repair it to be the physical-density gradient companion of
factorand add it to the #1497 property sweep; (2) delete it — if the #1498/#1500 adjudication lands a single-sourcelog_densitycontract, a hand-rolled second gradient path is exactly the kind of duplicate this cleanup exists to remove. Do not fix silently ahead of the #1498 contract decision.