Skip to content

Make ADTypes the NLsolveJL autodiff interface, deprecate the Symbols - #1212

Merged
ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:fix-nlsolve5-autodiff
Aug 29, 2026
Merged

ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:fix-nlsolve5-autodiff

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Aug 29, 2026 •

Copy link
Copy Markdown
Member

Ignore this PR until it has been reviewed by @ChrisRackauckas.

What / why

NLsolveJL documented autodiff as ":central, :forward, or ADTypes". NLsolve 5.0.0 (NLSolversBase 8) switched its own autodiff keyword from Symbol to ADTypes.AbstractADType, so OnceDifferentiable(f!, u0, resid; autodiff = :central) now throws a TypeError and the Wrappers group fails on master (https://github.com/SciML/NonlinearSolve.jl/actions/runs/33251921454).

Rather than translate the Symbols at the call site, this makes ADTypes the interface, matching the rest of NonlinearSolve:

  • The default is now AutoFiniteDiff(; fdtype = Val(:central)) — exactly what :central meant, and also NLSolversBase 8's own default, so numerics are unchanged.
  • :central / :forward are normalized to AutoFiniteDiff(; fdtype = Val(:central)) / AutoForwardDiff() in the constructor, with a Base.depwarn. They keep working; alg.autodiff is now always an AbstractADType.
  • The extension drops both isa Symbol branches and passes the backend straight to NLsolve, which drives DifferentiationInterface generically.

Non-breaking, so 4.28.1 → 4.29.0. Actually removing the Symbols is a 5.0 change.

Why not just unify onto construct_extension_jac

The obvious cleanup — delete the "let NLsolve do the AD" branch and always use NonlinearSolveBase.construct_extension_jac, like CMINPACK and friends — does not work. I tried it; it fails the existing forward_ad_tests__item1.jl two ways:

Scalar AD: Error During Test at test/Wrappers/forward_ad_tests__item1.jl:22
  MethodError: no method matching setindex(::Float64, ::Float64, ::Int64)
    [1] setindex(::Float64, ::Float64, ::Int64)
      @ FiniteDiff ~/.julia/packages/FiniteDiff/Z4dvL/src/FiniteDiff.jl:45

Jacobian: Error During Test at test/Wrappers/forward_ad_tests__item1.jl:41
  DimensionMismatch: arrays could not be broadcast to a common size: a has axes Base.OneTo(4) and b has axes Base.OneTo(2)

construct_extension_jac is broken for scalar u0 (hands a bare Real to DI.jacobian) and for non-vector u0 (feeds the flattened u to prob.f, which still expects the original shape). Both are pre-existing and affect every wrapper — they are simply unreachable today because each wrapper short-circuits that path on its default autodiff. Filed separately with a standalone MWE: #1215. This PR routes around it; construct_extension_jac is still used when the problem supplies jac or jac_prototype.

Verification

GROUP=Wrappers julia --project -e 'using Pkg; Pkg.test()', Julia 1.12.4 linux, resolving NLsolve v5.0.1 / NLSolversBase v8.0.1.

Before (unmodified master c498eaf):

Scalar AD: Error During Test at test/Wrappers/forward_ad_tests__item1.jl:22
  TypeError: in keyword argument autodiff, expected ADTypes.AbstractADType, got a value of type Symbol
Jacobian: Error During Test at test/Wrappers/forward_ad_tests__item1.jl:41
  TypeError: in keyword argument autodiff, expected ADTypes.AbstractADType, got a value of type Symbol
Test Summary: | Pass Error Total Time
ForwardDiff.jl Integration | 130672 2 130674 7m18.1s
ERROR: LoadError: Some tests did not pass: 130672 passed, 0 failed, 2 errored, 0 broken.
exit=1

Pin evidence (master with [compat] NLsolve = "4.5" restored in Project.toml and test/Wrappers/Project.toml, nothing else changed) — confirms NLsolve 5 is the trigger:

⌅ [2774e3e8] + NLsolve v4.5.1
⌅ [d41bc354] + NLSolversBase v7.10.0
ForwardDiff.jl Integration | 135631 135631 7m44.6s
Nonlinear Root Finding Problems | 58 58 18.9s
     Testing NonlinearSolve tests passed
exit=0

After (this branch, NLsolve v5.0.1 / NLSolversBase v8.0.1):

ForwardDiff.jl Integration | 135631  135631  7m38.8s
Simple Scalar Problem | 13 13  38.1s
Simple Vector Problem | 12 1 13  11.7s
Power Method  | 19 6 25  27.8s
Anderson does not allocate dense Jacobian (#862) | 2 2  1.3s
LeastSquaresOptim.jl | 40 40  36.7s
FastLevenbergMarquardt.jl + CMINPACK: Jacobian Provided | 9 9  15.9s
FastLevenbergMarquardt.jl + CMINPACK: Jacobian Not Provided | 27 27  18.0s
FastLevenbergMarquardt.jl + StaticArrays | 1 1  2.1s
Steady State Problems | 16 16  10.6s
Nonlinear Root Finding Problems | 63 63  20.1s
     Testing NonlinearSolve tests passed
exit=0

Nonlinear Root Finding Problems goes 58 → 63: test/Wrappers/rootfind_tests__item2.jl now runs the solve over AutoFiniteDiff(; fdtype = Val(:central)), AutoForwardDiff(), :central and :forward, and asserts the Symbols normalize to the right backends. The discriminating tests are the pre-existing ones in forward_ad_tests__item1.jl and rootfind_tests__item2.jl; the latter never ran on CI because the group aborts after the first errored @safetestset.

The deprecation path fires as intended:

┌ Warning: `NLsolveJL(autodiff = :central)` is deprecated, pass `AutoFiniteDiff(; fdtype = Val(:central))` instead.

Runic --check and typos pass on all changed files.

Not verified

  • macOS, Julia lts and pre channels — CI covers these.
  • Other test groups (Core, Downstream, QA) were not run locally. The change touches src/extension_algs.jl and ext/NonlinearSolveNLsolveExt.jl; nothing outside NLsolveJL construction reads alg.autodiff.
  • Docs build was not run. The NLsolveJL docstring changed, so docs/make.jl is worth a look on CI.

For a reviewer to push back on

  • Default choice. I kept central finite differencing to preserve today's numerics. The consistent choice with the rest of NonlinearSolve would be autodiff = nothing (auto-select → ForwardDiff), which is faster but silently changes results for existing NLsolveJL() users. That belongs in a 5.0, not here.
  • ADTypes now go to NLsolve, not construct_extension_jac. Previously an explicit ADType took the construct_extension_jac path when prob.f.jac === nothing. It now goes to NLsolve unless jac or jac_prototype is set. That is what dodges construct_extension_jac is broken for scalar and non-vector u0 with an explicit ADTypes backend #1215, and both are DI-backed, but it does mean a jac_prototype-less sparsity setup no longer gets NonlinearSolve's coloring — there was no such path exercised in the tests.
  • Version bump. Called this minor (4.29.0) on the grounds that Symbols still work. If the changed default representation counts as breaking for anyone pattern-matching on alg.autodiff, say so.

🤖 Generated with Claude Code (model: claude-opus-5[1m])
https://claude.ai/code/session_01QTnjBBwmH4EMvxpEVNCqua

NLsolve 5.0.0 (NLSolversBase 8) accepts only ADTypes backends for
`autodiff`, so `OnceDifferentiable(f!, u0, resid; autodiff = :central)`
now throws a TypeError and the `Wrappers` group fails on master.

`NLsolveJL` already accepted ADTypes; make that the interface. The
default becomes `AutoFiniteDiff(; fdtype = Val(:central))`, which is
what `:central` meant and also NLSolversBase 8's own default, and
`:central`/`:forward` are normalized to ADTypes in the constructor with
a `Base.depwarn`. `alg.autodiff` is therefore always an
`AbstractADType`, and the extension passes it straight to NLsolve, which
drives DifferentiationInterface with any backend.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Agent-Harness: Claude Code 2.1.251
Agent-Model: claude-opus-5[1m]
Agent-Session: https://claude.ai/code/session_01QTnjBBwmH4EMvxpEVNCqua
@ChrisRackauckas-Claude ChrisRackauckas-Claude changed the title Translate NLsolveJL autodiff Symbols to ADTypes for NLsolve 5 Make ADTypes the NLsolveJL autodiff interface, deprecate the Symbols Aug 29, 2026
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review August 29, 2026 22:00
@ChrisRackauckas
ChrisRackauckas merged commit dc80c72 into SciML:master Aug 29, 2026
72 checks passed
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.

2 participants