Supply the _costabs Dual overload SupernodalLU's matching documents - #1118
Merged
ChrisRackauckas merged 1 commit intoJul 27, 2026
Merged
Conversation
`src/SupernodalLU/matching.jl` says of `_costabs`:
Value types with no exact Float64 conversion (e.g. ForwardDiff.Dual) get
an overload in the package extension that extracts the primal value.
No such overload exists. It was left behind when the solver was vendored from
PurePardiso.jl, so the comment documents behaviour the package does not have.
MC64 matching decides its max-product assignment in Float64, so `_costabs`
needs a real magnitude per entry. Without the overload, factoring a `Dual`
matrix whose diagonal is missing or weak - exactly when `matching = :auto`
engages - throws:
MethodError: no method matching Float64(::ForwardDiff.Dual{Nothing, Float64, 1})
_costabs at matching.jl:206 [inlined]
mc64_matching(::SparseMatrixCSC{Dual{Nothing, Float64, 1}, Int64})
Only the *ordering* of candidate pivots comes from these numbers; the
factorization itself stays in Dual arithmetic, so taking the primal loses
nothing.
Reachable today through `LinearSolve.__init`. The public path is unaffected:
`__dual_init` strips duals before any algorithm sees them, so `solve` and
`init` on a Dual sparse problem factor in Float64 and never reach this code.
`_scalarval` needs no such fix - returning `NaN` for a Dual is intended and
documented there (`anorm` is informational; thresholds stay in native
arithmetic).
Test: an anti-diagonal matrix, which has no structural diagonal at all so
matching always engages, checked in both value and derivative against the
closed form. It fails on the unfixed code at the `snlu` call.
GROUP=Core passes.
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 08:42
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.
Follow-up to the SupernodalLU series, and the first half of the dual-number question from #1064.
The bug
src/SupernodalLU/matching.jl:203-206documents an overload that does not exist:grep -rn _costabs src ext libreturns only the definition and its two call sites — the extension overload was left behind when the solver was vendored from PurePardiso.jl. So the comment describes behaviour the package does not have.MC64 matching runs its max-product assignment in
Float64, so it needs a real magnitude per entry. Factoring aDualmatrix whose diagonal is missing or weak — exactly whenmatching = :autoengages — therefore throws:Measured before/after on an anti-diagonal matrix (
needs_matching = true):Scope
Only the ordering of candidate pivots is decided from these numbers; the factorization stays in Dual arithmetic, so taking the primal loses nothing.
This is reachable today through
LinearSolve.__init. The public path is unaffected —__dual_initstrips duals before any algorithm sees them, sosolve/initon a Dual sparse problem factor inFloat64(verified: innerAeltypeFloat64, derivative correct to 5.7e-15 relative). This PR does not change how duals are routed, and deliberately does not addSupernodalLUFactorizationto_use_direct_dual_solve: I measured native Dual factorization at 1.7× / 3.9× / 15.3× the primal cost at N = 1 / 4 / 8 partials, which is the same shape as the RFLU regression in #1052._scalarvalneeds no equivalent fix — returningNaNfor a Dual is intended and documented there (anormis informational; thresholds stay in native arithmetic).Test
test/Core/forwarddiff_overloads.jlgains a case on an anti-diagonal matrix — no structural diagonal at all, so matching always engages — asserting both the value and the derivative against the closed form. It fails on the unfixed code at thesnlucall.GROUP=Corepasses locally (rc=0); the new testset passes 4/4.🤖 Generated with Claude Code
https://claude.ai/code/session_017rr42T1vFRRT8D7DMAmJzf