Bound the UMFPACK-backed sparse cachevals to the eltypes UMFPACK has - #1122
Merged
ChrisRackauckas merged 1 commit intoJul 27, 2026
Merged
Conversation
`init(LinearProblem(A, b), nothing)` throws for a Float32 sparse matrix on
main:
defaultalg(...) = DefaultLinearSolver(DefaultAlgorithmChoice.KLUFactorization, ...)
init(...) = MethodError: no method matching
SparseArrays.UMFPACK.UmfpackLU(::SparseMatrixCSC{Float32, Int64})
Note the algorithm that throws is not the one selected. `_init_default_cacheval`
(src/default.jl, @generated) emits an unconditional `init_cacheval` for every
slot - only the three Krylov slots are guarded - so a too-permissive
`init_cacheval` on an *unselected* algorithm still breaks `init`. KLU is
chosen; the `LUFactorization` slot is built first and throws.
`LUFactorization`'s and `UMFPACKFactorization`'s sparse cachevals are bounded
by `BLASELTYPES = Union{Float32, Float64, ComplexF32, ComplexF64}`, but
UMFPACK's `UmfpackLU` exists only for Float64/ComplexF64 (SuiteSparse's
`UMFVTypes`), so the bound is too wide by exactly Float32/ComplexF32. KLU
already does this correctly with `T <: KLU.KLUTypes`.
Bound them to a local `UMFPACKELTYPES` instead. The additional `BLASELTYPES`
method is not redundant: the old methods' `is_cusparse(A)` branch is the only
`LUFactorization`+CuSparse `init_cacheval` in the tree, so narrowing alone
would have silently dropped Float32 cuDSS support. `UMFPACKELTYPES` is defined
locally rather than reusing `SparseArrays.UMFPACK.UMFVTypes`, which is a
non-public internal.
Float32/ComplexF32 sparse now resolve to the `KLUFactorization` slot, which
`algchoice_to_alg` maps to `PureKLUFactorization()` - pure Julia, and the only
thing in the tree that supports these eltypes. It is a hard dependency, so no
extra `using` is needed.
Two separate breakages, both bisected:
0ce03c2 2025-05-22
widened the cachevals to `T <: BLASELTYPES` while adding ComplexF64.
Broke Float32 sparse for Sparspak users; everyone else still hit the
"using Sparspak required" error in `defaultalg` first, so it stayed hidden.
52ebf25 (SciML#1037) 2026-06-13
removed that early error, exposing the breakage to everyone.
Measured before/after, all 10 eltype/index combinations:
before: Float32 and ComplexF32 (Int64 and Int32) all `INIT THREW`
after: all 10 solve, residual 0, eltype preserved (no Float64 promotion)
Explicitly-requested `LUFactorization`/`UMFPACKFactorization` on Float32
sparse remain unsupported, as before this change - they now fail the way
SuiteSparse KLU already does for an unsupported eltype rather than with a
`MethodError`. Making them work would mean silent Float32->Float64 promotion,
which is a behavior addition, not a bug fix.
`Test.detect_ambiguities` on the extension: 12 before, 12 after.
GROUP=Core passes on the fixed tree and on unmodified main.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017rr42T1vFRRT8D7DMAmJzf
ChrisRackauckas
marked this pull request as ready for review
July 27, 2026 11:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ignore until reviewed by @ChrisRackauckas.
Found while checking what the sparse default does per element type. Reproduces on a pristine checkout of
main.The bug
The algorithm that throws is not the one selected.
_init_default_cacheval(src/default.jl,@generated) emits an unconditionalinit_cachevalfor every slot — only the three Krylov slots are guarded — so a too-permissiveinit_cachevalon an unselected algorithm still breaksinit. KLU is chosen; theLUFactorizationslot is constructed first and throws.LUFactorization's andUMFPACKFactorization's sparse cachevals are bounded byBLASELTYPES = Union{Float32,Float64,ComplexF32,ComplexF64}, butUmfpackLUexists only forFloat64/ComplexF64(SuiteSparse'sUMFVTypes) — too wide by exactlyFloat32/ComplexF32. KLU already gets this right withT <: KLU.KLUTypes.4 of 10 eltype/index combinations fail on unmodified
main:The fix
Bound them to a local
UMFPACKELTYPES = Union{Float64, ComplexF64}.Two notes on the shape of it:
BLASELTYPESmethod is not redundant. The old methods'is_cusparse(A) → cudss_loaded(A) ? lu_instance(A) : nothingbranch is the onlyLUFactorization+CuSparseinit_cachevalin the tree (LinearSolveCUDAExtdefines none), so narrowing alone would have silently dropped Float32 cuDSS support.UMFPACKELTYPESis defined locally rather than reusingSparseArrays.UMFPACK.UMFVTypes, which is a non-public internal.Float32/ComplexF32 sparse now resolve to the
KLUFactorizationslot, whichalgchoice_to_algmaps toPureKLUFactorization()— pure Julia, a hard dependency, and the only thing in the tree that actually supports these eltypes (SuiteSparse KLU, UMFPACK, and Sparspak all reject them).Bisect — two separate breakages
0ce03c28T <: BLASELTYPESwhile adding ComplexF64. Broke Float32 sparse for Sparspak users; everyone else still hit the "using Sparspakrequired" error indefaultalgfirst, so it stayed hidden.52ebf258(#1037)Measured at
0ce03c28^with Sparspak loaded:initOK. So this path did work once.Testing
All 10 combinations after the fix — eltype preserved, no silent Float64 promotion:
test/Core/default_algs.jlgains a 4-combination testset (Float32/ComplexF32×Int64/Int32). Confirmed it executes:Default Alg Testsgoes 89 → 109 tests.GROUP=Corepasses on the fixed tree and on unmodifiedmain— main is not independently red.Test.detect_ambiguitieson the extension: 12 before, 12 after.Out of scope
Explicitly-requested
LUFactorization/UMFPACKFactorizationon Float32 sparse remain unsupported, exactly as before this change — they now fail the way SuiteSparse KLU already does for an unsupported eltype instead of with aMethodError. Making them work would mean silent Float32→Float64 promotion (SparseArrays'ludoes this), which is a behavior addition rather than a bug fix. Happy to do it separately if you want it.Also cleared while looking: BigFloat sparse is not a second bug — the
KLUFactorizationenum slot maps toPureKLUFactorization, not SuiteSparse KLU, so it works today. AndSymmetricFloat32 sparse → CHOLMOD is fine, because CHOLMOD'sinit_cachevalreturnsnothingfor the non-Float64 case.🤖 Generated with Claude Code
https://claude.ai/code/session_017rr42T1vFRRT8D7DMAmJzf