Repository navigation
Fix build_J_W cache init for structured jac_prototypes - #3942
ChrisRackauckas merged 1 commit into
Conversation
d8cbb9e (SciML#3833) initializes the J/W caches allocated in build_J_W with ones so factorizations never see undef values, but fill! on LinearAlgebra's banded structured matrices (Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal) throws for any nonzero value since the off-band entries are constrained to zero. Any structured jac_prototype on the default-linsolve path then errored at cache construction, e.g. solve(ODEProblem(ODEFunction(f; jac, jac_prototype = Diagonal(zeros(2))), ones(2), (1.0, 10.0)), Rosenbrock23()). Add a fill_stored! helper that writes only the stored bands of the structured types and falls back to fill! otherwise, and use it for the non-sparse initialization in build_J_W. Dense and sparse prototypes are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
|
Local verification (Julia 1.12, current registry, SciMLBase 3.36.0): Bisect evidence — reproducer on Same reproducer on unmodified master: With this PR — reproducer: Previously failing downstream file
|
|
Status check from an independent triage run today (2026-08-08, master Three open drafts fix the same Downstream impact. The using OrdinaryDiffEq, OrdinaryDiffEqRosenbrock, LinearAlgebra
const n = 10
f(du, u, p, t) = (du .= 0;
du[2:(end - 1)] .= p[1] .* (u[3:end] .- 2 .* u[2:(end - 1)] .+ u[1:(end - 2)]);
nothing)
u0 = sin.(range(0, 1, length = n))
jp = Tridiagonal(similar(u0, n - 1), similar(u0), similar(u0, n - 1))
prob = ODEProblem(ODEFunction(f; jac_prototype = jp), u0, (0.0, 1.0), [0.42])
solve(prob, Rodas4P(), saveat = 0.1)Regression range confirms this PR's bisect: One measured difference between the three PRs, for whoever picks. This PR's Independent verification of an equivalent patch (band-writing
No fourth PR opened. Suggest merging whichever of the three you prefer (my read: #4072 or #3913 for the |
Note
This PR should be ignored until reviewed by @ChrisRackauckas.
Problem
lib/DiffEqBase's[Downstream]test group fails on master intest/downstream/default_linsolve_structure.jl:thrown from
build_J_Watlib/OrdinaryDiffEqDifferentiation/src/derivative_utils.jlwhen solving anODEProblemwhosejac_prototypeis a structured matrix (Diagonal,Tridiagonal, ...) with a default (factorization) linsolve, e.g.Root cause
Bisected to d8cbb9e ("Initialize sparse Jacobian caches with stored values", #3833). That commit initializes the
J/Wcaches allocated inbuild_J_Wwith ones so factorizations never see undef garbage:But LinearAlgebra's
fill!on banded structured matrices (Diagonal,Bidiagonal,Tridiagonal,SymTridiagonal) throws for any nonzero value because the off-band entries are constrained to zero. So every structured (non-sparse, non-dense)jac_prototypeon the default-linsolve path now errors at cache construction. Verified by running the reproducer on d8cbb9e~1 with the same current dependency versions: it passes there and fails on master, so this is a code regression from #3833, not registry drift (the failure only surfaced later because the monorepo's own CI lanes for #3833 did not run the DiffEqBase Downstream group).Fix
Add a
fill_stored!helper that writes only the stored bands of the LinearAlgebra structured matrix types and falls back tofill!otherwise, and use it inbuild_J_W's non-sparse initialization. Semantics for dense and sparse prototypes are unchanged.Tests
fill_stored!unit tests and a structured-jac_prototypeinit/solve regression test inlib/OrdinaryDiffEqDifferentiation/test/nzval_helpers_tests.jl(asserts theJ/Wcaches are initialized with stored ones, mirroring the Initialize sparse Jacobian caches with stored values #3833 testset, and that the solve completes).lib/DiffEqBase/test/downstream/default_linsolve_structure.jlpasses locally with this change (output in the PR conversation).This resolves the red
lib/DiffEqBase [Downstream]lane seen on #3692 (which merely surfaced the pre-existing master failure).🤖 Generated with Claude Code