Skip to content

Fix Sparspak symbolic reuse invalidated by per-solve dropzeros#1036

Merged
ChrisRackauckas merged 2 commits into
SciML:mainfrom
ChrisRackauckas-Claude:fix-per-solve-dropzeros-reuse-symbolic
Jun 13, 2026
Merged

Fix Sparspak symbolic reuse invalidated by per-solve dropzeros#1036
ChrisRackauckas merged 2 commits into
SciML:mainfrom
ChrisRackauckas-Claude:fix-per-solve-dropzeros-reuse-symbolic

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor

Summary

The nonstructural_zeros feature introduced in v3.85 (per-solve dropzeros mode, cache_union=false) can return sparse matrices with different nnz counts across successive Newton iterations. Sparspak's sparspaklu! reuses the symbolic factorization from the previous call without checking whether the sparsity structure has changed — unlike KLU/UMFPACK, which both guard against this. When nnz changes, sparspaklu! produces a corrupt factorization, leading to wrong linear-solve results and stalled nonlinear (e.g. BVP) iterations.

  • ext/LinearSolveSparseArraysExt.jl: Add reduced_nnz::Int and structure_changed::Bool to SparseReduction. Update reduce_operand! to track whether the per-solve dropzeros changed the matrix structure.
  • ext/LinearSolveSparspakExt.jl: Before calling sparspaklu!, check structure_changed via the DefaultLinearSolverInit cache. If the structure is unstable, skip symbolic reuse and fall back to a full sparspaklu.

Fixes the BigFloat BVP stalling regression reported in SciML/BoundaryValueDiffEq.jl#509 (comment from @ChrisRackauckas identifying FIRK test failures).

Test plan

  • test/core/nonstructural_zeros.jl — 122/122 pass
  • BigFloat + SparspakFactorization direct solve — correct results
  • BigFloat + DefaultLinearSolver (Sparspak path) — correct results
  • CI on full test suite

Note: Please ignore until reviewed by @ChrisRackauckas.

When the DefaultLinearSolver's nonstructural_zeros feature operates in
per-solve dropzeros mode (cache_union=false), successive calls to
reduce_operand! can return sparse matrices with different nnz counts.
Sparspak's sparspaklu! reuses the symbolic factorization from the
previous call without checking whether the sparsity structure matches.
This produces an invalid factorization when nnz changes, causing wrong
linear-solve results and stalled nonlinear (BVP) iterations.

Fix: track `reduced_nnz` and `structure_changed` on SparseReduction.
The Sparspak solve! reads `structure_changed` and skips symbolic reuse
(falls back to a full sparspaklu) when the structure is unstable.

Fixes regression introduced by the nonstructural_zeros feature (v3.85)
that caused BigFloat BVP solves to stall (SciML/BoundaryValueDiffEq.jl#509).

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
PureUMFPACKFactorization (added in SciML#1004) is an AbstractSparseFactorization
but was missing from resolve.jl's sparse-conversion lists, so the Re-solve
testset exercised it with a dense matrix and errored in do_factorization.
Add it alongside the other sparse LU factorizations.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor Author

Root-cause companion: #1037 makes PureKLU the default sparse LU for generic (non-BLAS) element types, so the default path for BigFloat no longer selects Sparspak at all. This PR remains valuable as defense-in-depth for users who explicitly request SparspakFactorization(). The resolve.jl PureUMFPACKFactorization test fix is shared between both PRs (identical patch, resolves cleanly when either merges).

@ChrisRackauckas ChrisRackauckas marked this pull request as ready for review June 13, 2026 09:16
@ChrisRackauckas ChrisRackauckas merged commit a815384 into SciML:main Jun 13, 2026
35 of 55 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