Early FWW Wrap MTK-Generated SciMLFunctions in SciMLProblems#4426
Early FWW Wrap MTK-Generated SciMLFunctions in SciMLProblems#4426ChrisRackauckas-Claude wants to merge 2 commits intoSciML:masterfrom
Conversation
When AutoSpecialize is active and the problem is IIP, wrap the GeneratedFunctionWrapper inside NonlinearFunction with NonlinearSolveBase.AutoSpecializeCallable at problem construction time. This erases the concrete function type (RuntimeGeneratedFunction hashes) so that different MTK models sharing the same state/parameter structure reuse compiled NonlinearSolve code instead of recompiling per model. Wrapping happens at: - NonlinearProblem construction (after process_SciMLProblem returns u0, p) - NonlinearLeastSquaresProblem construction (same) - SCCNonlinearProblem sub-problem construction Uses NonlinearSolveBase.wrapfun_iip which the ForwardDiff extension overrides with dual-aware signatures, keeping in sync with the solver's tag standardization. ODEFunction wrapping is NOT changed — DiffEqBase.promote_f already handles that at solve time. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
When AutoSpecialize is active and u0/p/t are available, wrap f with DiffEqBase.wrapfun_iip at ODEFunction construction time. This erases the GeneratedFunctionWrapper type early, before DiffEqBase.promote_f runs at solve time. Requires DiffEqBase >= 6.215.0 (SciML/OrdinaryDiffEq.jl#3335) which guards promote_f against double-wrapping already-wrapped functions. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
|
Requires SciML/NonlinearSolve.jl#891 to actually work. |
Benchmark: MTK construction-time FWW wrapping vs solver-time wrappingCompared the construction-time FWW wrapping in this PR against the baseline (AutoSpecialize set but no construction-time wrapping, relying on solver-side wrapping). Setup10 pendulum DAE models with nonlinear algebraic constraint ( Results
AnalysisNo measurable difference for this benchmark. Both paths report identical init The construction-time FWW wrapping would only produce a measurable benefit for models with genuinely different initialization equations (different nonlinear structure, not just different parameters/variable names). Where the win IS measurablePure NonlinearProblem benchmark with unique function types (not MTK, manually created structs F1-F5):
~5.5x speedup per new function type when function types actually differ. AutoSpecialize vs FullSpecialize (the meaningful comparison)
2.8x speedup per subsequent model, 2x total wall time. This is the real win from AutoSpecialize — the ODE solver path reuses compiled code via |
Summary
AutoSpecializeis active and the problem is IIP, wrapsGeneratedFunctionWrapperinsideNonlinearFunctionwithNonlinearSolveBase.AutoSpecializeCallableat construction timeRuntimeGeneratedFunctionhashes) so different MTK models reuse compiled NonlinearSolve codeNonlinearProblem,NonlinearLeastSquaresProblem, andSCCNonlinearProblemsub-problem constructionNonlinearSolveBase.wrapfun_iipso the ForwardDiff extension adds dual-aware signatures, staying in sync with the solverDiffEqBase.promote_fhandles it at solve timeContext
PR #4335 switched MTK to
AutoSpecializeby default. For ODEs,DiffEqBase.promote_fwraps in FWW at solve time. But for initializationNonlinearProblems,NonlinearSolveBase.maybe_wrap_nonlinear_fskipped wrapping because the parameters areMTKParameters(notVector{Float64}). This meant each MTK model triggered recompilation through NonlinearSolve for initialization.This PR fixes that by wrapping eagerly at construction time, when
u0andptypes are known.Verified locally
NonlinearProblemf.fisAutoSpecializeCallablecontainingFunctionWrappersWrapper✓f.ftypes ✓remakepreserves wrapping ✓Dependencies
Test plan
🤖 Generated with Claude Code