Make the out-of-place jacobian's return type inferrable - #229
Merged
ChrisRackauckas merged 1 commit intoAug 3, 2026
Merged
Conversation
`finite_difference_jacobian` (out-of-place) inferred to `Any`. Its per-color closures assign variables that also exist in the enclosing function's scope — `epsilon`, `vecfx`, `x_save`, `dx`, ... — so those became captured variables with several assignment sites, which closure conversion boxes. Boxed captures read back as `Any`, and that `Any` propagates through `mapreduce(calculate_Ji_*, hcat, ...)` all the way to the returned matrix. Declare the closures' temporaries `local`, rename the ones that had to stay distinct from a capture, and give `vecfx`/`rows_index`/`cols_index` a single assignment site each. No behavioral change: the closures only ever wrote those outer variables by accident, and no branch read the values back. This surfaced downstream as `@inferred SciMLBase.init(prob, alg)` failing for a `TrustRegion(autodiff = AutoFiniteDiff())` `NonlinearProblem`, whose Jacobian cache is typed by this return value (SciML/NonlinearSolve.jl#1092). Static-array inputs remain uninferrable: the `hcat` accumulator gains an `SMatrix` column per color, so its type depends on `maximum(colorvec)`. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MoeiVWqAEFJQK22kiXu48f
Author
|
CI: The failure is unrelated to this diff:
|
ChrisRackauckas
marked this pull request as ready for review
August 3, 2026 17:16
ChrisRackauckas
added a commit
to SciML/NonlinearSolve.jl
that referenced
this pull request
Aug 3, 2026
`inference_tests__item1.jl` only exercised in-place problems, so the out-of-place instability in #1092 went unnoticed: prob = NonlinearProblem((u, p) -> u .^ 2 .- 2.0, [1.0]) @inferred SciMLBase.init(prob, TrustRegion(autodiff = AutoFiniteDiff())) inferred the cache's `jac_cache`/`descent_cache` to open `where`-bounds. The root cause was FiniteDiff's out-of-place `finite_difference_jacobian` returning `Any` (JuliaDiff/FiniteDiff.jl#229); the Jacobian cache is typed by that return value, so the whole solver cache went with it. Add the out-of-place problem to the existing testset and raise the FiniteDiff lower bound to the release carrying the fix, so Downgrade CI resolves a version where these tests hold. Claude-Session: https://claude.ai/code/session_01MoeiVWqAEFJQK22kiXu48f Co-authored-by: ChrisRackauckas-Claude <accounts@chrisrackauckas.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ignore until reviewed by @ChrisRackauckas.
Closes SciML/NonlinearSolve.jl#1092
Problem
The out-of-place
finite_difference_jacobianinfers toAny:The
calculate_Ji_forward/calculate_Ji_central/calculate_Ji_complexclosures assignvariables that also exist in the enclosing function's scope (
epsilon,vecfx,x_save,dx, ...). An inner function assigning a name that exists in the enclosing scope binds theouter variable, so those became captured variables with several assignment sites — which
closure conversion boxes. Reading a boxed capture gives
Any, and that propagates throughmapreduce(calculate_Ji_*, hcat, ...)to the returned matrix.rows_index/cols_indexhad the same problem via the comprehension that reassigns them.
@report_opton the FiniteDiff path, before:Fix
Declare the closures' temporaries
localso they stop aliasing the enclosing function'svariables, rename the few that had to stay distinct from a genuine capture, and give
vecfx/rows_index/cols_indexa single assignment site each.No behavioral change — the closures only ever wrote those outer variables incidentally, and
no branch read the values back afterwards.
After:
Static-array inputs stay uninferrable, which is a separate (and harder) problem: the
hcataccumulator gains one
SMatrixcolumn per color, so its type depends on the runtime valueof
maximum(colorvec). Noted in a comment next to the new test.Downstream motivation
This is the root cause of SciML/NonlinearSolve.jl#1092 — the
Jacobian cache is typed by this return value, so the whole solver cache inferred to an open
where-bound:Testing
Added an inference regression test to
test/out_of_place_tests.jlcovering all threefdtypes. Ran the fullGROUP=Coresuite locally on Julia 1.10.11 — green:(the one
Brokenis pre-existing)🤖 Generated with Claude Code
https://claude.ai/code/session_01MoeiVWqAEFJQK22kiXu48f