Skip to content

Evaluate f_info for real in Gauss-Newton init instead of zero-filling#234

Open
Sumu004 wants to merge 1 commit into
patrick-kidger:mainfrom
Sumu004:fix/gauss-newton-init-zeroed-closure
Open

Evaluate f_info for real in Gauss-Newton init instead of zero-filling#234
Sumu004 wants to merge 1 commit into
patrick-kidger:mainfrom
Sumu004:fix/gauss-newton-init-zeroed-closure

Conversation

@Sumu004

@Sumu004 Sumu004 commented Jul 7, 2026

Copy link
Copy Markdown

Thanks @NiallOswald for the root-cause tracing in #229 — this implements what you diagnosed.

AbstractGaussNewton.init filled state.f_info with zeros via tree_full_like, which also zeroes any data closed over inside f_info.jac (the jax.linearize capture). ClassicalTrustRegion.predict_reduction calls f_info.jac.mv(...) unconditionally on every step, including the first, so a residual function that's nan-sensitive at a zeroed closure value produces a real nan that contaminates the JVP — even though the accept/reject decision itself is separately forced correct on the first step. GaussNewton (no trust region) is unaffected; LevenbergMarquardt/InverseLevenbergMarquardt both inherit the same init and are.

Fix: call _make_f_info directly in init instead of faking its shape with zeros — exactly what step already does every other iteration. Costs one extra linearization at init.

I don't have a working JAX install, so I've verified this by tracing the exact call paths rather than running it — would appreciate a maintainer confirming against the real reproducer.

Fixes #229

…closures

AbstractGaussNewton.init filled state.f_info with a same-shaped
placeholder of zeros (tree_full_like(f_info_struct, 0, ...)) rather
than a real evaluation of _make_f_info. This silently zeroed out any
values closed over inside f_info.jac too -- in particular, the point
at which jax.linearize captured its linearization inside _make_f_info.

ClassicalTrustRegion.predict_reduction calls f_info.jac.mv(y_diff)
unconditionally on every call to step -- including the very first,
where f_info is still this placeholder -- even though the decision it
feeds into (accept) is separately forced to True via
accept | first_step. If the residual function involves an operation
that is nan-sensitive at zero (e.g. a division), the corrupted
placeholder produces a genuine nan in predicted_reduction's value,
which then contaminates the JVP even though the decision itself is
unaffected by it. GaussNewton (no trust region) doesn't hit this,
which is why the original report singles out
LevenbergMarquardt/InverseLevenbergMarquardt specifically -- both
inherit this same init with no override.

Fix: call _make_f_info(fn, y, args, tags, jac) directly in init (y is
already available as a parameter), matching what step already does for
subsequent iterations, instead of eval_shape + zero-fill. This costs
one extra linearization at init in exchange for never fabricating
ill-defined closure data.

Fixes patrick-kidger#229
@Sumu004 Sumu004 changed the title fix(gauss_newton): evaluate f_info for real in init, don't zero-fill closures Evaluate f_info for real in Gauss-Newton init instead of zero-filling Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NaNs encountered when taking the quotient of two arrays

1 participant