Skip to content

Add PureUMFPACKFactorization extension (PureUMFPACK.jl)#1004

Merged
ChrisRackauckas merged 3 commits into
SciML:mainfrom
ChrisRackauckas-Claude:add-pureumfpack-ext
Jun 11, 2026
Merged

Add PureUMFPACKFactorization extension (PureUMFPACK.jl)#1004
ChrisRackauckas merged 3 commits into
SciML:mainfrom
ChrisRackauckas-Claude:add-pureumfpack-ext

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in PureUMFPACKFactorization <: AbstractSparseFactorization that wraps the pure-Julia UMFPACK port PureUMFPACK.jl, mirroring the existing SuiteSparse UMFPACKFactorization and the pure-Julia PureKLUFactorization.

  • PureUMFPACK is a weak dependency; the algorithm is provided by a new LinearSolvePureUMFPACKExt extension and is only available after using PureUMFPACK.
  • The SuiteSparse UMFPACKFactorization and the default polyalgorithm are unchanged — this is purely additive/opt-in.

Details

  • src/factorization.jl: PureUMFPACKFactorization(; reuse_symbolic = true, check_pattern = true) struct + docstring + generic init_cacheval(...) = nothing stub, placed next to KLUFactorization/PureKLUFactorization.
  • src/LinearSolve.jl: export PureUMFPACKFactorization.
  • Project.toml: PureUMFPACK added to [weakdeps], [compat], [extras], the test target, and the new extension LinearSolvePureUMFPACKExt = ["PureUMFPACK", "SparseArrays"].
  • ext/LinearSolvePureUMFPACKExt.jl: init_cacheval (returns an empty typed PureLU for sparse inputs so the cache field type can hold the real factor) and SciMLBase.solve! modeled on the UMFPACKFactorization/PureKLUFactorization methods.

Caveat on caching semantics

PureUMFPACK exposes splu (full factorization) and solve/\; it has no in-place numeric-refactorization (lu!-style) API and no symbolic-reuse API. So each fresh solve (cache.isfresh) refactorizes via splu. reuse_symbolic/check_pattern are accepted for API parity with UMFPACKFactorization but cannot skip the symbolic step here; this is documented in the docstring and code comments. Cached factorizations are still reused across solves that only change the right-hand side. Singular factors (a zero on U's diagonal under check = false) are mapped to ReturnCode.Infeasible.

Registration

PureUMFPACK.jl is not yet registered in the General registry, so test/pureumfpack.jl adds it via Pkg.add(url = "https://github.com/SciML/PureUMFPACK.jl.git") before running.

Tests

test/pureumfpack.jl (wired into the Core/All group of runtests.jl) verifies the extension loads (Base.get_extension(...) !== nothing), a square sparse solve, caching + value-only refactor, reuse_symbolic = false, and singular -> Infeasible. Run locally on Julia 1.10, all passing.


Please ignore until reviewed by @ChrisRackauckas.

🤖 Generated with Claude Code

@ChrisRackauckas

Copy link
Copy Markdown
Member

This should also get an opt-out #1023

@ChrisRackauckas

Copy link
Copy Markdown
Member

No opt out since it uses blas

Adds an opt-in `PureUMFPACKFactorization <: AbstractSparseFactorization`
backed by the pure-Julia UMFPACK port PureUMFPACK.jl, mirroring the existing
SuiteSparse `UMFPACKFactorization` and `PureKLUFactorization`. PureUMFPACK is a
weak dependency loaded via the new `LinearSolvePureUMFPACKExt` extension; the
SuiteSparse `UMFPACKFactorization` and the default polyalgorithm are unchanged.

PureUMFPACK has no in-place numeric-refactorization (`lu!`-style) API, so each
fresh solve refactorizes via `splu`; `reuse_symbolic`/`check_pattern` are
accepted for API parity but cannot skip the symbolic step. Singular factors
(zero on U's diagonal under `check=false`) map to `ReturnCode.Infeasible`.

PureUMFPACK.jl is not yet registered in General, so the test adds it by URL.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas ChrisRackauckas marked this pull request as ready for review June 11, 2026 08:52
@ChrisRackauckas ChrisRackauckas merged commit fcf2fc2 into SciML:main Jun 11, 2026
35 of 53 checks passed
ChrisRackauckas-Claude pushed a commit to ChrisRackauckas-Claude/LinearSolve.jl that referenced this pull request Jun 13, 2026
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 added a commit that referenced this pull request Jun 13, 2026
* Test PureUMFPACKFactorization with a sparse matrix in resolve.jl

PureUMFPACKFactorization (added in #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>

* Default to PureKLU for generic-eltype sparse LU instead of Sparspak

For non-BLAS sparse element types (e.g. BigFloat), the default sparse LU
was SparspakFactorization, which requires `using Sparspak` and whose
symbolic-factorization reuse produces invalid factorizations when the
sparsity pattern changes across solves (the per-solve dropzeros of the
nonstructural_zeros path). That manifested as stalled BVP Newton
iterations for BigFloat problems (SciML/BoundaryValueDiffEq.jl#509).

PureKLU is a pure-Julia, hard dependency that factors any `Number`
element type and correctly re-analyzes on pattern changes, so make it the
default sparse LU for generic eltypes. Benchmarks (BigFloat, n=10..200)
show PureKLU is competitive-to-faster than Sparspak (e.g. n=200: 0.219s
vs 0.307s single solve) with comparable accuracy, and it needs no extra
`using`.

- Generic-eltype `init_cacheval` for PureKLU and SparseColumnPivotedQR so
  the default polyalgorithm's KLU and SPQR slots are concretely typed for
  non-BLAS eltypes (the latter is required for the singular-LU QR fallback
  to `setfield!` a real factorization).
- `defaultalg(::AbstractSparseMatrixCSC)` for square systems now selects
  the KLU (PureKLU) slot.

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

---------

Co-authored-by: Chris Rackauckas (Claude) <accounts@chrisrackauckas.com>
ChrisRackauckas added a commit that referenced this pull request Jun 13, 2026
* Fix Sparspak symbolic reuse with per-solve dropzeros

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>

* Test PureUMFPACKFactorization with a sparse matrix in resolve.jl

PureUMFPACKFactorization (added in #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>

---------

Co-authored-by: Chris Rackauckas (Claude) <accounts@chrisrackauckas.com>
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