Commit 6df384d
Add SupernodalLUFactorization (pure-Julia Schenk–Gärtner supernodal LU via PurePardiso.jl) (#1102)
* Add SupernodalLUFactorization (pure-Julia Schenk-Gartner supernodal LU)
New sparse LU algorithm backed by PurePardiso.jl (hard dependency,
following the PureKLU precedent): the supernodal left-right-looking LU
method of O. Schenk and K. Gartner (FGCS 20(3) 2004; ETNA 23 2006) --
supernodal BLAS-3 LU on the symmetrized pattern with restricted-block
static pivoting, iterative-refinement recovery, and MC64-style matching
+ scaling preprocessing. Pure Julia, no binary dependencies.
This is the strongest solver for 'more structured' (PDE-mesh-like)
sparse systems: in PurePardiso's benchmark suite it is the fastest
single-threaded full factorization on every mesh-like problem, ahead of
UMFPACK (0.5-0.8x) and KLU (up to 50x on 3D), with allocation-free
numeric refactorization on an unchanged pattern (the cache-reuse path
here). Numerically singular systems surface as ReturnCode.Infeasible
via a residual check on the (rare) perturbed-pivot path, since static
pivoting never aborts.
Loading RecursiveFactorization (e.g. for RFLUFactorization) also
activates PurePardiso's speed extension automatically, routing the
dense panel kernels through RecursiveFactorization/TriangularSolve.
Not wired into the default polyalgorithm in this PR; the natural
follow-up is routing the 'more structure' branch (currently
UMFPACKFactorization, gated on Base.USE_GPL_LIBS) to this algorithm,
which would make the entire default sparse LU stack pure Julia.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add SupernodalLUFactorization to resolve.jl sparse-input lists
The re-solve sweep iterates all AbstractSparseFactorization subtypes and
converts the test matrix to sparse only for algorithms in its explicit
lists; without this the new algorithm received a dense Matrix and its
sparse-only solve! errored (the 4 Core-group failures).
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Route the structured-sparse default LU to SupernodalLUFactorization
The default polyalgorithm's :UMFPACKFactorization slot now resolves to
the pure-Julia supernodal left-right-looking LU (same pattern as the
:KLUFactorization slot resolving to PureKLU); explicit
UMFPACKFactorization() requests are unchanged. Since both sparse LU
slots now resolve to pure-Julia solvers, the LU side of the sparse
default no longer depends on Base.USE_GPL_LIBS (previously the non-GPL
build sent structured systems to the scalar-kernel KLU path); the QR
side keeps its SPQR gating.
Occupying the UMFPACK slot gives the new algorithm the established
sparse-LU -> column-pivoted-QR fallback chain: verified locally that a
consistent singular structured system falls back and solves to residual
0.0, an inconsistent one returns the least-squares solution with the
chain's existing retcode policy, and explicit SupernodalLUFactorization()
requests still report Infeasible.
Rationale for replacing UMFPACK in the default: benchmarks (PurePardiso
results/SUMMARY.md) show it faster on every mesh-like structured system
(0.5-0.8x UMFPACK full factorization, 0.2-0.7x refactorization) with
equal-or-better residuals; the trade-off is circuit-like large-n systems
(~1.6x slower than UMFPACK on circuit_100k), which sit near the
klulike/structured boundary anyway.
test/Core/default_algs.jl passes unchanged: the DefaultAlgorithmChoice
enum and slot names are untouched, only the slot's resolved algorithm
changed.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Vendor SupernodalLU self-contained in src/SupernodalLU; drop external dep
Per review direction: the solver now lives entirely inside LinearSolve
as src/SupernodalLU (module LinearSolve.SupernodalLU) instead of
depending on an external registered package. The folder is
self-contained (needs only SparseArrays and LinearAlgebra, both hard
deps): AMD port (BSD-3, retained SPDX headers + NOTICE.md with the full
per-component lineage), orderings, symbolic analysis, MC64-style
matching, the supernodal numeric kernel, and solves. Internal names are
de-branded (snlu/snlu!/snlu_symbolic, SupernodalLUFactor,
SymbolicAnalysis); the method is cited as Schenk & Gartner (FGCS 20(3)
2004; ETNA 23 2006).
Dense-default composition is now wired directly:
LinearSolveRecursiveFactorizationExt gains TriangularSolve as a second
trigger (loads transitively with RecursiveFactorization, so activation
conditions are unchanged) and overrides the supernodal dense panel
hooks with RecursiveFactorization/TriangularSolve kernels - the same
components RFLUFactorization uses - with static-pivoting semantics
preserved via the optimistic fast path. The sparse structured default
and the dense default share one engine whenever RF is loaded.
The default-slot routing (:UMFPACKFactorization -> supernodal) is
unchanged from the previous commit. New test/Core/supernodal_lu.jl
ports the solver's internal invariant tests (factor identity,
zero-alloc refactorization, matching incl. the mass-perturbation retry,
banded fast path, threaded-vs-serial, BigFloat/Complex, multi-RHS).
GROUP=Core passes locally in full.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Factor supernode diagonal blocks through LinearSolve's dense init/solve!
Replaces the bespoke RecursiveFactorization/TriangularSolve kernel
overrides in LinearSolveRecursiveFactorizationExt (reverted, along with
the TriangularSolve weakdep) with LinearSolve's own dense machinery:
each supernode whose pivot block is at least dense_threshold (64) wide
owns a dense LinearCache over an np x np buffer, built with
SciMLBase.init at analysis time, and the block factorization runs
through SciMLBase.solve! on it.
The dense algorithm therefore follows whatever LinearSolve's dense
default resolves to for that block size and element type -
RFLUFactorization when RecursiveFactorization is loaded, MKL/Apple/
LAPACK/generic otherwise, including autotune preferences - with no
backend-specific code in the sparse solver. The new "dense_alg" keyword
on SupernodalLUFactorization overrides the choice explicitly.
(defaultalg is queried with "A === nothing", its documented dense
stand-in, because a panel-block view is not a DenseMatrix.)
Static pivoting is preserved without a backup copy: the block is copied
into the cache buffer, factored there, and copied back only if every
pivot clears eps*||A||; on rejection the panel is still pristine, so the
built-in static-perturbation kernel reruns on it directly.
Measured (poisson2d_512/poisson3d_32, thresholds 32-256, with and
without RecursiveFactorization): performance-neutral vs the built-in
kernel, +/-2%. Honest note: the removed direct-kernel wiring measured
8-18% faster refactorization, but most of that came from routing the
panel triangular solves through TriangularSolve, which is a separate
dependency and not part of the dense init/solve! API; the block-getrf
routing itself is a wash.
One documented property changed: refactorization is no longer strictly
zero-allocation - each dense block cache's solve! returns a
LinearSolution (~64 B per cached supernode, independent of problem
size). Solves remain allocation-free, and dense_threshold=typemax(Int)
restores fully allocation-free refactorization. Test and docstrings
updated to state this precisely.
GROUP=Core passes in full.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Fix QA and adjoint-policy violations after merging main
- Declare the adjoint reuse policy for SupernodalLUFactorization
(_NoAdjointFactorizationReuse, matching PureUMFPACKFactorization): the
factorization object is not a LinearAlgebra.Factorization and exposes
no adjoint solve, so the generic reverse path applies. Required by the
new adjoint.jl policy test from main.
- Drop the stale explicit import of spzeros in the SupernodalLU module
and access LinearAlgebra.BlasFloat from its owner rather than through
LinearAlgebra.BLAS (ExplicitImports QA).
- Restore Project.toml to main verbatim: the vendored solver needs no
manifest change at all (SparseArrays/LinearAlgebra/SciMLBase are
existing deps), and a stray RecursiveFactorization [deps] entry from
local testing was tripping Aqua stale-deps.
GROUP=Core and GROUP=QA both pass; the vendored solver's internal tests
also pass on Julia 1.10 LTS.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Allow-list SuiteSparse AMD port identifiers in the typos config
pn, hashi (variable names carried over from the SuiteSparse AMD source
in src/SupernodalLU/amd.jl) and Tpos (the transpose-position map in the
supernodal factorization) are identifiers, not misspellings.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Revert accidental absolute dev path in test/qa/Project.toml
Running the QA group locally made Pkg rewrite the [sources] entry from
the relative ../.. to my absolute checkout path, which was committed and
broke every CI job that instantiates that environment (Dev path does not
exist). Restored from main.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Split out the default-algorithm change into a follow-up PR
This PR is now purely additive: it adds SupernodalLUFactorization (and
the vendored src/SupernodalLU solver behind it) as an opt-in algorithm,
without touching the default polyalgorithm.
Reverted here and moved to the follow-up:
- src/default.jl: the :UMFPACKFactorization slot resolving to
SupernodalLUFactorization (file is now identical to main)
- the sparse defaultalg in LinearSolveSparseArraysExt (restored to
main's USE_GPL_LIBS-gated version)
- the docs wording claiming it is the structured-sparse default
The algorithm now reads its own :SupernodalLUFactorization cacheval slot
(the symbol only matters for DefaultLinearSolver caches, matching the
PureUMFPACKFactorization convention), and the docs note says explicitly
that it is not yet wired into the default polyalgorithm.
GROUP=Core passes; default routing verified unchanged (structured sparse
still selects UMFPACKFactorization).
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent 4f2af4a commit 6df384d
19 files changed
Lines changed: 3568 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
230 | 230 | | |
231 | 231 | | |
232 | 232 | | |
| 233 | + | |
233 | 234 | | |
234 | 235 | | |
235 | 236 | | |
| |||
241 | 242 | | |
242 | 243 | | |
243 | 244 | | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
244 | 254 | | |
245 | 255 | | |
246 | 256 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
34 | 38 | | |
35 | 39 | | |
36 | 40 | | |
| |||
680 | 684 | | |
681 | 685 | | |
682 | 686 | | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
683 | 809 | | |
684 | 810 | | |
685 | 811 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
409 | 409 | | |
410 | 410 | | |
411 | 411 | | |
| 412 | + | |
412 | 413 | | |
413 | 414 | | |
414 | 415 | | |
| |||
524 | 525 | | |
525 | 526 | | |
526 | 527 | | |
| 528 | + | |
527 | 529 | | |
528 | 530 | | |
529 | 531 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
0 commit comments