Skip to content

Commit f0de8b6

Browse files
Fix @inferred type instability from JacReuseState Union type (#3399)
_make_jac_reuse_state previously returned Union{Nothing, JacReuseState{T}} because it conditionally allocated based on max_jac_age > 1. This caused type instability in the Rosenbrock cache's JRType parameter, which propagated to ODESolution return types and broke @inferred tests in: - OrdinaryDiffEqRosenbrock: DAE Rosenbrock AD Tests - OrdinaryDiffEqNonlinearSolve: Mass Matrix Tests with Singular Mass Matrices Fix: always allocate JacReuseState regardless of max_jac_age. When max_jac_age <= 1, the age check in _rosenbrock_jac_reuse_decision triggers every step anyway, so behavior is unchanged. Co-authored-by: ChrisRackauckas-Claude <accounts@chrisrackauckas.com>
1 parent a6c6e19 commit f0de8b6

2 files changed

Lines changed: 9 additions & 10 deletions

File tree

lib/OrdinaryDiffEqRosenbrock/src/algorithms.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,10 @@ for (Alg, desc, refs, is_W) in [
9696
# Rosenbrock23/32 are low-order methods typically used on small/warm-up
9797
# problems where J evaluation is cheap and per-step overhead dominates.
9898
# Default them to max_jac_age = 1 which effectively disables reuse (the
99-
# cache allocates `nothing` for jac_reuse and the decision function
100-
# short-circuits through do_newJW, matching master behavior). Users can
101-
# still opt into reuse with an explicit max_jac_age kwarg. Higher-order
102-
# W-methods (Rodas23W, ROS34PW*, Rodas4P2, Rodas5P/Pe/Pr, Rodas6P) keep
103-
# the full reuse default which wins on PDE workloads.
99+
# age check in _rosenbrock_jac_reuse_decision triggers every step).
100+
# Users can still opt into reuse with an explicit max_jac_age kwarg.
101+
# Higher-order W-methods (Rodas23W, ROS34PW*, Rodas4P2, Rodas5P/Pe/Pr,
102+
# Rodas6P) keep the full reuse default which wins on PDE workloads.
104103
default_max_jac_age = (Alg === :Rosenbrock23 || Alg === :Rosenbrock32) ? 1 : 20
105104
@eval begin
106105
@doc $(

lib/OrdinaryDiffEqRosenbrock/src/rosenbrock_caches.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ end
4141
"""
4242
_make_jac_reuse_state(dtgamma, max_jac_age)
4343
44-
Allocate a `JacReuseState` only when reuse is meaningfully enabled
45-
(`max_jac_age > 1`). When `max_jac_age ≤ 1` every accepted step would
46-
recompute J anyway, so return `nothing` to let the decision function
47-
take the master-compatible fast path through `do_newJW`.
44+
Always allocate a `JacReuseState` to keep cache types concrete and avoid
45+
Union-type instability that breaks `@inferred` tests. When `max_jac_age ≤ 1`
46+
the age check in `_rosenbrock_jac_reuse_decision` triggers every step anyway,
47+
so the overhead is negligible.
4848
"""
4949
@inline function _make_jac_reuse_state(dtgamma, max_jac_age::Int)
50-
return max_jac_age > 1 ? JacReuseState(dtgamma, max_jac_age) : nothing
50+
return JacReuseState(dtgamma, max_jac_age)
5151
end
5252

5353
# Fake values since non-FSAL

0 commit comments

Comments
 (0)